15 KiB
ADMIN BULK UPDATE — TASKS.md
Backend
BULK-BE-001
ID: BULK-BE-001 Title: BulkOperation domain types Goal: Define all types for bulk operations Why: Shared contracts for handlers, orchestrator, and API Dependencies: None Applications: Backend Modules: bulk/domain/bulk-operation.ts Database impact: None API contracts: Shapes of request/response objects Permissions: N/A Implementation: Define BulkOperationType enum, BulkOperationParams union, PreviewItem, PreviewResult, ExecuteResult interfaces; ValidationResult Tests: Type tests (TypeScript compilation) Expected blast radius: Low Definition of Done: All types defined; TypeScript compiles without errors
BULK-BE-002
ID: BULK-BE-002
Title: BulkOperationHandler port + handler registry
Goal: Define the extension interface for bulk operations
Why: Allows adding future operations without touching core orchestrator
Dependencies: BULK-BE-001
Applications: Backend
Modules: bulk/domain/ports.ts
Database impact: None
API contracts: BulkOperationHandler interface
Permissions: N/A
Implementation: Define BulkOperationHandler interface; create HANDLERS registry map; export from bulk/index.ts
Tests: Handler lookup by type works; unknown type returns undefined
Expected blast radius: Low
Definition of Done: Interface exists; registry is extensible; adding a handler requires only adding to registry
BULK-BE-003
ID: BULK-BE-003 Title: Price bulk operation handlers Goal: Implement SET, INCREASE_PERCENT, DECREASE_PERCENT, INCREASE_FIXED, DECREASE_FIXED Why: Initial price operations Dependencies: BULK-BE-002 Applications: Backend Modules: bulk/application/handlers/price-handlers.ts Database impact: None API contracts: None (domain) Permissions: N/A Implementation: Five handler classes; each implements validate(), preview(), buildPatch(); rounding uses Math.round(); VAT is stored as net price Tests:
- BULK-BE-UT-001: INCREASE_PERCENT 5% on 350 cents → 368 cents
- BULK-BE-UT-002: DECREASE_PERCENT 5% on 350 cents → 333 cents
- BULK-BE-UT-003: INCREASE_FIXED 50 on 350 cents → 400 cents
- BULK-BE-UT-004: DECREASE_FIXED 50 on 350 cents → 300 cents
- BULK-BE-UT-005: DECREASE_FIXED below 0 → floor at 0
- BULK-BE-UT-006: SET_PRICE 500 → 500
- BULK-BE-UT-007: invalid percent (0, negative, >1000) → ValidationResult.valid=false
- BULK-BE-UT-008: missing VAT rate → ValidationResult.valid=false
- BULK-BE-UT-009: product without price → PreviewItem.status=failed, error=PRICE_NOT_FOUND Expected blast radius: Low Definition of Done: All five handlers produce correct price calculations; invalid params rejected at validate()
BULK-BE-004
ID: BULK-BE-004 Title: Category bulk operation handlers Goal: Implement ADD_CATEGORY, REMOVE_CATEGORY, REPLACE_CATEGORIES Why: Initial category operations Dependencies: BULK-BE-002 Applications: Backend Modules: bulk/application/handlers/category-handlers.ts Database impact: None API contracts: None (domain) Permissions: N/A Implementation: Three handler classes; ADD deduplicates; REMOVE filters; REPLACE replaces entirely; validates categoryId exists (optional — can be deferred) Tests:
- BULK-BE-UT-010: ADD_CATEGORY to product with 2 categories → 3 categories
- BULK-BE-UT-011: ADD_CATEGORY with duplicate → no-op (deduplicated)
- BULK-BE-UT-012: REMOVE_CATEGORY from product with 3 categories → 2 categories
- BULK-BE-UT-013: REMOVE_CATEGORY not present → no change
- BULK-BE-UT-014: REPLACE_CATEGORIES with 2 new categories → exactly those 2
- BULK-BE-UT-015: ADD without categoryId → ValidationResult.valid=false Expected blast radius: Low Definition of Done: All three handlers produce correct category patches; validation rejects invalid input
BULK-BE-005
ID: BULK-BE-005 Title: BulkService orchestrator Goal: Orchestrate preview + execute; integrate with audit Why: Core business logic tying handlers, repository, and audit together Dependencies: BULK-BE-001, BULK-BE-002, BULK-BE-003, BULK-BE-004 Applications: Backend Modules: bulk/application/bulk-service.ts Database impact: None API contracts: None (service) Permissions: N/A Implementation: BulkService with preview() and execute(); execute() uses transaction; logs audit entry on execution; rollback on error; generate operationId Tests:
- BULK-BE-UT-020: preview() calls handler.validate() and handler.preview() for each product
- BULK-BE-UT-021: execute() calls handler.buildPatch() for each valid product
- BULK-BE-UT-022: execute() logs one audit entry per bulk operation
- BULK-BE-UT-023: execute() rollback on error → no product modified
- BULK-BE-UT-024: unknown operation type → AppError(400) Expected blast radius: Medium — changes persistence Definition of Done: BulkService correctly orchestrates all operations; audit entry is complete; transaction is atomic
BULK-BE-006
ID: BULK-BE-006 Title: BulkProductRepository — read products with pricing in batch Goal: Efficiently load product+price data for bulk operations Why: Avoids N queries for N products Dependencies: BULK-BE-001 Applications: Backend Modules: bulk/infrastructure/pg-bulk-repository.ts Database impact: None API contracts: None Permissions: N/A Implementation: Single SQL query to JOIN catalog_products + pricing_variants; returns ProductWithPrice[] (internal type); handles products with no price gracefully Tests: BULK-BE-IT-001: batch load of 20 products returns correct data; BULK-BE-IT-002: product without price returns null price field Expected blast radius: Low Definition of Done: Products loaded in one query; missing price handled gracefully
BULK-BE-007
ID: BULK-BE-007 Title: Bulk API routes — preview and execute Goal: Expose bulk operations via REST API Why: Admin UI needs these endpoints Dependencies: BULK-BE-005, BULK-BE-006 Applications: Backend Modules: bulk/api/bulk.routes.ts Database impact: None (read for preview; write for execute) API contracts: POST /admin/bulk/preview, POST /admin/bulk/execute Permissions: admin role required on both Implementation: Register routes in build-app.ts; parse request body; call BulkService; serialize response; handle errors with AppError Tests:
- BULK-BE-IT-010: POST /admin/bulk/preview with valid INCREASE_PERCENT → 200 + PreviewResult
- BULK-BE-IT-011: POST /admin/bulk/preview with invalid params → 422
- BULK-BE-IT-012: POST /admin/bulk/preview without auth → 401
- BULK-BE-IT-013: POST /admin/bulk/execute with valid params → 200 + ExecuteResult
- BULK-BE-IT-014: POST /admin/bulk/execute → applies changes to DB; products updated
- BULK-BE-IT-015: audit log entry created after execute Expected blast radius: Low — new routes Definition of Done: Both routes respond correctly; auth enforced; changes persisted
BULK-BE-008
ID: BULK-BE-008 Title: Wire bulk module into build-app.ts Goal: Register bulk routes with Fastify Why: Module must be mounted in the application Dependencies: BULK-BE-007 Applications: Backend Modules: app/build-app.ts Database impact: None API contracts: None Permissions: N/A Implementation: Import registerBulkRoutes; call in build-app.ts alongside other modules; pass pool, authenticate, audit Tests: Integration test that /admin/bulk/preview and /admin/bulk/execute respond Expected blast radius: Low Definition of Done: Bulk routes accessible at /admin/bulk/*
Admin
BULK-ADM-001
ID: BULK-ADM-001 Title: Bulk Update page shell + product selection Goal: Product selection step of the wizard Why: Admin needs to pick which products to update Dependencies: BULK-BE-007 Applications: Admin Modules: apps/admin/src/app/(dashboard)/bulk-update/page.tsx Database impact: None API contracts: GET /catalog/products (existing) Permissions: products.bulk_update Implementation: Full-width page with product table; checkbox column; search/filter bar; selected count badge; "Continue to Configure" button Tests: Products load; checkboxes select/deselect; selected count accurate; filter narrows selection Expected blast radius: Low Definition of Done: Admin can select products and see selection count
BULK-ADM-002
ID: BULK-ADM-002 Title: Operation selector + configuration step Goal: Choose and configure the bulk operation type Why: Second step of the wizard Dependencies: BULK-ADM-001 Applications: Admin Modules: apps/admin/src/app/(dashboard)/bulk-update/page.tsx Database impact: None API contracts: None yet Permissions: products.bulk_update Implementation: Two cards: Price / Categories; selecting Price shows sub-options (SET, +%, -%, +FIXED, -FIXED) with parameter inputs; selecting Categories shows (ADD, REMOVE, REPLACE) with category picker; Back button returns to selection Tests: Selecting each operation shows correct sub-options; invalid input shows error state Expected blast radius: Low Definition of Done: Admin can select operation type and enter parameters
BULK-ADM-003
ID: BULK-ADM-003 Title: Preview step Goal: Show proposed changes before committing Why: Critical safety step — see what will change before writing Dependencies: BULK-BE-007, BULK-ADM-002 Applications: Admin Modules: apps/admin/src/app/(dashboard)/bulk-update/page.tsx Database impact: None (dry-run) API contracts: POST /admin/bulk/preview Permissions: products.bulk_update Implementation: Call preview API with selected productIds, operation, parameters; display table (Product | Current | Proposed | Status); show summary bar ("18 valid, 2 failed"); Back button to Configure Tests: Preview loads; shows correct current→proposed values; failed rows highlighted with error reason Expected blast radius: Low Definition of Done: Preview accurately reflects what execute() would do; no product modified
BULK-ADM-004
ID: BULK-ADM-004 Title: Confirmation + execution step Goal: Require explicit confirmation before executing bulk operation Why: Safety barrier — prevent accidental bulk changes Dependencies: BULK-ADM-003, BULK-BE-007 Applications: Admin Modules: apps/admin/src/app/(dashboard)/bulk-update/page.tsx Database impact: MODIFIES PRODUCTS API contracts: POST /admin/bulk/execute Permissions: products.bulk_update Implementation: Show warning message ("X changes will be applied. This cannot be undone."); [Cancel] [Apply Changes] buttons; on Apply: call execute API; on success: show result summary; on error: show error + retry option Tests: Confirmation dialog shows before any write; cancel returns to preview; apply calls execute; result shows success/failure counts Expected blast radius: HIGH — modifies products Definition of Done: Confirmation required; execute called only after explicit confirmation; result displayed accurately
BULK-ADM-005
ID: BULK-ADM-005 Title: Navigation sidebar — add Bulk Update link Goal: Make bulk update accessible from admin navigation Why: Discoverability Dependencies: BULK-ADM-001 Applications: Admin Modules: apps/admin/src/lib/permissions.ts Database impact: None API contracts: None Permissions: products.bulk_update Implementation: Add to NAV_ITEMS: { href: '/bulk-update', label: 'Actualización masiva', icon: '📦', permission: 'products.bulk_update' } Tests: Bulk Update appears in sidebar for admin users Expected blast radius: Low Definition of Done: Nav item visible in admin sidebar
QA
BULK-QA-001
ID: BULK-QA-001 Title: Bulk update E2E tests Goal: End-to-end validation of complete bulk update flow Why: Critical safety feature — full flow must work Dependencies: BULK-BE-007, BULK-ADM-004 Applications: QA Modules: E2E tests Tests:
- BULK-QA-UT-001: Select 5 products → preview shows correct proposed prices
- BULK-QA-UT-002: Preview does NOT modify any product
- BULK-QA-UT-003: Confirm → products actually updated in DB
- BULK-QA-UT-004: Audit log entry exists for bulk operation
- BULK-QA-UT-005: 5% increase on products with VAT general → correct net price applied
- BULK-QA-UT-006: ADD_CATEGORY → categories added without removing existing
- BULK-QA-UT-007: REMOVE_CATEGORY → category removed, others preserved
- BULK-QA-UT-008: REPLACE_CATEGORIES → only new categories remain
- BULK-QA-UT-009: Product without price → failed in preview and in execute (not silently skipped)
- BULK-QA-UT-010: Non-admin user → 401 on /admin/bulk/execute Expected blast radius: N/A Definition of Done: All tests pass; QA sign-off obtained
Task Summary Table
| Task | Layer | Feature | Depends On | Risk | Parallel |
|---|---|---|---|---|---|
| BULK-BE-001 | Backend | Domain types | — | Low | * |
| BULK-BE-002 | Backend | Handler interface | BULK-BE-001 | Low | * |
| BULK-BE-003 | Backend | Price handlers | BULK-BE-002 | Low | * |
| BULK-BE-004 | Backend | Category handlers | BULK-BE-002 | Low | * |
| BULK-BE-005 | Backend | BulkService orchestrator | BULK-BE-001..004 | Medium | * |
| BULK-BE-006 | Backend | BulkProductRepository | BULK-BE-001 | Low | * |
| BULK-BE-007 | Backend | API routes | BULK-BE-005, BULK-BE-006 | Medium | * |
| BULK-BE-008 | Backend | Wire into build-app | BULK-BE-007 | Low | * |
| BULK-ADM-001 | Admin | Product selection UI | BULK-BE-007 | Low | * |
| BULK-ADM-002 | Admin | Operation config step | BULK-ADM-001 | Low | * |
| BULK-ADM-003 | Admin | Preview step | BULK-ADM-002, BULK-BE-007 | Low | * |
| BULK-ADM-004 | Admin | Confirm + execute step | BULK-ADM-003, BULK-BE-007 | Medium | * |
| BULK-ADM-005 | Admin | Sidebar nav link | BULK-ADM-001 | Low | * |
| BULK-QA-001 | QA | E2E tests | BULK-BE-007, BULK-ADM-004 | Medium | After all |
Parallel group: BULK-BE-001 and BULK-BE-002 are independent; BULK-BE-003 and BULK-BE-004 are parallel (both depend on BE-002); BULK-BE-005 depends on all four handlers; BULK-BE-006 independent; BULK-BE-007 depends on BE-005 + BE-006; Admin tasks BULK-ADM-001 through BULK-ADM-005 are sequential (wizard flow).
Recommended order:
- BULK-BE-001 + BULK-BE-002 + BULK-BE-006 (parallel, no deps)
- BULK-BE-003 + BULK-BE-004 (parallel, both depend on BE-002)
- BULK-BE-005 (depends on BE-001..004)
- BULK-BE-007 (depends on BE-005, BE-006)
- BULK-BE-008 (depends on BE-007)
- BULK-ADM-001 (depends on BE-007)
- BULK-ADM-002 through BULK-ADM-004 (sequential wizard)
- BULK-ADM-005 (parallel with any ADM task)
- BULK-QA-001 (after everything)
High-risk tasks:
- BULK-BE-005: Transaction logic must be correct — wrong rollback = data corruption
- BULK-ADM-004: Executes actual DB writes — requires confirmation UI; must not auto-submit
- BULK-BE-007: New API endpoints with admin auth — verify auth enforced
MVP boundary: BULK-BE-001 through BULK-BE-008 (backend) + BULK-ADM-001 through BULK-ADM-005. BULK-QA-001 is required before go-live.
Migration risks: None — bulk update is purely additive; no existing data migration required.