Commit Graph

3 Commits

Author SHA1 Message Date
spouliot 9ce361235f Track store credit as a Customer Credits liability (GL)
Audit finding 9b: store-credit refunds and credit memos posted nothing to the GL
on issue (only a CreditMemo + Customer.CreditBalance), so outstanding store credit
was invisible on the balance sheet and the contra-revenue was recognized only on
apply. Introduces a 2350 "Customer Credits" liability so the credit is on the books
from issue to apply.

Model (chosen): lifecycle-equivalent to before, plus the liability is tracked.
- Issue (credit memos, goodwill, and store-credit refunds): DR Sales Discounts
  (4950) / CR Customer Credits (2350).
- Apply: DR Customer Credits / CR AR (was DR Sales Discounts / CR AR).
- Void unapplied remainder: DR Customer Credits / CR Sales Discounts.

Posting updated in all 8 sites: CreditMemosController Create/Apply/Void and
InvoicesController IssueCreditMemo/IssueRefund(store credit)/ApplyCredit/
VoidCreditMemo/CancelRefund. New 2350 account (seed + self-heal).

Reporting moved in lockstep so the books still balance: the 4950 contra-revenue
shifts from applied -> issued (active memos in full + applied portion of voided),
the 2350 liability = unapplied balance on active memos, AR still credited by
applications. Updated in FinancialReportService (balance sheet retained earnings,
trial balance, P&L) and LedgerService (per-account + prior-balance 2350 section).
Verified the balance-sheet identity for active and voided memos by hand; new
ledger test covers the 2350 lifecycle. Build clean; 284 unit tests pass.

Note: pre-existing quirks left untouched (out of 9b scope) — account 2300 is
seeded as "Payroll Liabilities" but resolved as Customer Deposits in code, and
LedgerService doesn't recompute 4950 so RecalculateBalances understates it; both
predate this change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 08:57:22 -04:00
spouliot 2a82a1d34b Make cash refunds reverse the sale instead of re-opening AR
Audit finding 9a: a cash refund posted DR AR (control up) while the customer
subledger went down — opposite directions, guaranteeing AR drift. Per the chosen
model, a cash refund now *reverses the sale*: DR Sales Returns (revenue portion)
+ DR Sales Tax Payable (tax portion) / CR Bank, with the customer AR balance left
untouched (the invoice stays paid; the sale is contra'd).

- New 4960 "Sales Returns & Allowances" contra-revenue account (seed + self-heal).
- Tax/revenue split centralized in Core RefundAllocation.Split so the posting and
  both reporting recomputes compute it identically (a mismatch would unbalance the
  trial balance).
- InvoicesController IssueRefund/CancelRefund: new posting + reversal; no AR/
  customer-balance change.
- LedgerService (per-account + prior balance): refunds debit Sales Returns + Sales
  Tax, no longer debit AR; bank credit unchanged.
- FinancialReportService: trial balance, balance sheet (retained earnings + tax
  liability), and P&L (contra-revenue line) all updated to match. Store-credit
  refunds are excluded everywhere (they post via CreditMemo, not the GL — 9b).

Verified: $108 refund (incl. $8 tax) -> bank -100... checks: assets -108 =
liabilities -8 + equity -100. New tests cover the split invariant and the ledger
postings. Build clean; 283 unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 19:58:42 -04:00
spouliot 404ab3c45d Add unit tests for LedgerService, AccountBalanceService, DepositsController, and GiftCertificatesController
181 tests total passing. Covers all 9 LedgerService transaction sources, debit/credit
balance mechanics, AR/AP movements, date range filtering, running balance computation,
and future-dated opening balance exclusion. Also covers deposit recording/deletion,
gift certificate lifecycle (issue, void, lazy expiry), and account balance recalculation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 15:48:34 -04:00