Skip to content

fix: folio reversal loop + payment refund UI (closes #166)#170

Merged
telivity-otaip merged 2 commits into
mainfrom
cursor/166-folio-reversal-refund-ci-69eb
Jul 22, 2026
Merged

fix: folio reversal loop + payment refund UI (closes #166)#170
telivity-otaip merged 2 commits into
mainfrom
cursor/166-folio-reversal-refund-ci-69eb

Conversation

@telivity-otaip

@telivity-otaip telivity-otaip commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #166 (contributor Gugabit): folio charge Reverse could be clicked repeatedly (including on reversal rows), creating a negative-balance deadlock, and captured payments had no refund action in the dashboard.

Note: supersedes #169 — that branch became push-locked after the first commit; this branch includes the same fix plus the README/docs/test-stats.json sync that CI requires after adding 2 tests.

Root causes

  1. Backend hole: reverseCharge blocked a second reverse of the same original, but never rejected reversing a row that itself had isReversal: true. Reversing reversal R created R2 = -R, then R3, indefinitely. The same hole existed on the direct postCharge path when posting isReversal: true against a reversal row.

  2. UI gating bug: Folios used a phantom c.isReversed field that is not in the schema/API (only isReversal + originalChargeId exist), so Reverse showed on every unlocked open-folio row.

  3. Missing refund UI: POST /v1/payments/:id/refund already existed with full/partial refund support. The Folios payments list simply rendered no action button. Negative direct payments stay rejected by design (CreatePaymentDto positive-only); refunds are the sanctioned path.

Changes

API

  • Reject reversing a reversal in reverseCharge and in postCharge when isReversal targets another reversal (Cannot reverse a reversal transaction).
  • Operational-integrity rule (not a KB domain invention): undo a mistaken reversal by re-posting the charge.

Dashboard

  • Derive reversed originals from isReversal + originalChargeId; hide Reverse on reversal rows and already-reversed charges; strikethrough originals that have been reversed.
  • Add Refund on open-folio payments in captured / settled / partially_refunded (excludes negative refund-child rows).
  • Toast error feedback on reverse/refund failures.

Tests / docs

  • Unit tests for reverse-of-reversal rejection on both service paths (+2 tests).
  • Synced README + docs/test-stats.json to 1153 tests (fixes CI “README test counts are stale”).
  • folios.refund (+ confirm/success/failure keys) in en / pt-BR; minimal folios keys in de.

Verification

  • Folio + payment unit suites green locally
  • pnpm readme:sync-tests → 1153 / 139

Closes #166

cursoragent and others added 2 commits July 22, 2026 21:06
…e in dashboard (#166)

Prevent reverse-of-reversal deadlocks in reverseCharge/postCharge, fix
dashboard Reverse gating to use isReversal/originalChargeId, and wire
the existing payment refund API into Folios with toast error feedback.

Co-authored-by: telivity-otaip <telivity-otaip@users.noreply.github.com>
Co-authored-by: telivity-otaip <telivity-otaip@users.noreply.github.com>
@telivity-otaip
telivity-otaip marked this pull request as ready for review July 22, 2026 23:12
@telivity-otaip telivity-otaip self-assigned this Jul 22, 2026
@telivity-otaip
telivity-otaip merged commit 1a5a4ac into main Jul 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Folios & Billing

2 participants