Skip to content

Rebuild the inventory model monthly, with a fourth method for stock ageing - #4

Merged
anthomakr merged 2 commits into
mainfrom
feat/inventory-monthly-four-methods
Aug 9, 2026
Merged

Rebuild the inventory model monthly, with a fourth method for stock ageing#4
anthomakr merged 2 commits into
mainfrom
feat/inventory-monthly-four-methods

Conversation

@anthomakr

Copy link
Copy Markdown
Owner

Follow-up to #3, which shipped the three-method yearly model. Two changes on top of it.

1. One tab per method, and grouped assumptions

Assumptions were a flat list of 18 drivers and the three methods shared one section, so reading a single recipe meant filtering the other two out by eye.

Assumptions are now grouped in four sub-sections (revenue and margin, working capital terms, capital and financing and tax, inventory method drivers). Sub-sections group inside the tab rather than creating new ones, verified against the Excel export: still one Assumptions sheet. Each method gets a root section, so one tab per method, and pulls the drivers it reads through callups. The Assumptions tab stays the only source of truth: a callup mirrors a value, it does not own it.

2. Monthly, and a fourth method

The yearly model could not express stock ageing at all. On flat annual flows, a "last 180 days of purchases" rule is just a DIO of 180 in disguise: I checked, and the engine spreads a yearly flow evenly across months, so the mechanism would have been inert.

Rebuilt on a monthly grain with a real seasonal profile over 2024-2029. 2024 is a ramp-in year, not a forecast: it gives the trailing-12-month windows a full history so 2025 onward is clean, and it absorbs the working capital build.

COGS12 (trailing twelve months) is what every ratio method reads. A monthly COGS times a ratio would make inventory swing with the season instead of with the business.

Method D forecasts the flows and lets the stock follow, instead of inferring the stock from COGS. Under FIFO the remaining stock is always the most recent purchases, so age is a subtraction rather than a layer-by-layer simulation:

Stock older than 60 days  = MAX(0, inventory − purchases of the last 2 months)
Write-off (over 180 days) = MAX(0, stock before write-off − purchases of the last 6 months)

The finding

Dec 2025 Dec 2026 Dec 2027 Dec 2028 Dec 2029
Share of stock older than 60 days 16% 16% 17% 20% 22%
Write-off 0 0 0 0 0

At normal purchasing the 180-day write-off never fires, and the stock ages anyway. A business turning stock in 60 days never holds anything for 180, so the accounting alarm stays silent for years while a quarter of the warehouse quietly gets old. Raise Purchase policy factor from 1.02 to 1.08 and two thirds of the stock is over 60 days with the write-off still at zero.

No ratio method can produce that column: A, B and C derive the stock from COGS, so by construction they cannot know how old it is.

Continuity

A, B and C reproduce the yearly model to the euro at every December, so nothing in the earlier framing breaks:

Method Dec 2025 Dec 2029
A. Percent of COGS 4,315,200 6,849,537
B. Inventory days 4,322,192 6,255,285
C. Unit build 4,330,667 5,984,138
D. Ageing from flows 3,509,000 6,051,723

Spreads unchanged: 594,252 A vs B and 865,398 A vs C in 2029. Annual P&L totals identical (net income 2,475,000 in 2025 through 4,874,093 in 2029).

Verification

Balance check at 0 on all 72 periods and all four methods, re-run with the switch on 1, 2, 3 and 4. Cash never goes negative under any method (floor 2.1M under D, the most demanding). layerz_validate_model clean. The model is promoted to a Layerz template at user scope, not system.

The superseded yearly model is no longer referenced anywhere in the repo and can be deleted once this merges.

🤖 Generated with Claude Code

anthomakr and others added 2 commits August 9, 2026 22:34
Assumptions were a flat list of 18 drivers and the three methods shared
one section, so reading a single recipe meant filtering the other two out
by eye.

Assumptions are now grouped in four sub-sections (revenue and margin,
working capital terms, capital and financing and tax, inventory method
drivers). Sub-sections group inside the tab rather than creating new
ones, verified against the Excel export: still one Assumptions sheet.

Each method gets a root section, so one tab per method, and pulls the
drivers it reads through callups. The Assumptions tab stays the only
source of truth: a callup mirrors a value, it does not own it. Added
Spread A vs C alongside Spread A vs B, so the cost of each simplification
is a line of the model rather than a subtraction left to the reader.

Structure only: every value is unchanged and the balance check still
holds at 0 on all five periods.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three-method yearly model could not express stock ageing: on flat
annual flows a "last 180 days of purchases" rule is just a DIO of 180 in
disguise. Rebuilt on a monthly grain with a real seasonal profile, over
2024-2029, where 2024 is a ramp-in year that gives the trailing-12-month
windows a full history and absorbs the working capital build.

Method D forecasts purchases and lets the stock follow, instead of
inferring the stock from COGS. Under FIFO the remaining stock is always
the most recent purchases, so age is a subtraction, not a layer-by-layer
simulation: stock older than 60 days = MAX(0, inventory − purchases of
the last 2 months).

The finding it produces is the reason it exists: at normal purchasing the
180-day write-off never fires across the whole horizon, and the stock
still ages from 16% to 22% over 60 days. The accounting alarm stays
silent for years while a quarter of the warehouse gets old, and no ratio
method can see it.

A, B and C reproduce the previous yearly model to the euro at every
December, so the headline spreads are unchanged: 594,252 A vs B and
865,398 A vs C in 2029. Annual P&L totals are identical too.

Balance check verified at 0 on all 72 periods and on all four methods.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@anthomakr
anthomakr merged commit 1178752 into main Aug 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant