mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
7464d55b6c
Under accrual, a vendor expense is recognized once — when the bill is entered (Dr Expense / Cr A/P). Paying a bill must only relieve the liability (Dr A/P / Cr Bank) and never re-hit the expense account. This hardens the existing Pay Bills flow against re-recognition and double counting. - Strict bill matching: extract a pure, dependency-free matcher into lib/billMatch.ts (debitMatchesBill/matchOpenBills) — same vendor, status open/overdue/partially_paid, amount within $0.01 of remaining (or <= remaining for partials), debit date within ±30 days of due/issue date. Unit-tested in billMatch.test.ts (covers the identical-recurring-charge regression). - AccountingBankingPage.saveTx uses the strict rule (was "any open bill"), so a thrice-paid identical charge only clears the in-window bill. - Bank-feed categorizer (bulkSetCategory) matches open bills before assigning an expense COA: single match clears A/P + links the bill; multi-match is skipped with a prompt to resolve in Pay Bills; no match categorizes as a direct expense. - DB guard: add accounting.transactions.bill_id (FK -> bills) and CHECK chk_bill_payment_no_coa (bill_id IS NULL OR coa_account_id IS NULL) so a bill-linked payment can never carry an expense category. Both writers set bill_id on single-bill payments; partial payments now write partially_paid. - One-time cleanup: clear coa_account_id on Ashley Manor's 8 double-counted bill payments ($2,198.98) so the GL reposts them as Dr A/P / Cr Bank. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>