Guard money-account selections; derive account type from sub-type
Item 1 — server-side guard (defense in depth) on payment-source / deposit / reconcilable account selections. New AccountGuard.IsValidMoneyAccountAsync checks the submitted account is active, company-owned, and an Asset or Liability before any GL posting, at: bill RecordPayment, bill Create (payNow), bill EditPayment, BankReconciliation.Create, and deposit Record. The dropdowns already constrain normal users; this rejects tampered/stale POSTs. Per the "trust the operator" decision it still allows A/R etc. (any Asset/Liability) — it only blocks non-money types. Item 2 — account AccountType is now derived from the chosen AccountSubType on create/edit via the new AccountClassification.TypeForSubType (single source of truth, also used by the Create pre-select). The two can no longer disagree, so the sub-type-based debit/credit sign convention is always consistent with the account's type. A read-only sweep of the dev DB found 0 existing mismatches, so no repair tool was built. Audit doc updated: both backlog items marked resolved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -230,16 +230,21 @@ deposit account. **No ledger-drift bugs found.** Notes:
|
||||
- **Deposit account picker ↔ recompute:** consistent. Live posting debits the chosen `DepositAccountId`, and
|
||||
`LedgerService` reproduces the debit by `DepositAccountId == accountId` (lines ~78/724). Picking a non-default
|
||||
deposit account recomputes correctly.
|
||||
- **Bank / "pay-from" / bank-rec pickers** now list all Asset + Liability accounts with **no server-side type
|
||||
guard** — a user could pick a nonsensical source (e.g. A/R). Postings stay sign-correct
|
||||
(`AccountBalanceService` keys sign off `AccountSubType`), and this is the intended "trust the operator"
|
||||
tradeoff, but there is no longer a guardrail. Accepted; noted here for visibility.
|
||||
- **Bank / "pay-from" / bank-rec pickers — server-side guard added (2026-06-20):** the submitted account is
|
||||
now validated via `AccountGuard.IsValidMoneyAccountAsync` (active, company-owned, AccountType Asset or
|
||||
Liability) before any posting, at bill `RecordPayment` / `Create(payNow)` / `EditPayment`,
|
||||
`BankReconciliation.Create`, and deposit `Record`. Defense in depth against tampered/stale POSTs. Per the
|
||||
"trust the operator" decision this still allows e.g. A/R (an Asset) as a source — it only rejects
|
||||
non-money types (Revenue/Expense/Equity/COGS).
|
||||
- **Latent deposit imbalance — RESOLVED (2026-06-20):** a deposit saved with a null `DepositAccountId` posted
|
||||
`CR 2300` with no offsetting debit → unbalanced. `DepositsController.Record` now blocks recording when the
|
||||
`2300` Customer Deposits account exists but no deposit/bank account resolves (user must pick one). When `2300`
|
||||
doesn't exist (company not using accounting), no GL posts at all, so the deposit is still allowed through.
|
||||
- **Pre-existing type/sub-type mismatch risk:** account create does not enforce a valid type↔sub-type pairing,
|
||||
and sign convention keys off sub-type — a mis-paired account would post with the wrong sign. Backlog item.
|
||||
- **Type/sub-type mismatch risk — RESOLVED (2026-06-20):** account `AccountType` is now **derived** from the
|
||||
chosen `AccountSubType` on create/edit via `AccountClassification.TypeForSubType` (single source of truth,
|
||||
also used by the Create pre-select), so the two can never disagree and the sub-type-based sign convention is
|
||||
always consistent with the displayed type. A read-only sweep of the dev DB (109 accounts) found **0** existing
|
||||
mismatches, so no repair tool was needed.
|
||||
|
||||
## Status
|
||||
**All findings O1–O9 + the read-path sweep are resolved** on `dev` (O9 by policy decision — expense at
|
||||
|
||||
Reference in New Issue
Block a user