249 lines
12 KiB
Markdown
249 lines
12 KiB
Markdown
# EXPIRATION TRACKING — TASKS.md
|
|
|
|
## Backend
|
|
|
|
### EXP-BE-001
|
|
**ID**: EXP-BE-001
|
|
**Title**: Product expiration_tracking_enabled column
|
|
**Goal**: Add boolean column to catalog_products
|
|
**Why**: Products need to declare whether they require expiration tracking
|
|
**Dependencies**: None
|
|
**Applications**: Backend
|
|
**Modules**: catalog_products table, Product domain, ProductRepository
|
|
**Database impact**: ALTER TABLE catalog_products ADD COLUMN expiration_tracking_enabled boolean NOT NULL DEFAULT false
|
|
**API contracts**: GET /products/:id returns field; PATCH /products/:id accepts field
|
|
**Permissions**: admin
|
|
**Implementation**: Add to NewProduct interface, ProductPatch type, pg-product-repository
|
|
**Tests**: Unit — default false; Integration — column exists with correct default
|
|
**Migration**: See MIGRATION.md Phase 1
|
|
**Expected blast radius**: Low — only affects new product queries
|
|
**Definition of Done**: Column exists, defaults to false, persists on create/update
|
|
|
|
### EXP-BE-002
|
|
**ID**: EXP-BE-002
|
|
**Title**: InventoryLot domain and repository
|
|
**Goal**: New InventoryLot entity and InventoryLotRepository port + PgInventoryLotRepository
|
|
**Why**: Core data model for lot-level stock
|
|
**Dependencies**: EXP-BE-001
|
|
**Applications**: Backend
|
|
**Modules**: inventory/domain, inventory/infrastructure
|
|
**Database impact**: CREATE TABLE inventory_lots (see DESIGN.md)
|
|
**API contracts**: None yet (repository only)
|
|
**Permissions**: N/A
|
|
**Implementation**: InventoryLot interface, CreateLotCommand, InventoryLotRepository port, PgInventoryLotRepository
|
|
**Tests**: Unit — CRUD operations; Integration — table constraints, FK
|
|
**Expected blast radius**: Low — new table, no existing data touched
|
|
**Definition of Done**: Lot CRUD works, FK to catalog_variants enforced, CHECK constraint on expiration_date
|
|
|
|
### EXP-BE-003
|
|
**ID**: EXP-BE-003
|
|
**Title**: LotService — create, update, delete, FEFO allocation
|
|
**Goal**: Domain service handling lot business rules
|
|
**Why**: Encapsulates expiration validation and FEFO logic
|
|
**Dependencies**: EXP-BE-002
|
|
**Applications**: Backend
|
|
**Modules**: inventory/application
|
|
**API contracts**: None (domain service)
|
|
**Permissions**: N/A
|
|
**Implementation**: LotService class with createLot (validates expiry required), allocateLots (FEFO), adjustQuantity
|
|
**Tests**: Unit — missing expiry rejected; past expiry rejected; FEFO order; partial lot allocation
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: LotService methods have correct business rules; FEFO allocates from earliest expiry first
|
|
|
|
### EXP-BE-004
|
|
**ID**: EXP-BE-004
|
|
**Title**: InventoryLots API routes
|
|
**Goal**: CRUD endpoints for lots + availability query with filter
|
|
**Why**: Admin needs to manage lots; frontend needs to display them
|
|
**Dependencies**: EXP-BE-003
|
|
**Applications**: Backend
|
|
**Modules**: inventory/api
|
|
**API contracts**: GET /inventory/lots, POST /inventory/lots, PATCH /inventory/lots/:id, DELETE /inventory/lots/:id (all admin)
|
|
**Permissions**: admin role required
|
|
**Implementation**: New route registrations in inventory module; serialize LotService results
|
|
**Tests**: Integration — CRUD round-trip; filter=expiring|expired|all
|
|
**Expected blast radius**: Low — new routes
|
|
**Definition of Done**: All 5 routes respond correctly; filter parameters work; auth enforced
|
|
|
|
### EXP-BE-005
|
|
**ID**: EXP-BE-005
|
|
**Title**: InventoryService — integrate LotService for expiration products
|
|
**Goal**: Extend existing InventoryService to delegate to LotService when expiry is enabled
|
|
**Why**: Preserve existing InventoryServicePort contract while adding expiration support
|
|
**Dependencies**: EXP-BE-001, EXP-BE-003
|
|
**Applications**: Backend, Checkout
|
|
**Modules**: inventory/application, checkout
|
|
**API contracts**: Existing InventoryServicePort contract unchanged
|
|
**Permissions**: N/A
|
|
**Implementation**: In InventoryService, check product.expiration_tracking_enabled; if true, use LotService.getAvailableStock and LotService.allocateLots; feature flag gates behavior
|
|
**Tests**: Unit — delegation to LotService for expiry products; existing path for non-expiry products
|
|
**Expected blast radius**: Checkout uses InventoryServicePort — must not break
|
|
**Definition of Done**: Checkout reserve/confirm still works for both expiry and non-expiry products; FEFO used for expiry products
|
|
|
|
### EXP-BE-006
|
|
**ID**: EXP-BE-006
|
|
**Title**: Extend inventory_movements with lot operation types
|
|
**Goal**: Track lot_create, lot_adjust, lot_delete in movement audit log
|
|
**Why**: Full auditability of lot changes
|
|
**Dependencies**: EXP-BE-002
|
|
**Applications**: Backend
|
|
**Modules**: inventory/infrastructure, security
|
|
**Database impact**: ALTER TABLE inventory_movements — new operation types + optional lot_id FK
|
|
**API contracts**: Movement audit reflects lot operations
|
|
**Permissions**: N/A
|
|
**Implementation**: Add operation types in pg-inventory-repository insertMovement calls
|
|
**Tests**: Integration — movements logged with correct operation type
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: Lot mutations produce audit entries
|
|
|
|
## Database
|
|
|
|
### EXP-DB-001
|
|
**ID**: EXP-DB-001
|
|
**Title**: Run expiration tracking migrations
|
|
**Goal**: Apply all DB changes from MIGRATION.md Phase 1
|
|
**Why**: Infrastructure for lot model
|
|
**Dependencies**: EXP-BE-001 (column on catalog_products), EXP-BE-002 (lots table)
|
|
**Applications**: Database
|
|
**Modules**: N/A
|
|
**Database impact**: See MIGRATION.md Phase 1
|
|
**API contracts**: N/A
|
|
**Permissions**: DBA
|
|
**Implementation**: Add migration file or run raw SQL against dev DB; apply via docker-compose migration pipeline
|
|
**Tests**: Verify schema after migration
|
|
**Expected blast radius**: Low — additive changes
|
|
**Definition of Done**: Migration runs without error; new columns/tables exist with correct constraints
|
|
|
|
## Admin
|
|
|
|
### EXP-ADM-001
|
|
**ID**: EXP-ADM-001
|
|
**Title**: Product Editor — expiration tracking toggle
|
|
**Goal**: Add checkbox to General tab: "Track expiration dates"
|
|
**Why**: Admin configures per-product policy
|
|
**Dependencies**: EXP-BE-001
|
|
**Applications**: Admin
|
|
**Modules**: ProductEditor, GeneralSection
|
|
**Database impact**: None (uses EXP-BE-001)
|
|
**API contracts**: PATCH /catalog/products/:id
|
|
**Permissions**: products.write
|
|
**Implementation**: Add toggle to GeneralSection; saves { expiration_tracking_enabled: boolean } on save
|
|
**Tests**: Toggle saves correctly; shows/hides expiration UI based on state
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: Admin can enable/disable expiry tracking per product; toggle persists
|
|
|
|
### EXP-ADM-002
|
|
**ID**: EXP-ADM-002
|
|
**Title**: Inventory — lot-level stock view
|
|
**Goal**: Show lots table for expiry-tracking products in inventory page
|
|
**Why**: Operational visibility into expiration state
|
|
**Dependencies**: EXP-BE-004
|
|
**Applications**: Admin
|
|
**Modules**: InventorySection, inventory page
|
|
**Database impact**: None
|
|
**API contracts**: GET /inventory/lots?variant_id=X
|
|
**Permissions**: inventory.read
|
|
**Implementation**: Extend InventorySection to show lots when product has expiry enabled; compute status (VALID/NEAR_EXPIRY/EXPIRED) client-side from FLAG_EXPIRY_WARNING_DAYS
|
|
**Tests**: Lot table renders correctly; status computed from dates
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: Lots displayed with correct quantity, date, and status badge
|
|
|
|
### EXP-ADM-003
|
|
**ID**: EXP-ADM-003
|
|
**Title**: Inventory — lot create/edit/delete
|
|
**Goal**: Inline lot management in inventory section
|
|
**Why**: Admin must be able to add/update/remove lots
|
|
**Dependencies**: EXP-BE-004
|
|
**Applications**: Admin
|
|
**Modules**: InventorySection
|
|
**Database impact**: None
|
|
**API contracts**: POST/PATCH/DELETE /inventory/lots/:id
|
|
**Permissions**: inventory.write
|
|
**Implementation**: Add lot form (quantity, expiration date); inline edit on lot row; delete confirmation
|
|
**Tests**: Create lot with required expiry date; edit quantity; delete lot; 422 shown for missing expiry
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: Admin can fully manage lots; validation errors shown correctly
|
|
|
|
### EXP-ADM-004
|
|
**ID**: EXP-ADM-004
|
|
**Title**: Inventory filters — expiring, expired, all, no-expiry
|
|
**Goal**: Filter inventory page by expiration status
|
|
**Why**: Operational efficiency for stock management
|
|
**Dependencies**: EXP-BE-004
|
|
**Applications**: Admin
|
|
**Modules**: inventory page
|
|
**Database impact**: None
|
|
**API contracts**: GET /inventory/lots?filter=expiring|expired|all|no-expiry
|
|
**Permissions**: inventory.read
|
|
**Implementation**: Add filter tabs/dropdown to inventory page; calls API with filter param
|
|
**Tests**: Each filter returns correct lot subset
|
|
**Expected blast radius**: Low
|
|
**Definition of Done**: Filters work; filter state reflected in URL or UI
|
|
|
|
## QA
|
|
|
|
### EXP-QA-001
|
|
**ID**: EXP-QA-001
|
|
**Title**: Expiration tracking regression tests
|
|
**Goal**: Ensure existing checkout flow is unbroken
|
|
**Why**: No regressions on existing products
|
|
**Dependencies**: EXP-BE-005, EXP-DB-001
|
|
**Applications**: QA
|
|
**Modules**: E2E tests
|
|
**Tests**:
|
|
- Normal product (non-expiry) still checks out correctly
|
|
- Expiry product with no lots: unavailable
|
|
- Expiry product with valid lot: available and reservable
|
|
- FEFO: earliest expiry lot consumed first
|
|
- Expired lot: contributes zero sellable units
|
|
**Expected blast radius**: N/A
|
|
**Definition of Done**: All regression tests pass
|
|
|
|
### EXP-QA-002
|
|
**ID**: EXP-QA-002
|
|
**Title**: Expiration tracking unit/integration tests
|
|
**Goal**: Comprehensive test coverage for all new domain code
|
|
**Why**: Business rules must be correct
|
|
**Dependencies**: EXP-BE-003, EXP-BE-004, EXP-BE-005
|
|
**Applications**: QA
|
|
**Modules**: Backend test suite
|
|
**Tests**: See TESTS.md
|
|
**Expected blast radius**: N/A
|
|
**Definition of Done**: 100% pass rate on expiration-specific tests
|
|
|
|
---
|
|
|
|
## Task Summary Table
|
|
|
|
| Task | Layer | Feature | Depends On | Risk | Parallel |
|
|
|------|-------|---------|-----------|------|---------|
|
|
| EXP-BE-001 | Backend | Product expiry column | — | Low | * |
|
|
| EXP-BE-002 | Backend | Lot model + repository | — | Low | * |
|
|
| EXP-BE-003 | Backend | LotService domain | EXP-BE-002 | Low | * |
|
|
| EXP-BE-004 | Backend | Lot API routes | EXP-BE-003 | Low | * |
|
|
| EXP-BE-005 | Backend | InventoryService + LotService | EXP-BE-001, EXP-BE-003 | Medium | * |
|
|
| EXP-BE-006 | Backend | Movement audit for lots | EXP-BE-002 | Low | EXP-BE-004 |
|
|
| EXP-DB-001 | DB | Run migrations | EXP-BE-001, EXP-BE-002 | Low | * |
|
|
| EXP-ADM-001 | Admin | Product expiry toggle | EXP-BE-001 | Low | * |
|
|
| EXP-ADM-002 | Admin | Lot stock view | EXP-BE-004 | Low | * |
|
|
| EXP-ADM-003 | Admin | Lot CRUD | EXP-BE-004 | Low | * |
|
|
| EXP-ADM-004 | Admin | Expiry filters | EXP-BE-004 | Low | * |
|
|
| EXP-QA-001 | QA | Checkout regression | EXP-BE-005, EXP-DB-001 | Medium | After backend |
|
|
| EXP-QA-002 | QA | Domain unit tests | All BE tasks | Low | With backend |
|
|
|
|
**Parallel group**: EXP-BE-001 and EXP-BE-002 can run in parallel. BE-003 depends on BE-002. BE-004 and BE-006 depend on BE-003. BE-005 depends on BE-001 + BE-003.
|
|
|
|
**Recommended order**:
|
|
1. EXP-BE-001 + EXP-BE-002 (parallel, no dependencies)
|
|
2. EXP-BE-003 (depends on BE-002)
|
|
3. EXP-BE-004 + EXP-BE-006 (depend on BE-003, parallel)
|
|
4. EXP-DB-001 (run after BE-001 + BE-002 code is deployed)
|
|
5. EXP-BE-005 (depends on BE-001 + BE-003)
|
|
6. EXP-ADM-001 (depends on BE-001)
|
|
7. EXP-ADM-002 + EXP-ADM-003 + EXP-ADM-004 (depend on BE-004, parallel)
|
|
8. EXP-QA-001 + EXP-QA-002 (after all backend + admin)
|
|
|
|
**High-risk tasks**: EXP-BE-005 (changes InventoryService contract internal behavior, affects checkout — thorough regression testing required).
|
|
|
|
**MVP boundary**: EXP-BE-001 through EXP-BE-005 + EXP-DB-001 + EXP-ADM-001 through EXP-ADM-004. EXP-BE-006 (audit) is low priority for MVP. EXP-QA-002 is bundled with implementation. EXP-QA-001 is blocking go-live.
|