Skip to content

Financials: Implement idempotent refund system & fix partial refund over-collection #278

Closed as not planned

Description

@khaira777

Refs: #370 — POS feature roadmap (Phase 1)

Important

Status: Needs maintainer review — ported from fork codewithsharma/FloCafe (branch develop). Requires maintainer review/approval before implementation.

Financial Integrity & Refund Tracking Request

Extracted from fork codewithsharma/FloCafe (branch: develop):

Overview

Improvements to track refunds, ensure idempotency on financial transactions, handle cash refunds during shift/day close, and prevent math issues on partial refunds.

Proposed Changes

  1. Refunds Table & Idempotency Tracking:
    • Add dedicated refunds table and refund_idempotency table (Migration 72).
    • Track amount_cents, original_method, approved_by, and shift_id.
    • Reference: main/routes/refunds.ts, main/services/refund.ts.
  2. Partial Refund Over-Collection Fix:
    • Fix calculation logic preventing over-collection or double refund balance mismatches on partial refunds.
    • Reference: commit fedbe4c.
  3. Cash Refund Integration into Day Close:
    • Ensure cash refund subtractions are properly accounted for in expected cash counts during shift reconciliation and day close.
    • Reference: commit aaf8720.
  4. Fail-Closed DB Recovery Guard:
    • Fail closed safely if the production SQLite database is missing or unreadable rather than generating a blank database in production mode.
    • Reference: commit e236542.

Acceptance criteria

  • refunds + refund_idempotency tables (amount, original method, approver, shift) with idempotency tracking.
  • Partial-refund over-collection / double-refund balance bug fixed.
  • Cash refunds correctly reduce expected cash in shift/day-close reconciliation.
  • Fail-closed guard: production refuses to create a blank DB when the real one is missing/unreadable.

Activity

  1. khaira777 commented on Aug 16, 2026

    @khaira777
    ContributorAuthor

    Verdict: VALID BUT NEEDS RESCOPING

    Findings:

    • There is no refunds/refund_idempotency table on main. Refunds are modeled as bill adjustment lines created when items are voided (frontend/src/lib/i18n/en.json:192 "Voiding it adds a refund line to the bill"; statuses voided/void_adjustment handled throughout main/routes/bills.ts and main/routes/order-items.ts:87-158).
    • Idempotency infrastructure already exists for payments/orders: payment_idempotency (main/db.ts:3292), order_idempotency (:3347), payment_transaction_refs (:3300). A refunds table should mirror this pattern rather than invent a new one.
    • Split math already uses minor-unit rounding with remainder distribution (main/routes/bills.ts:96-99, :175, :548-556). The "partial refund over-collection" claim comes with no reproduction; I found no obvious defect — this needs a failing test/evidence first (related: tests/cancel-override.test.ts, tests/issue-24-cancel-item-checkout.test.ts).
    • "Cash refund into day close": no day close exists — this part is blocked on Feature: Shift lifecycle management, cash drawer reconciliation, & business day close #279.
    • Fail-closed DB: initDatabase (main/db.ts:607-625) opens/creates the DB file; creating a blank DB in production is the behavior to guard, but first-run setup depends on creating a fresh DB — needs a "production + missing/unreadable DB → fail with restore guidance" decision; note runStartupIntegrityCheck() already runs at startup (main/db.ts:623).

    Suggested scope: (1) refunds table + idempotency keyed like payment_idempotency; (2) reproduce and fix the partial-refund math with a regression test before changing anything; (3) defer day-close integration to #279; (4) split the fail-closed-DB item out with first-run compatibility as an acceptance criterion.

  2. itsbkm commented on Sep 3, 2026

    @itsbkm
    Contributor

    Status update on this issue's 4 acceptance criteria:

    • refunds + refund_idempotency tables — already shipped (main/db.ts:4102-4130, main/routes/refunds.ts), landed separately in feat: add weighted products and refund workflows (#537).
    • Partial-refund over-collection / double-refund balance bug — still open. No reproduction has been found on main; split-check math already uses minor-unit rounding with remainder distribution (main/routes/bills.ts). Needs a failing test/evidence before changing anything.
    • Cash refunds correctly reduce expected cash in shift/day-close — blocked on Feature: Shift lifecycle management, cash drawer reconciliation, & business day close #279 (no shift/day-close concept exists yet).
    • Fail-closed DB recovery guard — done in #610. Production installs no longer silently create a blank database when flo.db has gone missing; they now fail closed with ERR_DATABASE_MISSING and direct the user to restore from backup.

    Leaving this open to track the remaining two items.

    🤖 Generated with Claude Code

  3. added
    type:featureNew capability or enhancement request
    state:blockedBlocked on an external dependency or decision
    on Sep 21, 2026
  4. itsbkm commented on Sep 21, 2026

    @itsbkm
    Contributor

    Re-scoping check per the stabilization plan: this issue's title still reads as the original "implement idempotent refund system" ask, but that's already shipped. docs/business-decisions.md ("Refunds and Staff Approval PINs", decided 2026-09-17) documents: approver_id-based approval, a refundable-balance ceiling per bill, time-tiered approval rules keyed off order creation time, expanded item eligibility (served/completed), independent refund payment method, store-credit refunds, and audit-log entries. Enforced by main/services/refund.ts and covered by test:refunds and test:refund-completed-orders.

    Labeling state:blocked rather than narrowing the title myself, since I don't have the original reporter's context on what idempotency/over-collection gap (if any) is still open against the current implementation. Owner: please confirm whether there's a concrete remaining gap, or close this as superseded by the shipped refund work.

  5. itsbkm commented on Sep 21, 2026

    @itsbkm
    Contributor

    Closing as superseded — confirmed shipped: idempotent refund system with a per-bill refundable-balance ceiling, tiered approval (owner/manager within 1hr, owner-only same business day, refused after), expanded item eligibility, independent refund payment method, store credit, and audit-log entries. See docs/business-decisions.md ("Refunds and Staff Approval PINs", decided 2026-09-17) and main/services/refund.ts. Covered by test:refunds and test:refund-completed-orders.

    If a specific remaining gap surfaces, please open a fresh issue scoped to that gap rather than reopening this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendBackend (Express/SQLite)enhancementNew feature or requeststate:blockedBlocked on an external dependency or decisiontype: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