Physical stock, virtual stock and inventory in Dolibarr
What moves physical and virtual stock in Dolibarr 24, six tested reasons why the virtual stock looks wrong, and the built-in inventory step by step, including the closing button bug in 24.0.2.
Dolibarr shows two figures on the Stock tab of a product, and the second one raises the most questions:
- Physical Stock: what is in your warehouses. It is stored in the database and only changes through stock movements (automatic or manual);
- Virtual stock: what will be left once the orders in progress are shipped and received. It is stored nowhere: Dolibarr recalculates it on every display from the orders, shipments and receptions in progress.
A virtual stock that “does not update” is therefore never a late calculation: there is nothing to recalculate. When it looks wrong, a document in progress is being counted, or is not. This guide shows which ones, with figures taken on 14 October 2026 on Dolibarr 24.0.2.
First: when does the stock move?
Everything is set in Home › Setup › Modules/Applications, Stocks card, gear icon. The page offers two lists: one decrease rule (goods out) and one increase rule (goods in). You pick only one per list: ticking one unticks the others.
| Out: decrease real stocks… | In: increase real stocks… |
|---|---|
| on validation of customer invoice/credit note | on validation of vendor invoice/credit note |
| on validation of sales order | on purchase order approval |
| on shipping validation | on validation of reception |
| when shipping is set to closed | when reception is set to closed |
On a fresh installation, no rule is ticked. Nothing moves by itself: the stock only changes through Correct stock or Transfer stock on the product, or through an inventory. And the virtual stock stays equal to the physical stock: in our test, a validated order for 3 pens left the virtual stock at 10 out of 10. This is the first thing to check when “the virtual stock does not move”.
If you use shipments, the on shipping validation rule is the easiest to read: physical stock goes down when the parcel leaves, and virtual stock anticipates the orders to deliver. With the Product Lots module, Dolibarr greys out the invoice and order rules anyway: only the shipment and reception rules remain.
The page also warns that a point of sale module may ignore these rules and decrease stock its own way: check its own setup if you use one.
What the virtual stock adds up
The calculation depends on the chosen rule. What we checked:
Rule “on shipping validation” (or “when shipping is set to closed”): virtual stock = physical stock − (quantities of validated or in process sales orders − what has already been shipped from them).
| Step (starting stock: 10) | Physical | Virtual |
|---|---|---|
| Order for 3 validated | 10 | 7 |
| Shipment of 2 created, draft | 10 | 7 |
| Shipment of 2 validated | 8 | 7 |
| Order classified delivered (the last pen will not ship) | 8 | 8 |
Draft orders do not count, nor do cancelled ones: a validated order for 4 dropped the virtual stock to 4, cancelling it brought it back to 8.
With the rule “when shipping is set to closed”, a validated but not closed shipment does not change the physical stock; the shipment’s Close button does. In our test, closing a shipment that covered the whole order also classified the order as delivered.
Rule “on validation of sales order”: physical stock goes down at validation (the confirmation box asks for the warehouse), and virtual stock equals physical stock. Shipments no longer touch the stock.
Purchasing side (rule “on validation of reception”): virtual stock adds the quantities of purchase orders that have been ordered (Make order button) or partially received, minus what has already been received from them.
| Step (starting stock: 20) | Physical | Virtual |
|---|---|---|
| Purchase order for 10 validated and approved | 20 | 20 |
| Make order | 20 | 30 |
| Reception of 4 validated | 24 | 30 |
| Receptions of the rest validated | 30 | 30 |
Ten-second diagnosis: the tooltip
On the product’s Stock tab, hover the small “i” to the right of the virtual stock figure. Dolibarr shows Explanation for difference between physical and virtual stock: quantities in open sales orders (and in drafts), already sent, in open purchase orders (and in draft or approved orders not yet ordered), already received. Almost every case below can be read there.
Six reasons why the virtual stock looks wrong
Each one was reproduced on our test instance.
1. No rule is active
See above: without a rule, the virtual stock ignores orders. Tick one decrease rule and one increase rule.
2. A validated order was never completed
An order shipped in part, whose remainder will never ship, keeps “reserving” that remainder: the virtual stock stays below the physical stock, forever. Same for a validated and forgotten order. Open the order and click Classify delivered (or Cancel order if nothing shipped): the virtual stock comes back at once.
3. The purchase order is approved but not “ordered”
A validated and approved purchase order does not count in the virtual stock. It only counts after Make order. In the tooltip, it shows in brackets: “draft or approved orders, not yet ordered”.
4. A draft reception lowers the virtual stock
This is the “wrong virtual stock after a reception” case. On a purchase order for 10, already received for 4, we created a second reception of 3 without validating it:
| Physical | Virtual | |
|---|---|---|
| After the 1st reception (4) validated | 24 | 30 |
| 2nd reception (3) created, draft | 24 | 27 |
| 2nd reception validated | 27 | 30 |
The tooltip said “already received: 7” while only 4 had entered the stock. The reason, in the code: for an order already partially received, Dolibarr counts every reception line, drafts included. On the very first reception (order not yet partially received), the draft changed nothing. Fix: validate the reception, or delete the draft.
5. The rule was changed while orders were in progress
With the rule “on validation of sales order”, a validated order for 2 took the stock from 5 to 3. We then switched to the rule “on shipping validation”: the virtual stock dropped to 1, then shipping that same order removed 2 more. Result: 2 pens taken out twice, once at order validation, once at shipment (both movements are in the product’s movement list).
Change the rule when no order is in progress, or correct the stock afterwards. An inventory does it cleanly (see below: that is what we did).
6. Kits
With kits enabled (setup of the Products module: “Enable Kits (set of several products)”), we created a kit containing 2 boxes of staples, then validated an order for one kit. The virtual stock of the staples did not move (10 out of 10): by default, kit orders do not count for their components.
An option without a checkbox changes this: in Setup › Other Setup, add the constant
PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC with the value 1. The virtual stock of the staples then
went to 8. According to the code, this option also adds the kit’s documents to the component’s
statistics (Related items tab).
When the kit ships, the components leave the stock (staples: 108); the kit itself has no stock of its own, and its virtual stock can show as negative.
The built-in inventory, step by step
Menu Products › Stocks › Inventories. The Stocks module is enough, there is nothing to install.
- New inventory: a Ref., a Label, the Warehouse (or a single product, or products by category), then Create.
- Validate (Start): Dolibarr fills the list with the products that already have stock recorded in the warehouse. The Expected Qty column is the current physical stock.
- Enter the counted Real Qty, then Save. This button only saves the quantities: no stock
movement is created at this stage.
- empty field: the product is left alone (in our test, the notebook left empty kept its stock);
- 0: the stock is reset to zero;
- Fill real quantity with expected quantity copies the expected quantity everywhere: handy, but a forgotten product is then confirmed as is.
- Generate movements and close, then Yes. Dolibarr creates one movement per difference,
labelled “Inventory INV-2026-10” (code
INV-followed by the reference), and the inventory becomes Closed.
Entering opening stock
A product that has never had stock in the warehouse does not appear at step 2. Add it with the top line of the table: warehouse, product, quantity, Add. That is how we entered 15 binders: at closing, a +15 movement. For a single product, Correct stock on its Stock tab does the same.
The same inventory fixed the error from reason 5: Dolibarr thought it had 1 pen, we counted 3, the +2 movement put the stock right.
The trap: sales during the inventory
At closing, Dolibarr does not replay the difference found when counting: it sets the stock to the counted quantity. The movement is “real quantity − stock at closing time”.
In our test, 8 boxes of staples were counted and saved, then one box was sold and shipped before closing (stock: 7). At closing, Dolibarr created a +1 movement and the stock went back to 8: the box sold reappeared. The help on the Expected Qty column says so: making stock movements during the inventory is not recommended.
Count, enter and close without shipping or receiving in between, or inventory one warehouse or one category at a time so each inventory stays short.
In 24.0.2, the closing button shows an error
In Dolibarr 24.0.2, clicking Generate movements and close does save the quantities entered, then shows a blank page with this message:
Access to this page this way (POST method or GET with a sensible value for 'action' parameter) is refused by CSRF protection in main.inc.php. Token not provided.
The inventory is not closed and no movement is created. The cause: the redirection made by this button does not send the security token, which Dolibarr requires by default since version 24.0. The fix has been in Dolibarr’s 24.0 branch since 10/10/2026, but not in 24.0.2, the latest published version on 14/10/2026.
The workaround we tested:
- Save the quantities;
- open the Generate movements and close button in a new tab (middle click, or right click › open in new tab): the link itself does contain the token;
- in that new tab, confirm with Yes.
The inventory is then closed and the movements created normally. Do not change
$dolibarr_nocsrfcheck or the protection level to get around the error.
With lots and expiry dates
If you track lot or serial numbers (Product Lots module, with sell-by and eat-by dates), the inventory is done lot by lot: a Lot/Serial column is added to the table.
In short
- “Frozen” virtual stock: check that a stock rule is ticked.
- Virtual stock too low: a validated order never completed, or a draft reception.
- Virtual stock too high: a purchase order placed but never fully received.
- Kit components: constant
PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC. - Wrong physical stock: a rule changed along the way, or sales during an inventory. A properly closed inventory puts it right.
Before changing the stock rule or starting a large inventory, make a backup: Back up and restore Dolibarr.
If your stock still makes no sense, the €90 diagnosis is about looking at your instance with you for one hour, then giving you in writing what skews the calculation and what putting it right would cost.