Test Report — 18.0.1.0.0
What is tested in Dead Stock & Inventory Ageing, and how to run the suite.
Result: 54 automated tests, 0 failures, 0 errors. Exit code 0.
Every figure here comes from an actual run. Nothing is asserted without having been executed.
Environment
| Odoo | 18.0 (community, source checkout) |
| Python | 3.12.3 |
| PostgreSQL | 16.15 |
| OS | Ubuntu 24.04.1 LTS |
| Database | created fresh for the run |
| Command | odoo-bin -d <db> -i stock_dead_ageing_report --test-enable --test-tags /stock_dead_ageing_report --stop-after-init |
The database is dropped and recreated before each run, so a result never depends on what a previous run left behind.
Result
Tests started : 54
Failures : 0
Errors : 0
Odoo exit code: 0Coverage by area
| Suite | Tests | Covers |
|---|---|---|
| TestReportView | 13 | The SQL view: quantity, cost, value, currency and warehouse resolution, days in stock, last-shipment logic, exclusions, read_group aggregation |
| TestAgeingBuckets | 13 | Every bucket and both edges of every boundary, including day 0, 60/61, 90/91, 180/181, a year and beyond |
| TestWizard | 11 | Each filter, the defaults taken from settings, refusal of nonsense input, and that the action it returns opens real views |
| TestDigest | 10 | The weekly email: when it sends, when it stays quiet, who receives it, and that its numbers match the report |
| TestSecurity | 7 | Read-only enforcement, group gates, multi-company isolation |
| Total | 54 |
What the tests actually prove
The bucket edges are right. Off-by-one errors in ageing bands are the classic defect in a report like this, and they are invisible — a product simply appears in the wrong column. TestAgeingBuckets asserts both sides of every boundary individually rather than sampling the middle of each band, and test_no_row_is_unlabelled asserts that no age falls through the classification entirely.
Ageing and deadness are different things. test_ageing_and_deadness_are_independent asserts that stock which arrived long ago but ships regularly is not reported as dead, and that recently-arrived stock that has never moved is. Conflating the two is what makes most dead-stock reports useless.
Value is computed per company. test_cost_is_read_per_company and test_currency_follows_the_company assert that cost is read in the company that owns the stock and presented in that company's currency, not the reader's.
The report cannot be written to. test_the_report_is_read_only, test_cannot_be_created and test_cannot_be_deleted assert that the view rejects create, write and unlink — including from a stock manager. A report that can be edited is not a report.
The digest stays quiet when it should. Three separate tests assert that a disabled digest, one with no recipients, and one with nothing dead to report all send nothing at all. A weekly email that arrives empty trains people to ignore it.
Its numbers are the report's numbers. test_digest_totals_match_the_report recomputes the totals from the report itself and asserts the email agrees. A summary that disagrees with the screen is worse than no summary.
Known gaps in coverage
Stated rather than glossed over:
- No browser (tour) tests. The pivot and list views are covered on the server side — the action, its view references, and the aggregation behind them — but there is no automated click-through of the rendered page. It was verified manually.
- Scale. The suite works with tens of products, not the hundreds of thousands a large warehouse holds. The view's cost is dominated by the stock.quant and stock.move.line indexes Odoo already maintains.
- Odoo 19. Not tested; this release targets 18.0 only.
Reproducing
createdb -O odoo ds_test
odoo-bin -c odoo.conf -d ds_test \
-i stock_dead_ageing_report --test-enable \
--test-tags /stock_dead_ageing_report --stop-after-init
echo "exit code: $?"A clean run prints no FAIL: or ERROR: Test lines and exits 0.