Record O9 decision: expense materials at purchase (periodic inventory)

Owner decision: this is a service business that uses materials to deliver a service
rather than selling inventory, so powder/consumables are expensed at purchase (bill
to a COGS/expense account) and inventory is not capitalized on the Balance Sheet.

No code change required — the Balance Sheet already behaves this way, and the
perpetual consumption-COGS path (O6) is opt-in via item account mappings (set only
via CSV import) and stays dormant under this policy. Documents the double-count
footgun (do not both expense at purchase and map item COGS/Inventory accounts) and
locks the periodic choice into the ledger-refactor plan's Phase 5.

All accounting audit findings O1-O9 now resolved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 21:37:43 -04:00
parent 012f4d9a3e
commit 57ec3ed127
2 changed files with 24 additions and 16 deletions
+18 -13
View File
@@ -193,19 +193,24 @@ parallel with the live postings. The durable fix is to make **every** financial
lines and drive all reports/recompute from those lines alone (single source of truth) — O8's write-off already lines and drive all reports/recompute from those lines alone (single source of truth) — O8's write-off already
does exactly this, which is why it was the cleanest. Worth considering before the ledger grows further. does exactly this, which is why it was the cleanest. Worth considering before the ledger grows further.
### O9 — Balance Sheet does not capitalize/relieve inventory (periodic vs perpetual) — **OPEN, needs policy decision** ### O9 — Inventory accounting policy — **RESOLVED (policy decision: expense at purchase / periodic)**
- Surfaced while fixing O6. The **Trial Balance** computes an inventory asset as opening + purchase bill-lines - **Decision (owner, 2026-06-19):** materials (powder, consumables) are **expensed at purchase**, not
consumption (perpetual), but the **Balance Sheet** `ComputeBalance` does **not** add inventory purchase capitalized as inventory-for-resale — this is a service business that *uses* materials to deliver a
bill-lines to the asset and instead expenses all bill costs through retained earnings (periodic). So the BS service, it does not sell inventory. So the **periodic** model is correct.
inventory line ≈ opening balance only, and BS vs TB disagree on inventory. O6's consumption relief was - **Implication — no code change needed:**
therefore intentionally **not** applied to the BS (doing so alone would drive inventory negative / unbalance - The Balance Sheet correctly does **not** capitalize inventory (cost hits the P&L at purchase via the
the sheet). bill line categorized to a COGS/expense account). The earlier decision to leave the BS untouched stands.
- **Impact:** Balance Sheet inventory asset is understated and COGS timing differs between BS (periodic) and - Purchase receiving creates a `Purchase`-type `InventoryTransaction` that posts **no** GL — correct.
P&L/TB (now perpetual for consumption). Pre-existing; not a trial-balance imbalance. - The **perpetual** consumption-COGS path (O6: `DR COGS / CR Inventory` on JobUsage/Waste) is **opt-in**:
- **Fix direction:** decide on an inventory accounting policy (perpetual vs periodic) and make the Balance it only fires when an item has *both* `CogsAccountId` and `InventoryAccountId`, which are set **only via
Sheet consistent with the Trial Balance / P&L. Best done as part of the JournalEntry single-source refactor. CSV import** (never by normal item creation). Under this policy those mappings should be left empty, so
the path stays dormant. O6's recompute fix remains correct and harmless when unused.
- **⚠ Footgun to avoid:** do **not** both expense powder at purchase (bill → COGS account) *and* map an
item's `CogsAccountId` + `InventoryAccountId` — that would record the cost twice (once at purchase, once at
consumption). Keep item COGS/Inventory account mappings empty under the expense-at-purchase policy.
## Status ## Status
Findings **O1O8 + the read-path sweep are resolved** on `dev`. **O9 is newly identified and OPEN** (inventory **All findings O1O9 + the read-path sweep are resolved** on `dev` (O9 by policy decision — expense at
capitalization policy — needs a decision, recommend folding into the JournalEntry single-source refactor). purchase — needing no code change). The optional structural follow-up is the JournalEntry single-source
refactor (`docs/ACCOUNTING_LEDGER_REFACTOR.md`), which would prevent the O2/O6/O7/O8 bug class from recurring.
Original audit numbering #13/#5/#6/#8 remains unrecoverable (see top). Nothing merged to `master` yet. Original audit numbering #13/#5/#6/#8 remains unrecoverable (see top). Nothing merged to `master` yet.
+6 -3
View File
@@ -83,9 +83,12 @@ contra-revenue — all disappear because they are simply journal lines.
against the old numbers using the snapshot harness below. against the old numbers using the snapshot harness below.
- **Phase 4** — post conversion-balance JEs at the cutover date; delete the dead re-derivation code - **Phase 4** — post conversion-balance JEs at the cutover date; delete the dead re-derivation code
(~1,000+ lines). (~1,000+ lines).
- **Phase 5** — decide **O9** (perpetual vs periodic inventory) and implement it as JE postings. Perpetual - **Phase 5** — **O9 is already decided: expense at purchase (periodic).** Materials are used to deliver a
falls out naturally: purchases DR Inventory, consumption DR COGS / CR Inventory (O6 already posts the service, not sold, so they are expensed when purchased (bill → COGS/expense account); inventory is **not**
consumption half). This makes the Balance Sheet inventory line consistent with the Trial Balance / P&L. capitalized on the Balance Sheet. In the refactor, post material purchases as `DR COGS / CR AP` (expense
immediately) and do **not** emit a separate consumption JE. The opt-in perpetual path (item
`CogsAccountId` + `InventoryAccountId`) stays unused under this policy — keep those mappings empty to avoid
double-counting. No perpetual inventory asset/relief postings.
## Prerequisites before starting ## Prerequisites before starting