On a plant floor the general ledger has to do more than record cash and revenue — it has to track cost as it flows through production. Raw material is received at an actual price but valued in inventory at a standard cost; the difference is a purchase-price variance that belongs in the P&L, not buried in a balance. Inventory then moves into work-in-process, and out again as cost of goods sold. A generic GL has no concept of any of this — it sees a payables entry and an asset, and the variance disappears. This overlay turns the ledger into a cost-accounting ledger: standard-cost variance journals, a cost-center chart of accounts, and inventory that flows Inventory → WIP → COGS while every invariant holds to the cent.
The problem — and the money
The buyer is the plant controller or cost accountant who owns gross margin. If the purchase-price variance
isn't isolated to the P&L, margin is wrong and every make-vs-buy and pricing decision downstream is wrong
with it; if inventory and WIP don't roll cleanly onto the balance sheet, the plant's working-capital number
is fiction. The payoff is concrete: a receipt of 100 units with a standard cost of 100.00 and an actual
of 105.00 books inventory at 10000.00, routes the 500.00 variance straight to the P&L, and
credits payables at the 10500.00 actual — a balanced journal that keeps the trial balance at 0.00
and lands the variance exactly where a cost accountant needs to see it.
This is the GL suite + the manufacturing overlay. The six base skills — core, journal-entry, financial-reporting and the rest — are unchanged. The overlay only adds one column, one table, four chart-of-accounts leaves, and a variance-journal pattern. Same bucket, one copy of the rows.
What manufacturing adds
A small, additive diff on top of the suite (skill: gl/manufacturing → the
gl/overlays/manufacturing contract):
- One column on the
gl/coreaccountsmaster —cost_center— added withALTER TABLE … ADD COLUMNover the pg wire. A column-add, never a second copy. - Cost accounts — the chart of accounts gains Inventory (
1300) and Work in Process (1350) as assets, COGS (5100) and a Cost Variance / PPV account (5150) as P&L expenses, all taggedcost_center=plant-aand attached to the existing1000(assets) /5000(expenses) roots. - One table —
standard_costs(keyitem_id): the per-item standard vs actual unit cost (qty,std_unit_cost,actual_unit_cost, all moneyDECIMAL(18,2)) the variance journal reads. - One journal pattern — a standard-cost variance journal: DEBIT Inventory at standard, DEBIT Cost
Variance by the
actual − standardpurchase-price variance, CREDIT Accounts Payable at actual. Because standard + variance = actual, it balances. Inventory then flows Inventory → WIP → COGS through two balanced cost-flow journals.
Apply the diff — the ALTER, the cost accounts, the standard-cost table, and the balanced variance journal:
In the gl bucket, apply the manufacturing overlay: ALTER accounts ADD cost_center; add the Inventory (1300), WIP (1350), COGS (5100) and Cost Variance/PPV (5150) accounts on plant-a; create standard_costs and seed WIDGET-1 (qty 100, standard 100.00, actual 105.00); then post the standard-cost variance journal — inventory at standard, the variance to the P&L, payables at actual.
data_pg→data_table_create→data_table_upsertAdded cost_center to accounts (ALTER over pg — the master, not a copy). Seeded Inventory/WIP/COGS/Cost-Variance on plant-a. Created standard_costs; WIDGET-1 gives standard 10000.00 vs actual 10500.00 -> a 500.00 variance. Posted journal 2: DEBIT Inventory 10000.00 + DEBIT Cost Variance 500.00 + CREDIT Accounts Payable 10500.00 — balanced, debit=credit=10500.00.
export BUCKET=gl # the SAME bucket the GL suite built — the overlay only ADDs
# 1) the cost-center column on the gl/core accounts master — ALTER over the pg wire, never a copy
dodil data pg -b "$BUCKET" "ALTER TABLE accounts ADD COLUMN cost_center VARCHAR"
# 2) the cost accounts — assets (Inventory, WIP) + P&L expenses (COGS, Cost Variance/PPV), all on plant-a
dodil data table upsert accounts -b "$BUCKET" \
--row '{"account_id":1300,"name":"Inventory","type":"asset","normal_balance":"debit","parent_account_id":1000,"currency":"USD","active":true,"cost_center":"plant-a"}' \
--row '{"account_id":1350,"name":"Work in Process","type":"asset","normal_balance":"debit","parent_account_id":1000,"currency":"USD","active":true,"cost_center":"plant-a"}' \
--row '{"account_id":5100,"name":"COGS","type":"expense","normal_balance":"debit","parent_account_id":5000,"currency":"USD","active":true,"cost_center":"plant-a"}' \
--row '{"account_id":5150,"name":"Cost Variance (PPV)","type":"expense","normal_balance":"debit","parent_account_id":5000,"currency":"USD","active":true,"cost_center":"plant-a"}'
# 3) the per-item standard vs actual unit cost the variance journal reads
dodil data table create standard_costs -b "$BUCKET" --merge-key item_id \
--columns-json '[{"name":"item_id","type":"string","nullable":false},{"name":"item_name","type":"string","nullable":true},{"name":"cost_center","type":"string","nullable":true},{"name":"qty","type":"long","nullable":true},{"name":"std_unit_cost","type":"DECIMAL(18,2)","nullable":true},{"name":"actual_unit_cost","type":"DECIMAL(18,2)","nullable":true}]'
dodil data table upsert standard_costs -b "$BUCKET" \
--row '{"item_id":"WIDGET-1","item_name":"Widget assembly","cost_center":"plant-a","qty":100,"std_unit_cost":100.00,"actual_unit_cost":105.00}'
# 4) the STANDARD-COST VARIANCE JOURNAL — inventory at standard, variance to P&L, payables at actual (balanced)
dodil data table upsert journals -b "$BUCKET" \
--row '{"journal_id":2,"period":"2026-09","source":"cost-variance","status":"posted","memo":"Receive 100 WIDGET-1: inventory at standard, variance to P&L"}'
dodil data table upsert journal_lines -b "$BUCKET" \
--row '{"journal_id":2,"line_no":1,"account_id":1300,"debit":10000.00,"credit":0.00,"line_memo":"Inventory at standard (100 x 100.00)"}' \
--row '{"journal_id":2,"line_no":2,"account_id":5150,"debit":500.00,"credit":0.00,"line_memo":"Purchase price variance (actual-standard)"}' \
--row '{"journal_id":2,"line_no":3,"account_id":2100,"debit":0.00,"credit":10500.00,"line_memo":"Accounts Payable at actual (100 x 105.00)"}'Now the payoff — the variance derived from the standard-cost table, the journal balancing, and the variance landing in the P&L while the balance sheet ties:
In gl, derive WIDGET-1's variance from standard_costs (qty x (actual - standard)), confirm the variance journal balances, flow inventory Inventory -> WIP -> COGS, then roll the ledger up by account type and show that the variance hits the P&L and the balance sheet ties.
data_pg→data_sqlWIDGET-1: standard 10000.00, actual 10500.00 -> variance 500.00. Journal 2 balances (debit=credit=10500.00). After the cost-flow journals inventory and WIP net to 0.00 and COGS is 10000.00. Account-type rollup: assets 1000000.00, liabilities 10500.00, equity 1000000.00, expense 10500.00 (COGS 10000.00 + variance 500.00) -> net income -10500.00. Balance sheet ties (assets = liabilities + equity + net income), trial balance 0.00.
# the variance is DERIVED from standard_costs, not hand-keyed
dodil data sql -b "$BUCKET" "SELECT item_id, qty,
CAST(qty*std_unit_cost AS VARCHAR) AS std_total,
CAST(qty*actual_unit_cost AS VARCHAR) AS actual_total,
CAST(qty*actual_unit_cost - qty*std_unit_cost AS VARCHAR) AS variance FROM standard_costs"
# WIDGET-1 | 100 | 10000.00 | 10500.00 | 500.00
# inventory flows Inventory -> WIP -> COGS (two balanced cost-flow journals)
dodil data table upsert journals -b "$BUCKET" \
--row '{"journal_id":3,"period":"2026-09","source":"cost-flow","status":"posted","memo":"Issue to production (Inventory -> WIP)"}' \
--row '{"journal_id":4,"period":"2026-09","source":"cost-flow","status":"posted","memo":"Complete + ship (WIP -> COGS)"}'
dodil data table upsert journal_lines -b "$BUCKET" \
--row '{"journal_id":3,"line_no":1,"account_id":1350,"debit":10000.00,"credit":0.00}' \
--row '{"journal_id":3,"line_no":2,"account_id":1300,"debit":0.00,"credit":10000.00}' \
--row '{"journal_id":4,"line_no":1,"account_id":5100,"debit":10000.00,"credit":0.00}' \
--row '{"journal_id":4,"line_no":2,"account_id":1350,"debit":0.00,"credit":10000.00}'
# re-materialize the ledger, then roll up by account type — the variance HITS the P&L, the balance sheet TIES
dodil data pg -b "$BUCKET" "INSERT INTO ledger (account_id, balance, currency, as_of)
SELECT jl.account_id, SUM(jl.debit)-SUM(jl.credit), 'USD', now()
FROM journal_lines jl JOIN journals j ON j.journal_id=jl.journal_id
WHERE j.status IN ('posted','reversed') GROUP BY jl.account_id
ON CONFLICT (account_id) DO UPDATE SET balance=EXCLUDED.balance, as_of=EXCLUDED.as_of"
dodil data sql -b "$BUCKET" "
SELECT CAST(SUM(l.balance) AS VARCHAR) AS trial_balance,
CAST(SUM(CASE WHEN a.type='asset' THEN l.balance ELSE 0 END) AS VARCHAR) AS assets,
CAST(-SUM(CASE WHEN a.type='liability' THEN l.balance ELSE 0 END) AS VARCHAR) AS liabilities,
CAST(-SUM(CASE WHEN a.type='equity' THEN l.balance ELSE 0 END) AS VARCHAR) AS equity,
CAST(-SUM(CASE WHEN a.type IN ('revenue','expense') THEN l.balance ELSE 0 END) AS VARCHAR) AS net_income
FROM ledger l JOIN accounts a ON a.account_id=l.account_id"
# trial_balance 0.00 | assets 1000000.00 | liabilities 10500.00 | equity 1000000.00 | net_income -10500.00
# assets = liabilities + equity + net_income -> 1000000.00 = 10500.00 + 1000000.00 + (-10500.00) TIESThe variance is 500.00 because standard + variance = actual (10000.00 + 500.00 = 10500.00), so the
journal balances by construction. Inventory and WIP net to 0.00 once production completes; COGS carries
10000.00 and the Cost Variance carries 500.00 — both in the P&L, so net income already absorbs the
variance and the balance sheet ties without a plug.
Scaffold it — the one-shot
With the DODIL MCP connected, one prompt composes the suite and this overlay:
Scaffold a GL for my plant — the GL suite plus the manufacturing overlay.
Base: the full gl suite (chart of accounts + account-hierarchy graph + journal-entry / period-close /
financial-reporting / subledger-reconciliation / multi-currency) on one bucket, money DECIMAL(18,2),
idempotent ON CONFLICT posting.
Then apply the manufacturing overlay on the SAME bucket:
1. ALTER accounts ADD cost_center (pg wire — the master, not a copy).
2. Add Inventory (1300), WIP (1350), COGS (5100), Cost Variance/PPV (5150) on plant-a, under the existing
asset / expense roots.
3. Create standard_costs (key item_id) — the per-item standard vs actual unit cost.
4. Post a standard-cost variance journal: DEBIT Inventory at standard, DEBIT Cost Variance by
actual - standard, CREDIT Accounts Payable at actual (balanced); then flow Inventory -> WIP -> COGS.
Seed WIDGET-1 above and show the variance hitting the P&L while the balance sheet ties and the trial
balance stays 0.00.Verify
Every result below was live-validated on 2026-09-03 (org IHDIASH, throwaway bucket glovlmfg0903, torn
down after):
# the column lands (nullable, over the pg wire)
dodil data pg -b "$BUCKET" "ALTER TABLE accounts ADD COLUMN cost_center VARCHAR"
# the variance journal balances: debit = credit = 10500.00
dodil data sql -b "$BUCKET" "SELECT CAST(SUM(debit) AS VARCHAR) d, CAST(SUM(credit) AS VARCHAR) c,
CAST(SUM(debit)-SUM(credit) AS VARCHAR) imbalance FROM journal_lines WHERE journal_id=2" # -> 10500.00 | 10500.00 | 0.00
# the variance hits the P&L + the balance sheet ties + trial balance 0.00
dodil data sql -b "$BUCKET" "SELECT a.type, CAST(SUM(l.balance) AS VARCHAR) net FROM ledger l
JOIN accounts a ON a.account_id=l.account_id GROUP BY a.type ORDER BY a.type"
# a bare re-INSERT of a committed line raises 23505; the ON CONFLICT re-post leaves counts + trial balance unchangedThe ALTER-add, the standard_costs table, the balanced standard-cost variance journal (10500.00 = 10500.00), the Inventory → WIP → COGS cost flow, the variance landing in the P&L (expense net 10500.00
= COGS 10000.00 + variance 500.00), the balance-sheet tie (assets 1000000.00 = liabilities 10500.00 + equity 1000000.00 + net income -10500.00), and the trial balance holding at exactly 0.00 across 7
accounts are all proven live — including an idempotent ON CONFLICT re-post that leaves the journal at 3
lines and the trial balance at 0.00.
Connect your tools
Everything the overlay wrote lives in the one DataK3 bucket, reachable by your own stack — a cost-accounting
/ MES integration reads standard_costs and the cost accounts over the Postgres wire; a plant-controller
dashboard runs the account-type rollup as plain SQL to chart margin and variance. data connect gl prints
the endpoints. Full, live-validated walkthrough: Connect your tools.
Composes
This page is not a fork of the GL — it is a composition:
- Base: the six GL suite skills (
gl/core,gl/journal-entry,gl/financial-reporting, …) — the system of record, unchanged. - Overlay:
gl/overlays/manufacturing— the additive diff above (thecost_centercolumn, the Inventory / WIP / COGS / Cost-Variance accounts, thestandard_coststable, the standard-cost variance journal, the Inventory → WIP → COGS cost flow).
Read the base to learn the mechanics; this overlay is the small, industry-specific diff on top.