Files
mercadodevida/docs/pos/POS_TASKS.md
2026-08-21 21:24:58 +02:00

27 KiB
Raw Blame History

POS Tasks — Mercado de Vida

Companion to: POS_ARCHITECTURE.md and the other docs/pos/*.md Status: Discovery (Phase 1)

The POS implementation is split into 7 phases (per the brief) and each phase is broken into small, single-purpose tickets. Tickets follow the existing Orquestra pattern (scripts/new_ticket.py --id POS-NNN) and are sequenced in backlog/features.json.

Priority tiers:

  • P0 — imprescindible. Without these, no sale can happen.
  • P1 — importante. Daily operations break without them.
  • P2 — mejora. Polish + efficiency.
  • P3 — futuro. Operator-confirmed future work (offline, multi-currency, native hardware).

Each ticket follows the same shape:

POS-NNN [P?] [Phase] Title
   Why: one-sentence rationale
   Scope IN: bullet list
   Scope OUT: bullet list
   Touches: files / modules / migrations affected
   Acceptance: 3-7 testable criteria

Phase 0 — Discovery

POS-001 — POS Discovery & Architecture (in progress)

  • Why: Establish shared understanding before any code is written. Identify reusable components and gaps.
  • Status: this phase (the 5 docs under docs/pos/ + this file).

Phase 1 — Core TPV

POS-002 [P0] [Phase 1] Schema migrations 043046 (POS basics)

  • Why: All other code depends on the new tables and extended columns.
  • Scope IN: migrations 043_pos_basics.js (stores, terminals, payment_methods, quick_products, parked_tickets, cash_sessions), 044_pos_inventory_store.js (add store_id to inventory_stock and inventory_movements, backfill, swap unique constraint), 045_pos_orders_source.js (add source, terminal_id, cash_session_id, drop NOT NULL on user_id, add COMPLETED to state CHECK), 046_pos_backoffice_roles.js (extend role CHECK).
  • Scope OUT: No backend modules yet. No frontend.
  • Touches: project/migrations/, project/src/modules/orders/domain/order.ts (add COMPLETED to OrderState and ALLOWED_TRANSITIONS), project/src/shared/auth.ts (extend Role type).
  • Acceptance: all 4 migrations apply on a fresh DB and on the current dev DB; existing data is preserved; verify.sh exits 0; smoke test queries match the expected counts.

POS-003 [P0] [Phase 1] pos module skeleton (domain + repos + service)

  • Why: Provide the application layer for stores, terminals, sessions, payment methods.
  • Scope IN: project/src/modules/pos/{domain,application,infrastructure}/. Files: domain/{store,terminal,cash-session,errors}.ts, infrastructure/pg-store-repository.ts, infrastructure/pg-terminal-repository.ts, infrastructure/pg-payment-method-repository.ts, application/{list-stores,list-terminals,get-pos-config,open-cash-session,close-cash-session}.ts.
  • Scope OUT: No routes yet (covered by POS-004).
  • Touches: project/src/modules/pos/** (new), project/src/app/build-app.ts (wire the repos in deps).
  • Acceptance: unit tests cover each use case; the pos module is registered in build-app.ts without breaking the existing composition; verify.sh exits 0.

POS-004 [P0] [Phase 1] POS API — config, stores, terminals, sessions

  • Why: First batch of endpoints; allows the frontend to bootstrap and the admin to manage terminals.
  • Scope IN: project/src/modules/pos/api/pos.routes.ts with endpoints: GET /pos/admin/stores, POST /pos/admin/stores, GET /pos/admin/terminals, POST /pos/admin/terminals (returns bindingCode), GET /pos/terminals/me, POST /pos/terminals/bind, POST /pos/sessions, POST /pos/sessions/:id/close, GET /pos/sessions/me, GET /pos/config. Zod schemas for each request body. Swagger summaries.
  • Scope OUT: Sales, refunds, quick products admin — handled by POS-005/006/008.
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/shared/auth.ts (add requireAnyRole helper).
  • Acceptance: integration tests in project/src/modules/pos/tests/ cover happy path + 401/403/409; swagger renders; rate limits applied.

POS-005 [P0] [Phase 1] POS API — product search + payment methods

  • Why: Scanner + cart need a fast, denormalised product lookup; payment methods must be configurable per store.
  • Scope IN: GET /pos/products/search, GET /pos/products/by-ean/:ean, GET /pos/products/by-sku/:sku, GET /pos/products/:variantId, GET /pos/admin/payment-methods, POST /pos/admin/payment-methods, PATCH /pos/admin/payment-methods/:id. The product search joins catalog_products, catalog_product_variants, brands_brands, categories_categories, catalog_product_images, pricing_variant_prices, inventory_stock (for storeId).
  • Scope OUT: Sales endpoint (POS-008).
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/modules/pos/application/search-products-for-pos.ts.
  • Acceptance: integration tests show EAN lookup returns in < 200 ms on a 1000-product seed; search returns relevant results for partial queries; cache invalidation hook fires on sale completion.

POS-006 [P0] [Phase 1] apps/pos Next.js app skeleton

  • Why: The frontend is a new app; it needs the same auth flow, API client, and design tokens as the admin.
  • Scope IN: apps/pos/ with package.json, next.config.ts, tailwind.config.ts, src/app/layout.tsx, src/app/globals.css (copy of admin theme tokens), src/app/(auth)/login/page.tsx, src/lib/api-client.ts (copy of admin's, extended with posApi), src/lib/permissions.ts (extended with POS_*), src/features/auth/components/AuthProvider.tsx, src/lib/money.ts, src/lib/idempotency.ts, src/app/api/[...path]/route.ts (proxy), src/middleware.ts (terminal-id cookie + redirect to /login).
  • Scope OUT: UI components (POS-007). Multi-touch components (POS-016).
  • Touches: new apps/pos/ directory; project/scripts/monolith.sh (add pos service on port 3006).
  • Acceptance: cd project/apps/pos && npm run build succeeds; npm run dev boots; /login renders; login at the admin endpoint sets the cookie; posApi.me() returns the user.

POS-007 [P0] [Phase 1] POS UI — main register screen (layout + search + cart + totals)

  • Why: First usable iteration of the TPV screen.
  • Scope IN: components POSLayout, POSHeader, ProductSearch (with auto-focus + scanner integration), Cart, CartItem (with [-] N [+] quantity controls), CartTotals, CategorySelector, QuickProducts (read-only at first). Plus pages/(terminal)/page.tsx.
  • Scope OUT: Checkout (POS-011). Touch optimisations (POS-016).
  • Touches: apps/pos/src/components/pos/**, apps/pos/src/features/cart/** (zustand-like reducer in plain useReducer), apps/pos/src/lib/hardware/browser-scanner-adapter.ts.
  • Acceptance: the operator can search by name/EAN, click a quick product, change quantities, see totals update, and reset the cart. Scanner typing an EAN auto-adds to cart. TYPING in the search input does NOT trigger scanner. Lighthouse perf ≥ 90 in /pos.

POS-008 [P0] [Phase 1] POS API — create sale (idempotent)

  • Why: The core transaction; no POS without it.
  • Scope IN: POST /pos/sales, GET /pos/sales, GET /pos/sales/:id. application/create-pos-sale.ts orchestrates pricing → reserve → create order → register payments → confirm stock → mark COMPLETED in a single PG transaction. Idempotency via idempotencyKey UNIQUE on orders_orders.
  • Scope OUT: Refunds (POS-013). Reprint (POS-014).
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/modules/pos/application/create-pos-sale.ts, project/src/modules/inventory/application/inventory-service.ts (extend with storeId parameter), project/src/modules/payments/application/payment-service.ts (allow provider='cash' and provider='manual-card'), project/src/modules/orders/application/order-service.ts (allow source='pos').
  • Acceptance: integration test sells 2 items + cash payment → COMPLETED order exists, stock decreased, audit logged; same idempotencyKey returns same order; oversell returns 409; cash change computed correctly.

POS-009 [P1] [Phase 1] POS UI — customer association

  • Why: Some stores need receipts in the customer's name for loyalty / returns.
  • Scope IN: CustomerSelector component, search by email/phone via usersApi, apps/pos/src/features/cart/cart-slice.ts extended with customerId.
  • Scope OUT: Loyalty / points (deferred).
  • Touches: apps/pos/src/components/pos/CustomerSelector.tsx, apps/pos/src/lib/api-client.ts.
  • Acceptance: typing in the customer field shows suggestions; clicking a suggestion attaches the customer; the cart header shows the customer name; the receipt includes the customer.

POS-010 [P1] [Phase 1] POS UI — discount panel

  • Why: Daily operations need quick discount buttons.
  • Scope IN: DiscountPanel component with 0% / 5% / 10% / Custom buttons for both line and global discounts.
  • Scope OUT: Permission gating (POS-024).
  • Touches: apps/pos/src/components/pos/DiscountPanel.tsx, apps/pos/src/features/cart/cart-slice.ts.
  • Acceptance: discounts apply to subtotal; over-limit returns server error; UI explains the limit; audit log shows actor + amount + reason.

Phase 2 — Checkout

POS-011 [P0] [Phase 2] POS UI — payment modal (cash + card)

  • Why: Without a checkout, the cart is just a viewer.
  • Scope IN: PaymentModal, CashPayment (with amount tendered, change, quick-buttons [5 €] [10 €] [20 €] [50 €] [100 €] [EXACTO]), CardPayment (manual auth code entry), NumericKeypad.
  • Scope OUT: Native datáfono integration (POS-031+).
  • Touches: apps/pos/src/components/pos/**.
  • Acceptance: the operator can complete a sale end-to-end (open register → scan → cart → cobrar → cash/card → confirm); receipt prints via browser print; cart clears.

POS-012 [P0] [Phase 2] POS API — reprint receipt

  • Why: Cashier needs to reprint a receipt on request.
  • Scope IN: POST /pos/sales/:id/print, GET /pos/sales/:id/receipt.
  • Scope OUT: Refunds (POS-013).
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/modules/pos/application/render-receipt.ts.
  • Acceptance: reprinting returns the same ReceiptDto as the original sale; printerAdapter.print is called.

POS-013 [P0] [Phase 2] POS API — refunds (full + partial)

  • Why: Customer returns are an everyday reality.
  • Scope IN: POST /pos/sales/:id/refund with line-level granularity. Refunds create negative payments_transactions rows and inventory_movements.operation='set_available' rows.
  • Scope OUT: Refund UI (POS-026).
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/modules/pos/application/refund-pos-sale.ts.
  • Acceptance: partial refund returns PARTIALLY_REFUNDED, full refund returns REFUNDED; stock added back; audit logged with actor.

POS-014 [P0] [Phase 2] POS API — sale cancellation (within window)

  • Why: Cashier mistakes need a fast path.
  • Scope IN: POST /pos/sales/:id/cancel with 60-second window. Reservation released; state CANCELLED.
  • Scope OUT: Refunds (POS-013).
  • Touches: project/src/modules/pos/api/pos.routes.ts.
  • Acceptance: cancellation within 60 s succeeds; outside the window returns 409; reservation released; state CANCELLED.

POS-015 [P1] [Phase 2] POS UI — sale history + detail

  • Why: Cashier needs to look up today's sales quickly.
  • Scope IN: /pos/history page listing sales scoped to the terminal's store; /pos/history/[id] page showing items, payments, and a "Reimprimir" button.
  • Scope OUT: Cross-store history (admin only).
  • Touches: apps/pos/src/app/(terminal)/history/**, apps/pos/src/components/pos/TicketHistory.tsx.
  • Acceptance: the operator can find a sale by receipt number or partial customer name; reprinting works.

POS-016 [P2] [Phase 2] POS UI — first-pass touch optimisations

  • Why: Even desktop users benefit from bigger hit targets.
  • Scope IN: minimum 48×48 px touch targets, 16 px spacing between buttons, larger fonts in cart totals, virtual NumericKeypad always visible during cash payment.
  • Scope OUT: Full touch redesign (POS-029+).
  • Touches: apps/pos/src/components/pos/** styles.
  • Acceptance: Lighthouse accessibility ≥ 95; manual test on a 10-inch tablet shows no accidental taps.

Phase 3 — Cash register

POS-017 [P0] [Phase 3] POS UI — open register screen

  • Why: Required to start a shift.
  • Scope IN: /pos/open-register page with opening cash input, confirm button.
  • Scope OUT: Mid-shift reopen (closed sessions can't reopen; you close by opening a new one).
  • Touches: apps/pos/src/app/(terminal)/open-register/page.tsx.
  • Acceptance: cashier can open a register with a starting amount; on success, redirects to main screen; on 409 (already open), shows the existing session.

POS-018 [P0] [Phase 3] POS UI — close register screen (Z report)

  • Why: End-of-shift reconciliation is mandatory.
  • Scope IN: /pos/close-register page showing sales-by-method, refunds, expected cash vs operator-counted, difference.
  • Scope OUT: Manager override flow (POS-024).
  • Touches: apps/pos/src/app/(terminal)/close-register/page.tsx.
  • Acceptance: the Z report matches the database totals; the difference is recorded; the session transitions to CLOSED.

POS-019 [P0] [Phase 3] POS UI — register status in header

  • Why: Always-visible feedback of session state.
  • Scope IN: header pill showing session state, elapsed time, sale count, cash total.
  • Scope OUT: Configurable metrics (POS-024).
  • Touches: apps/pos/src/components/pos/POSHeader.tsx.
  • Acceptance: header updates in real time; "Cerrar caja" CTA visible when session is OPEN.

POS-020 [P1] [Phase 3] POS API — session summary endpoint

  • Why: The Z report reads from a single computed endpoint rather than fetching many.
  • Scope IN: GET /pos/sessions/:id/summary returns { salesCount, salesByMethod, refundsCount, refundsByMethod, expectedCashCents }.
  • Scope OUT: Historical session analytics.
  • Touches: project/src/modules/pos/api/pos.routes.ts, project/src/modules/pos/application/session-summary.ts.
  • Acceptance: summary is accurate against direct DB queries; cacheable for the duration of the close flow.

POS-021 [P1] [Phase 3] Audit hooks for register actions

  • Why: Open/close/difference must be logged for accountability.
  • Scope IN: AuditLogger.log calls in open-cash-session.ts, close-cash-session.ts.
  • Scope OUT: Audit UI (admin already has /admin/audit).
  • Touches: project/src/modules/pos/application/open-cash-session.ts, close-cash-session.ts.
  • Acceptance: every open/close is logged with actor, terminal, and amounts.

POS-022 [P2] [Phase 3] Z report export (PDF / email)

  • Why: Manager wants a copy at end of day.
  • Scope IN: "Send Z report by email" button on close screen; uses existing notifications module.
  • Scope OUT: PDF generation (use HTML→PDF via puppeteer if requested).
  • Touches: apps/pos/src/app/(terminal)/close-register/page.tsx, project/src/modules/notifications/**.
  • Acceptance: manager email receives the report; HTML email renders cleanly.

Phase 4 — Multi-terminal

POS-023 [P1] [Phase 4] Admin UI — stores CRUD

  • Why: Operators manage stores from the admin.
  • Scope IN: /admin/pos/stores (list, create, edit, deactivate).
  • Scope OUT: Per-store settings editor (POS-024).
  • Touches: new project/apps/admin/src/app/(dashboard)/pos/stores/page.tsx, apps/admin/src/lib/api-client.ts (add posAdminStoresApi).
  • Acceptance: operator can create / edit / deactivate stores; slug is auto-generated; deactivation prevents new terminals from binding.

POS-024 [P1] [Phase 4] Admin UI — terminals CRUD + binding codes

  • Why: Operators provision TPV devices.
  • Scope IN: /admin/pos/terminals (list, create, generate binding code, decommission).
  • Scope OUT: Remote desktop (deferred).
  • Touches: apps/admin/src/app/(dashboard)/pos/terminals/page.tsx, apps/admin/src/lib/api-client.ts.
  • Acceptance: operator creates a terminal, gets a binding code, the terminal binds on first launch; decommissioning prevents future sales.

POS-025 [P1] [Phase 4] Admin UI — payment methods per store

  • Why: Each store may have different cashiers / card types.
  • Scope IN: /admin/pos/payment-methods (list, create, toggle, reorder).
  • Scope OUT: Card terminal config (POS-031+).
  • Touches: apps/admin/src/app/(dashboard)/pos/payment-methods/page.tsx, apps/admin/src/lib/api-client.ts.
  • Acceptance: operator can disable a method mid-shift; POS app picks up the change on next config refresh.

POS-026 [P1] [Phase 4] Admin UI — sessions overview

  • Why: Operators see all open sessions.
  • Scope IN: /admin/pos/sessions (list, filter by store / terminal / user / date, click to view Z report).
  • Scope OUT: Real-time push (poll every 30 s is enough).
  • Touches: apps/admin/src/app/(dashboard)/pos/sessions/page.tsx.
  • Acceptance: operator can find any session, see its summary, and download the report.

POS-027 [P2] [Phase 4] Admin UI — quick products editor

  • Why: Store managers customize the quick-buttons grid.
  • Scope IN: /admin/pos/quick-products (drag-and-drop grid, add/remove, scope selector).
  • Scope OUT: Image upload (use existing product images).
  • Touches: apps/admin/src/app/(dashboard)/pos/quick-products/page.tsx.
  • Acceptance: operator can build a 12-product grid per terminal; changes propagate to the POS app within 60 s.

POS-028 [P2] [Phase 4] POS UI — concurrent terminal awareness

  • Why: A sale on terminal A should show up on terminal B (e.g., for cross-terminal refunds).
  • Scope IN: optional WebSocket / SSE channel from backend to POS app surfacing "new sale" events in the same store.
  • Scope OUT: Cross-store events.
  • Touches: project/src/modules/pos/api/pos.events.ts (SSE endpoint), apps/pos/src/lib/pos-events.ts.
  • Acceptance: terminal B sees terminal A's sales in real time within 2 s.

Phase 5 — Touch UI

POS-029 [P1] [Phase 5] Touch mode toggle + auto-detection

  • Why: Touchscreens need bigger targets; desktops don't.
  • Scope IN: interface_mode from terminal settings; default auto detects via (pointer: coarse) media query; user override stored in cookie.
  • Scope OUT: Per-section touch variants.
  • Touches: apps/pos/src/app/layout.tsx, apps/pos/src/components/pos/POSLayout.tsx, apps/pos/src/styles/touch.css.
  • Acceptance: auto mode uses media query; manual override persists; switching modes doesn't reload the app.

POS-030 [P1] [Phase 5] Touch mode — large targets, larger spacing, virtual keypad

  • Why: Touch-friendly UI is a separate design.
  • Scope IN: CSS variables for hit-target size and spacing; conditional class touch on root; bigger QuickProduct tiles; on-screen numeric keypad always visible during cash payment.
  • Scope OUT: Native number pad (deferred to native bridge).
  • Touches: apps/pos/src/styles/touch.css, apps/pos/src/components/pos/** styles.
  • Acceptance: manual test on a 10-inch touchscreen tablet shows no accidental taps; keypad is reachable without a physical keyboard.

POS-031 [P2] [Phase 5] Keyboard shortcuts (desktop)

  • Why: Power users want fast keyboard control.
  • Scope IN: F2 search, F4 customer, F6 discount, F8 park, F9 recall, F10 cobrar, Esc cancel modal.
  • Scope OUT: Customisable shortcuts.
  • Touches: apps/pos/src/lib/shortcuts.ts, apps/pos/src/app/(terminal)/page.tsx.
  • Acceptance: shortcuts work when no input is focused; ignore when typing in an input.

POS-032 [P2] [Phase 5] Touch mode — sound feedback (optional)

  • Why: Audio cue on scan success / error.
  • Scope IN: AudioContext based beeps; toggle in settings.
  • Scope OUT: Voice prompts.
  • Touches: apps/pos/src/lib/sounds.ts.
  • Acceptance: scan success plays a soft click; error plays a low beep; mute toggle works.

POS-033 [P3] [Phase 5] Responsive layout (1024×768 → 1920×1080)

  • Why: Wide monitors should use the space well; small screens should still fit.
  • Scope IN: CSS grid with auto-fill for quick products; column widths tuned per breakpoint.
  • Scope OUT: Phone-sized screens (< 1024 px).
  • Touches: apps/pos/src/components/pos/** styles.
  • Acceptance: no horizontal scroll at any tested width; quick products grid reflows correctly.

Phase 6 — Hardware (adapter layer)

POS-034 [P0] [Phase 6] Hardware adapter interfaces (canonical)

  • Why: Single source of truth for all hardware abstraction.
  • Scope IN: project/src/shared/hardware/types.ts (interfaces for Scanner, Printer, CashDrawer, PaymentTerminal, Scale); project/src/shared/hardware/index.ts (exports). Documentation in docs/pos/POS_HARDWARE.md.
  • Scope OUT: Implementations.
  • Touches: project/src/shared/hardware/** (new).
  • Acceptance: interfaces compile; documentation covers each adapter; tests cover the contract.

POS-035 [P0] [Phase 6] Browser implementations (scanner, printer, cash drawer)

  • Why: Required for any Phase 15 functionality.
  • Scope IN: BrowserScannerAdapter, BrowserPrinterAdapter, BrowserCashDrawerAdapter (delegates to printer).
  • Scope OUT: Native bridge.
  • Touches: apps/pos/src/lib/hardware/**.
  • Acceptance: scanner auto-detects HID input and adds to cart; printer prints receipt via window.print(); cash drawer opens via printer kick-out (or no-op for browser).

POS-036 [P1] [Phase 6] Print route /print/[orderId]

  • Why: Render the receipt in a print-stylesheet-only route.
  • Scope IN: apps/pos/src/app/print/[orderId]/page.tsx, print-specific CSS.
  • Scope OUT: Native ESC/POS rendering.
  • Touches: apps/pos/src/app/print/**, apps/pos/src/styles/print.css.
  • Acceptance: print preview shows the 80mm layout; no app chrome leaks; totals match the ReceiptDto.

POS-037 [P2] [Phase 6] Native bridge spec (HTTP)

  • Why: Define the contract for vendor-specific bridges.
  • Scope IN: docs/pos/POS_HARDWARE.md §3.3 and §4.2 (already written); add a sample Node skeleton under project/src/shared/hardware/native-bridge-spec/ (no implementation, just a README + OpenAPI).
  • Scope OUT: Real bridges.
  • Touches: project/src/shared/hardware/native-bridge-spec/** (new, optional).
  • Acceptance: README documents the HTTP contract and how a bridge would slot in.

POS-038 [P2] [Phase 6] Vendor adapter: SumUp (deferred)

  • Why: SumUp is a likely first datáfono vendor.
  • Status: P3 until operator picks SumUp.
  • Scope IN: native bridge process (pos-sumup-bridge) + apps/pos/src/lib/hardware/sumup-payment-terminal-adapter.ts.
  • Touches: new out-of-repo Node project (not in this codebase).
  • Acceptance: SumUp terminal charges the customer; auth code returns to POS; payment is recorded in payments_transactions.

POS-039 [P3] [Phase 6] Scale adapter

  • Why: Some products are sold by weight.
  • Scope IN: Scale adapter interface (already in POS-034); browser manual entry in Phase 5; native bridge in Phase 7+.
  • Scope OUT: Vendor-specific bridges.
  • Touches: apps/pos/src/lib/hardware/scale-adapter.ts.
  • Acceptance: weight entry shows in the cart with the right unit price calculation.

POS-040 [P3] [Phase 6] Cash drawer kick-out via printer command

  • Why: On real ESC/POS printers, the kick-out is ESC p 0.
  • Status: P3 until operator picks a printer.
  • Scope IN: native bridge sends the byte after receipt.
  • Scope OUT: Standalone drawer opener hardware.
  • Acceptance: drawer opens after every successful print.

Phase 7 — Polish + future

POS-041 [P2] [Phase 7] Refund UI (from sale history)

  • Why: Manager needs to refund from the history page.
  • Scope IN: /pos/sales/:id page with a "Refund" button → modal with line selection + reason.
  • Scope OUT: Cross-store refunds.
  • Touches: apps/pos/src/app/(terminal)/sales/[id]/page.tsx.
  • Acceptance: manager can refund a line or a full sale; stock is added back; audit logged.

POS-042 [P3] [Phase 7] Offline mode (read-only cache + queue)

  • Why: Operators want continuity during network drops.
  • Status: P3 until the operator explicitly commits. Requires deep analysis (stock consistency, payment refunds, audit trust).
  • Scope IN: IndexedDB-backed product + customer cache; queued mutations with conflict detection; merge strategy for offline sales.
  • Scope OUT: Real implementation in this codebase.
  • Acceptance: a design doc under docs/pos/POS_OFFLINE.md lays out the strategy.

POS-043 [P3] [Phase 7] Loyalty / points

  • Why: Brief §14 anticipates this.
  • Status: P3 until needed.
  • Scope IN: new pos_loyalty module; points ledger per customer; integration with pos.sale.created event.
  • Touches: project/src/modules/pos/loyalty/** (new).
  • Acceptance: customers earn points on each sale; can redeem at next sale.

POS-044 [P3] [Phase 7] Multi-currency

  • Why: EU cross-border expansion.
  • Status: P3 until needed. Requires extending pricing_variant_prices.currency (already supports it) and orders_orders.currency (already supports it).
  • Scope IN: UI currency picker, exchange-rate source, FX snapshot on order creation.
  • Scope OUT: Crypto.
  • Touches: apps/pos/src/components/pos/**, project/src/modules/pricing/**.
  • Acceptance: a sale in EUR with a USD display works; order snapshot stores the EUR value at sale time.

POS-045 [P3] [Phase 7] Gift receipts

  • Why: Brief §13 mentions reprinting.
  • Scope IN: "Gift receipt" option in print flow; emits a separate receipt with prices hidden.
  • Scope OUT: Email delivery.
  • Touches: apps/pos/src/components/pos/PaymentModal.tsx, apps/pos/src/app/print/[orderId]/page.tsx.
  • Acceptance: gift receipt omits prices; includes product names and a thank-you note.

POS-046 [P3] [Phase 7] Saved customer preferences (loyalty card scan)

  • Why: Brief §14 anticipates this.
  • Status: P3 until needed.
  • Scope IN: scan a loyalty barcode as a customer identifier.
  • Touches: apps/pos/src/components/pos/CustomerSelector.tsx.
  • Acceptance: scanning a loyalty code attaches the customer.

Summary

Priority Count Phases
P0 imprescindible 16 1, 2, 3, 6
P1 importante 18 1, 2, 3, 4, 5
P2 mejora 9 2, 3, 4, 5, 7
P3 futuro 6 5, 6, 7
Total 49 7

The operator can choose to delay or skip P2/P3 tickets freely. P0 tickets form the minimum viable TPV.


Status snapshot

  • Phase 0 (POS-001): in progress (this work).
  • Phase 1 (POS-002 … POS-010): queued in backlog; waiting on operator approval of this discovery phase.
  • Phase 2 (POS-011 … POS-016): queued.
  • Phase 3 (POS-017 … POS-022): queued.
  • Phase 4 (POS-023 … POS-028): queued.
  • Phase 5 (POS-029 … POS-033): queued.
  • Phase 6 (POS-034 … POS-040): queued.
  • Phase 7 (POS-041 … POS-046): queued.

No implementation work begins until the operator signs off on the 5 docs under docs/pos/ and approves Phase 1 to start.