Files
mercadodevida/project/specs/bulk-update/TASKS.md
2026-08-17 22:23:10 +02:00

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:

  1. BULK-BE-001 + BULK-BE-002 + BULK-BE-006 (parallel, no deps)
  2. BULK-BE-003 + BULK-BE-004 (parallel, both depend on BE-002)
  3. BULK-BE-005 (depends on BE-001..004)
  4. BULK-BE-007 (depends on BE-005, BE-006)
  5. BULK-BE-008 (depends on BE-007)
  6. BULK-ADM-001 (depends on BE-007)
  7. BULK-ADM-002 through BULK-ADM-004 (sequential wizard)
  8. BULK-ADM-005 (parallel with any ADM task)
  9. 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.