Skip to content

feat(billing): correct the payment method on a paid bill while its shift is open #1337

Open

Description

@khaira777

Parent: #830 (S2). Verified against main @ 8e39053.

Problem

A cashier picks the wrong tender (cash instead of card, or the reverse) and cannot fix it after checkout (+63, 11 Sep). The shift count is then off by that amount.

Current behavior (verified)

  • Payment lines are stored in the bill's payment_details JSON, and each line carries cash_session_id (main/routes/bills.ts ~L3772-3785).
  • No route changes the method of a recorded payment. Bills routes: list/get/print-history, generate, split-check, payment(s), applyDiscount, markPrinted, print, PATCH /:id/charges. There is no UPDATE ... payment path anywhere.
  • Expected cash per shift comes from payment lines and drawer movements by cash_session_id (main/routes/cash-closures.ts ~L615-740).

Decisions (made by maintainer, 23 Oct)

  • Only an owner or manager, authorized with their Staff Approval PIN (same approver selection as refunds and overrides).
  • Allowed only while the payment's cash shift is still open. Once that shift is closed (or the day is closed) the correction is refused with a clear reason.
  • The amount never changes: only the method (and the method id / card transaction id) on one payment line.
  • Every change is audited: old method, new method, actor, approver, reason, timestamp.
  • Expected cash is recalculated for the open shift, so the change shows up in variance.

Scope

  • New route, for example PATCH /bills/:id/payments/:index/method in main/routes/bills.ts, wrapped in a transaction, with PIN verification outside the write transaction as the existing refund flow does (verifyPinAsync, withPinVerificationLock).
  • Refuse on refunded lines, split siblings already settled elsewhere, and unknown or inactive methods.
  • UI: a "Correct payment method" action on paid bills in OrderDetailPanel.tsx, with approver picker and PIN prompt.
  • Update docs/reference/roles-and-permissions.md and docs/reference/api.md in the same PR.

Acceptance criteria

  • Cash to card on an open shift: shift expected cash drops by the amount, and the card total rises by it.
  • Closed shift or closed day: 409 with a reason, nothing changed.
  • Cashier role, or a wrong or ineligible PIN: refused. A cashier's PIN must never authorize this.
  • Audit row written. A replay of the same request is idempotent.
  • Receipt reprint shows the corrected method.

Verification

  • New tests/payment-method-correction.test.ts covering the cases above, modelled on the refund PIN tests.
  • npm run lint && npm test

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendBackend (Express/SQLite)area:frontendFrontend (React/Next.js)enhancementNew feature or requeststate:readyScoped and ready for implementationtype:featureNew capability or enhancement request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions