Product cost price avco sync
Odoo works out the average cost of a product from its stock valuation layers, and never restates one once it is written: a correction is always booked as a new layer, dated the day it is made, and everything that was valued in between keeps the figures it was given. That is a sound rule when the stock valuation is posted movement by movement, and it is exactly what falls short when it is not.
This module restates the layer instead. When the cost or the quantity of an already validated valuation layer is corrected, the layer is rewritten as if it had always held the corrected figures, and the whole chain is replayed from there on. That is what it buys:
- A mistake made on a receipt can really be undone. Somebody types the price of a purchase wrong, or enters the quantity in the wrong unit, and it is only noticed days later. Correcting the receipt is enough: the layer, the cost of the product and everything valued afterwards line up with what actually happened.
- The outgoing moves valued in between are re-priced. The units that left stock while the mistake was in place had been valued with the wrong average. They are given the cost they should have had, so the cost of goods sold stops carrying the error, and anything that reads the cost of an outgoing layer follows along. The margin sale_margin_sync writes on the sale order line is one such reader.
- A stock valuation asked for a past date comes out corrected. The valuation report adds up the current value of the layers created up to the requested date, so restating them is what makes a valuation as of last month right once a mistake made last month is fixed this one. Where the stock valuation is posted periodically rather than movement by movement, this is what allows the period to be closed with corrected figures instead of dragging the error into the next one.
- The cost of an oversold product or lot doesn't run away. With no units left to average against, Odoo still weighs the incoming cost against the negative quantity, and dividing by it returns a price that is not an average of anything and that can even be negative, which every outgoing move then copies. Here the cost of the receipt becomes the new average, which is also the cost Odoo's own negative stock vacuum uses to settle the deficit once enough real stock arrives, so both ends agree and the cost is always a price that was really paid.
- Products valuated by lot are supported. Each lot is replayed as its own chain, so a correction only re-prices what left that very lot and the other lots keep their own cost. Products valuated as a whole and products valuated by lot can coexist, and a product can be switched from one to the other at any time.
auto_awesome What this module does
AI-generated from the code · Odoo 18.0Corrects the average cost of products and lots when a validated stock move is edited, by rewriting the original valuation layer and replaying the whole valuation chain. Adds a server action to force this recalculation from selected valuation layers.
This module changes how Odoo handles corrections to already validated stock moves for products valued at average cost (AVCO). Normally, when you fix a receipt's quantity or price, Odoo creates a new valuation layer dated today, leaving past reports and intermediate outgoing costs wrong. Here, the original layer is rewritten as if the move had always had the corrected figures, and every later layer in the same valuation chain is recalculated.
It also protects the cost price when stock is oversold (negative on hand). Receipts on negative stock no longer produce a broken average; the incoming cost becomes the new average. Negative stock revaluations from Odoo's vacuum are not spread over remaining stock, avoiding negative or absurd unit costs.
For products valued by lot, each lot keeps its own cost chain, and the product's cost is derived from its layers. A server action named "Recompute average cost from here" is available on stock valuation layers (list view) for stock managers, to manually replay a chain when needed.
The module touches product.product, product.template, stock.lot, stock.move, stock.move.line, stock.picking and stock.valuation.layer. It also adjusts remaining quantities and values used by the negative stock vacuum and invoice price differences.
Depends on
Required by other modules
Models and fields (8)
- stock.lot · new model
- stock.move · extends an existing model
- stock.picking · extends an existing model
- product.product · new model
- stock.move.line · extends an existing model
- product.template · extends an existing model
- stock.valuation.layer · extends an existing model
- product.cost.price.avco.sync.mixin · new model
Text generated by AI (DeepSeek) from reading the Odoo 18.0 code on GitHub; it may contain mistakes, so check the code before deciding. View the code on GitHub
Availability by version
Reviewed: 2026-11-03Confirmation status: 7 confirmed, 2 with no information of 9 versions.
Sources · Odoo 18.0
Confirmed on 2026-11-03 in branch 18.0 of OCA/stock-logistics-workflow.
terminal Installation and usage
OCA documentation · Odoo 18.0Configuration
Source · Odoo 18.0The module acts on the products whose category uses the Average Cost (AVCO) costing method. Products costed at standard price or FIFO are left untouched.
Not compatible with automated inventory valuation
Use it only on product categories whose inventory valuation is manual.
With automated valuation every valuation layer has a journal entry posted along with it. This module rewrites the layer when a past mistake is corrected, and it does not touch that entry, which was posted for the figures the layer used to hold and may well sit in a period that is already closed. Stock valuation and accounting would drift apart, silently, by the amount of every correction.
The whole point of restating the past is to reach companies that post the stock valuation periodically, from the valuation report, rather than movement by movement. Those that post it automatically already have each correction booked on the day it was made, which is what Odoo does on its own and what their accounting expects.
Valuation by lot/serial number
Both models are supported and can coexist in the same database, so ticking or unticking Valuation by Lot/Serial number on a product needs no special care from this module's point of view: the layers it already had keep replaying as the chain they were, and the new ones replay per lot.
Bear in mind that enabling it is Odoo's own operation, not this module's: it empties the stock out and puts it back, giving every existing lot the current average cost of the product, because the cost each lot was really bought at is not recorded anywhere before that point. It also refuses to run while any lot has a negative quantity in a valued location.
Landed costs and vendor bill price differences
Both add value to a receipt layer without touching its cost, and both read the remaining quantity to know how much of it is still on hand. They are kept in step: correcting the cost or the quantity of a layer adjusts its remaining value by the difference, so what a landed cost or a bill added to it survives the correction and later corrections are priced right.
Correcting a price on the vendor bill is covered by purchase_stock_price_unit_sync, which applies it to the whole receipt instead of only to what is still on hand, so that route ends up equivalent to correcting the receipt or the layer.
Usage
Source · Odoo 18.0Correcting a mistake
There are four ways of correcting a purchase, and all of them end up in the same place: the layer is restated as if it had always held the corrected figures, the outgoing moves valued in between are re-priced, and the cost of the product, or of the lot, follows.
| What you correct | Where | What it takes | |---|---|---| | The cost of a receipt | The unit_cost of its valuation layer | This module | | The quantity of a done move, incoming or outgoing | The quantity of the move line | This module | | The price of a purchase order already received | The order line | purchase_stock_price_unit_sync | | The price on the vendor bill | The invoice line | purchase_stock_price_unit_sync |
Correcting the same price twice, on the order and then on the bill, does not count it twice: the second one finds the layer already worth what it says and does nothing.
Forcing it by hand
For the times a correction could not run on its own, such as layers written while this module was not installed, the Recompute average cost from here action is available from the Action menu of the stock valuation layers list. Select one or more layers and the chain is replayed from each of them.
Only the oldest layer of each chain in the selection is used, because replaying corrects everything that comes after the starting point and the rest would redo the same work. On a product valuated by lot each lot is a chain of its own, so the oldest layer of each lot is kept.
Replaying a long chain rewrites every layer after the starting point, in a single transaction. On a product with hundreds of thousands of layers, start as late in the chain as the correction allows.
What happens with everything else
These flows need nothing from you and are not disturbed by a correction:
- Landed costs, applied before or after the correction, and split per lot. What they added to the layer is not purchase cost, so it survives untouched and the correction only moves the cost of the goods.
- The stock revaluation wizard. What it adds is spread over the units on hand, the same way a landed cost is, so the cost of the product keeps matching what its layers are worth.
- Negative stock. With no units left to average against, a receipt sets the cost to the price actually paid, and Odoo's own vacuum settles the outstanding deficit with that same price once enough real stock arrives. Correcting a receipt down below what had already been delivered records the shortfall as the deficit the vacuum looks for, so those units get re-priced too. That shortfall is recorded on the last outgoing move of the chain, which is where it would have fallen short in chronological order, rather than on whichever delivery took each particular unit. Reconstructing that would mean replaying the consumption backwards, and it would change nothing: an average cost is spread over the whole quantity, so what matters is that the deficit exists and is settled, not which delivery it is attached to.
- Inventory adjustments and returns are valued at the cost of the moment rather than averaged in, and a correction re-prices them along with the rest.
- Switching a product to or from valuation by lot, at any time. The layers it already had keep replaying as the single chain they were.
What is left to Odoo
- Vendor bill refunds, which Odoo compensates against the original bill with a logic of its own.
- Layers created straight from code, outside a stock move and without running Odoo's negative stock vacuum, as a third party module could do. They do not trigger anything: use the server action above if a chain ever needs it.
- Products that are not average costed, and automated inventory valuation, which is not compatible at all. See the configuration notes.
No verified information in OCA: Installation.
Installation instructions, the distribution package, dependencies and configuration steps are not shown because they have not yet been verified in the module documentation for any version.
Data imported from GitHub: this sheet reflects the latest catalog review, not a live lookup. Always check the module documentation and staging before installing in production.