106 lines
3.7 KiB
Markdown
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
|