BFBambooForge Labs

Stock Valuation History & Ledger Reconciliation

Opening, in, out and closing stock value per account for a period, next to the ledger - read on the accounting date, so both answer one question.

Buy on the Odoo Apps StoreOpen the live demoAccounting€232Community & Enterprise

Available for Odoo 16.0, Odoo 17.0, Odoo 18.0. Technical name bambooforge_valuation_history.

Odoo 16.0Odoo 17.0Odoo 18.0
Full walkthrough on a live Odoo 18.0 database, with subtitles. It ends with what this app deliberately does not do.

Stock Valuation History & Ledger Reconciliation

At every closing somebody asks the same question: the stock account says one number, inventory says another, where is the difference. Odoo Community can list the valuation layers and filter them to a date, and from there the answer gets assembled by hand in a spreadsheet.

This is that answer as a report — and it is built on one correction that matters more than the layout.

Both sides on the accounting date

A valuation layer has no date of its own. Odoo's layer list groups by Date, and that field is create_date — the moment the row was written to the database. The journal entry the layer produced carries an accounting date, and that is what the trial balance is built from.

Most of the time they are the same day and nobody notices. Then somebody receives goods on the 2nd of January against December, or an entry is reset to draft and re-posted into the prior period, and the two dates part company. From then on the layer report and the balance sheet disagree, by exactly that amount, and neither of them says why.

This report places every layer on the date of the journal entry it produced. A layer that never produced one is placed on the day it was written — and still counted, because a value that reached no entry is precisely the sort of difference the report exists to surface.

Opening, in, out, closing

Pick a period. For every stock valuation account you get:

Opening

everything valued before the start of the period

In

positive value in the period — receipts, revaluations up

Out

negative value in the period — deliveries, scrap, write-downs

Closing

opening + in − out

Closing is never stored. It is its own parts added together, every time the report runs, because a stored total is one more number that can end up disagreeing with the numbers under it.

So a change in the stock value is explained rather than only observed: the balance moved by €40,000, of which €52,000 came in and €12,000 went out.

Against the ledger

Next to Closing sits In the Ledger — the posted balance of that same account on the same date — and the difference between them.

Every account a category values stock on appears, whether or not anything moved through it, and so does any account that stock entries once reached but no category names today — the balance left behind when a chart is renumbered or a category is pointed at a new account. Comparing only the accounts inventory reached this period is how a report announces that the books agree while a whole balance sits unexplained beside it.

Two buttons on every line open what is behind each figure:

  • the valuation layers that were added up to make the stock column, filtered by the same dates and the same accounting-date rule;

  • the journal items that were added up to make the ledger column, posted only, on or before the closing date.

The filters are the report's own, not an approximation of them, so the two lists always add up to the two numbers they came from. A difference becomes something to click on rather than something to go hunting for.

What does not fit is named, not hidden

Two kinds of value cannot belong to a reconciled line, and both are reported as their own explanation rather than quietly dropped:

  • Manually valued stock. A product category on manual (periodic) valuation still produces valuation layers, and Odoo posts no journal entries for them — core actually clears the valuation account of any category that is not automated. There is nothing to reconcile that value against, so it is reported as its own total and left out of the comparison. It is a supported way to run Odoo, not a fault, and this report does not tell you to change your accounting policy to suit it.

  • Value with no account. A category set to automated valuation that names no valuation account still produces layers, and that value belongs to no account at all. The fix is the category.

  • Value with no posted entry. A layer whose journal entry is still in draft — or was never created — counts as stock and counts for nobody in the ledger. Whenever there is a difference, this is usually most of it.

Dropping either would make the report balance while the stock did not, which is worse than having no report.

Filtering, and what a filtered run may claim

You can narrow the run to a few product categories. That narrows the stock side only — the ledger side is whole accounts, and there is no honest way to take a slice of an account balance by product category.

So a filtered run is a movement report, not a reconciliation, and it says so: the difference column is left out rather than filled with the difference between part of the inventory and all of an account.

Who can run it

The module carries its own group, Stock Valuation History, in the Accounting category, and it implies Odoo's read-only accounting group.

This is deliberate. account.group_account_readonly lives in Odoo's hidden module category, so on a Community database running Invoicing only it cannot be ticked for anybody. Had the menu been anchored to it, the only way an administrator could let an accountant read this report would be to grant them write access to something else. Now it is a tick-box, and the report writes nothing anywhere.

Series

Community 16.0, 17.0 and 18.0.

There is no 19.0 version, on purpose. Odoo 18.0 added this to Community itself — stock_value() next to stock_accounting_value(), grouped by account, with a date — and shipping a paid module that duplicates it would mean selling something given away, and disagreeing with the core report the first time the two rounded differently. On 19.0 the right answer is the one already in the box.

Screens

Backdated - bambooforge_valuation_history
Backdated
Drill - bambooforge_valuation_history
Drill
Movement - bambooforge_valuation_history
Movement
Reconcile - bambooforge_valuation_history
Reconcile