You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Refunds Table & Idempotency Tracking:
Add dedicated refunds table and refund_idempotency table (Migration 72).
Track amount_cents, original_method, approved_by, and shift_id.
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).
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.
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.
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.
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.
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.
Activity
khaira777 commentedon Aug 16, 2026
Verdict: VALID BUT NEEDS RESCOPING
Findings:
refunds/refund_idempotencytable 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"; statusesvoided/void_adjustmenthandled throughoutmain/routes/bills.tsandmain/routes/order-items.ts:87-158).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.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).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; noterunStartupIntegrityCheck()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.fix(db): fail closed when production database file goes missing (#278)
fix(db): retry and log marker-write failures instead of swallowing them
fix(db): fail closed when production database file goes missing (#278) (
itsbkm commentedon Sep 3, 2026
Status update on this issue's 4 acceptance criteria:
refunds+refund_idempotencytables — already shipped (main/db.ts:4102-4130,main/routes/refunds.ts), landed separately infeat: add weighted products and refund workflows (#537).main; split-check math already uses minor-unit rounding with remainder distribution (main/routes/bills.ts). Needs a failing test/evidence before changing anything.flo.dbhas gone missing; they now fail closed withERR_DATABASE_MISSINGand direct the user to restore from backup.Leaving this open to track the remaining two items.
🤖 Generated with Claude Code
itsbkm commentedon Sep 21, 2026
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.
itsbkm commentedon Sep 21, 2026
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.