8b9a3dff41
Repeatable post-deploy (and periodic) check that proves the books are consistent against real data: per company, Trial Balance debits==credits, Balance Reconciliation shows no drift, then Recalculate Balances and re-check. Includes the read-only pre-deploy migration preview, the two pending migrations in order, account spot-checks for the audit-touched accounts, and the inventory/sales-tax policy reminders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
87 lines
4.9 KiB
Markdown
87 lines
4.9 KiB
Markdown
# Accounting Deploy & Verification Checklist
|
||
|
||
> Repeatable steps to run whenever accounting changes ship (and a good periodic health check).
|
||
> Goal: prove the books are internally consistent with **real data**, not just code review.
|
||
> Companions: `docs/ACCOUNTING_AUDIT.md` (findings/decisions), `docs/ACCOUNTING_LEDGER_REFACTOR.md` (future).
|
||
|
||
The decisive invariant: **a Trial Balance whose total debits == total credits, with no drift on the
|
||
Balance Reconciliation report, for every company.** If that holds, the ledger is sound by construction.
|
||
|
||
---
|
||
|
||
## 1. Pre-deploy (against the production DB, read-only)
|
||
|
||
Migrations are applied **manually** at deploy (the app does not auto-migrate). Two are pending from the
|
||
2026-06 audit; apply in this order:
|
||
|
||
1. `RenameDepositsAccountAddPayroll` (O1 — renames 2300 → "Customer Deposits", adds 2400 Payroll)
|
||
2. `FixGiftCertificateLiabilityAccount` (O5 — relabels mislabeled 2500 → GC Liability, adds 2900)
|
||
|
||
Both are non-destructive (no account Id / number / balance is changed; only relabels + additive inserts).
|
||
Preview exactly what they'll touch (read-only — swap account numbers as needed):
|
||
|
||
```sql
|
||
-- O1: 2300 rows that will be renamed (only those still named the default), and who gets a new 2400
|
||
SELECT CompanyId, AccountNumber, Name, CurrentBalance,
|
||
CASE WHEN Name = 'Payroll Liabilities' THEN 'WILL RENAME -> Customer Deposits' ELSE 'kept as-is' END AS Action
|
||
FROM Accounts WHERE AccountNumber = '2300' AND IsDeleted = 0 ORDER BY CompanyId;
|
||
|
||
-- O5: 2500 rows that will be relabeled (only those still named "Long-Term Loan")
|
||
SELECT CompanyId, AccountNumber, Name, CurrentBalance,
|
||
CASE WHEN Name = 'Long-Term Loan' THEN 'WILL RELABEL -> Gift Certificate Liability' ELSE 'kept as-is' END AS Action
|
||
FROM Accounts WHERE AccountNumber = '2500' AND IsDeleted = 0 ORDER BY CompanyId;
|
||
```
|
||
|
||
## 2. Deploy
|
||
|
||
1. Merge `dev` → `master`, trigger the Jenkins production job.
|
||
2. Apply the two migrations above (in order) to the production DB.
|
||
|
||
## 3. Post-deploy verification (per company — all of them)
|
||
|
||
Run for **every** company (there are ~7). Most is doable from the app UI under Reports / Finance.
|
||
|
||
- [ ] **Trial Balance** — open it. **Total Debits must equal Total Credits.** Any difference is a
|
||
one-sided posting and must be investigated before trusting other reports.
|
||
- [ ] **Balance Reconciliation report** (`/Reports/Reconciliation`) — every account's *stored* balance
|
||
should match the *recomputed* balance (no drift highlighted). Also confirm:
|
||
- AR control account == sum of customer balances (AR subledger).
|
||
- AP control account == sum of vendor balances (AP subledger).
|
||
- [ ] **Recalculate Balances**, then re-open the Trial Balance and Balance Reconciliation. This exercises
|
||
the recompute paths the audit fixed (`LedgerService`). After a recalc:
|
||
- Trial Balance still balances.
|
||
- Reconciliation shows no drift (stored now == recomputed by definition; the point is TB stays balanced
|
||
and the values look sane).
|
||
|
||
## 4. Spot-check the accounts the audit touched
|
||
|
||
For each company, glance at these on the Trial Balance / chart of accounts:
|
||
|
||
- [ ] **2300 Customer Deposits** — named correctly; balance == outstanding (un-applied) customer deposits.
|
||
- [ ] **2400 Payroll Liabilities** — exists (likely 0 unless payroll is tracked).
|
||
- [ ] **2500 Gift Certificate Liability** — named correctly; balance == outstanding GC value (issued − redeemed − voided).
|
||
- [ ] **2900 Long-Term Loan** — present where the old 2500 was relabeled.
|
||
- [ ] **4950 Sales Discounts / 4960 Sales Returns** — contra-revenue, show as debit-balance.
|
||
- [ ] **AR** — for any company that uses gift certificates or has written off an invoice, confirm AR is not
|
||
overstated (these were O7 / O8). Cross-check AR total against the AR Aging report.
|
||
|
||
## 5. If something is off
|
||
|
||
- A Trial Balance that doesn't balance → a posting hit only one side. Note the company + amount and check it
|
||
against the findings in `docs/ACCOUNTING_AUDIT.md` (the resolved O2/O6/O7/O8 patterns) before assuming a new bug.
|
||
- Drift on the Reconciliation report → run **Recalculate Balances**; if it persists, the recompute is missing a
|
||
posting type (same class as the audit findings).
|
||
- Do **not** treat a recalc as a fix for a real imbalance — it makes stored == recomputed, which can *hide* a
|
||
one-sided posting if only one engine is wrong. The Trial Balance balancing is the real test.
|
||
|
||
## 6. Policy reminders (from the audit)
|
||
|
||
- **Inventory = expensed at purchase (periodic).** Do **not** map an item's `CogsAccountId` + `InventoryAccountId`
|
||
(set only via CSV import) while also expensing powder at purchase — that double-counts COGS. Keep those empty.
|
||
- Sales-tax remittance is capped at the outstanding 2200 balance (O4) — you cannot over-remit.
|
||
|
||
## When to repeat
|
||
|
||
- After any accounting feature change or import.
|
||
- As a periodic health check (e.g. monthly), run Section 3 — it's cheap and catches drift early.
|