Files
mercadodevida/project/specs/expiration-tracking/MIGRATION.md
2026-08-17 22:23:10 +02:00

106 lines
3.7 KiB
Markdown

# EXPIRATION TRACKING — MIGRATION.md
## Migration Philosophy
**No breaking changes to existing products or checkout flow.**
All existing products must retain their current behavior after migration.
## Phase 0: Feature Flag Off
The feature flag `expiration_tracking` starts as **off**. All code paths default to existing behavior.
## Phase 1: Database Migration (Zero-downtime safe)
```sql
-- 1. Add column to products (nullable, default false)
ALTER TABLE catalog_products
ADD COLUMN expiration_tracking_enabled boolean NOT NULL DEFAULT false;
-- 2. Create lots table
CREATE TABLE inventory_lots (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
variant_id uuid NOT NULL REFERENCES catalog_variants(id) ON DELETE CASCADE,
quantity integer NOT NULL DEFAULT 0 CHECK (quantity >= 0),
expiration_date date,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX inventory_lots_variant_id_idx ON inventory_lots(variant_id);
CREATE INDEX inventory_lots_expiration_idx ON inventory_lots(expiration_date)
WHERE expiration_date IS NOT NULL;
-- 3. Extend movements for lot traceability
ALTER TABLE inventory_movements
DROP CONSTRAINT IF EXISTS inventory_movements_operation_check;
ALTER TABLE inventory_movements
ADD CONSTRAINT inventory_movements_operation_check CHECK (
operation IN (
'reserve', 'release', 'confirm', 'set_available',
'lot_create', 'lot_adjust', 'lot_delete'
)
);
ALTER TABLE inventory_movements
ADD COLUMN lot_id uuid REFERENCES inventory_lots(id) ON DELETE SET NULL;
```
All changes are additive. No existing data is modified or deleted.
## Phase 2: Seed Existing Stock as Lots (for tracking-enabled products only)
For each existing product where `expiration_tracking_enabled = true`:
- Query `inventory_stock` for each variant
- Create one `inventory_lots` entry per variant with:
- `quantity = inventory_stock.available`
- `expiration_date = NULL` (admin must add expiration dates)
**Products with `expiration_tracking_enabled = false`**: no lots created. Existing variant-level stock table continues to be the source of truth.
## Phase 3: Code Deployment
Deploy code with:
- Feature flag `expiration_tracking = off`
- New domain classes (LotService, InventoryLotRepository, new routes)
- All existing paths still work via the flag check
## Phase 4: Admin Onboarding
Admin can now:
1. Enable expiration tracking on products
2. Create inventory lots with expiration dates
3. See lot-level stock view
**Admin instruction**: When enabling expiration tracking for a product, the admin should create inventory lots and set expiration dates before the product goes on sale. Existing stock without lots is not tracked.
## Phase 5: Flip Feature Flag
After admin confirms all required products have lots configured:
```
FLAG_EXPIRATION_TRACKING = true
```
## Rollback Plan
1. Set `FLAG_EXPIRATION_TRACKING = false`
2. Existing data is preserved (lots table + new columns)
3. Code paths revert to variant-level behavior
4. No data loss
## What Breaks if We Skip Feature Flag
If `expiration_tracking` is always on:
- Existing products without expiration_tracking flag → still create lots with null expiry dates
- Checkout continues to work (null expiry = always valid)
- No user-facing breakage
The flag is for gradual rollout and operational safety, not a hard architectural requirement.
## No-Tracking Products After Migration
Products with `expiration_tracking_enabled = false`:
- Continue using `inventory_stock` table
- `GET /inventory/:variantId/availability` uses variant-level query
- No lots are created or queried
- Behavior is byte-for-byte identical to pre-migration