Skip to content
OdooWebApps.com

Odoo stock quantities: on hand, forecasted, free, and why they disagree

By Tal Oz, OdooWebApps. Published 23 September 2026.

Three fields that all look like the stock level

Ask Odoo how much of a product you have and there is no single answer, because Odoo tracks several quantities that are all legitimate and all different. Reading the wrong one is the most common reason a dashboard or a portal disagrees with the warehouse. These are the field descriptions from Odoo's own source:

What each quantity field actually measuresScroll sideways to see every column
FieldWhat Odoo computesWhat it answers
qty_availableCurrent quantity of products, physically in stockWhat is on the shelf right now
free_qtyQuantity on hand minus reserved quantityWhat is on the shelf and not already promised to someone
virtual_availableQuantity on hand, minus outgoing, plus incomingWhat you expect to have once planned moves complete
incoming_qtyQuantity of planned incoming productsWhat is on its way in
outgoing_qtyQuantity of planned outgoing productsWhat is committed to go out

The one people reach for first is usually the one they want least. qty_available is a physical count, so it happily reports stock that is entirely reserved for orders shipping this afternoon. A screen built on it will cheerfully show items as available that nobody can actually sell.

So which one should the app show?

It depends on the question the person in front of the screen is asking, which is usually one of three:

  • Can I sell this right now? Free quantity. It is on hand minus what is already reserved, which is the closest thing Odoo has to "genuinely available". This is almost always the right field for a customer-facing portal.
  • Will I be able to fulfil this? Forecasted. It accounts for incoming purchases and manufacturing as well as outgoing commitments, which is what a planner or a buyer needs.
  • What is physically in the building? On hand. This is the right answer for a stock count or a picker standing in front of a shelf, and the wrong answer for nearly everything else.

Showing forecasted quantity to a customer is a particularly expensive mistake, because it can exceed what exists. A portal that promises stock arriving next Tuesday, without saying so, sells something the warehouse cannot ship today.

Why the same field returns a different number for different callers

This is the part that turns a reporting discrepancy into a long afternoon. These quantities are not stored values on the product. They are computed per request, and the computation reads the context of the call. In Odoo 19 the fields depend on, among others, the lot, the owner, the package, a date range, the location, the warehouse and the set of allowed companies.

The consequence is worth stating plainly: the stock level is not a property of the product. It is a property of the product as seen from somewhere. Two correct calls for the same product can return different numbers and neither is a bug. If a report and a screen disagree, compare the context each one passed before you go looking at the data.

It also means a number can be scoped deliberately. Asking for a single warehouse, or a location and its children, gives that warehouse's figure rather than a company-wide total, which is usually what a picking screen or a branch dashboard should show.

The multi-company trap

The allowed companies are part of that context, and this catches people in production rather than in testing. On a database with several companies, a quantity read by a user who can see every company is the total across all of them. The same read by a user scoped to one company returns that company's figure.

Nothing errors. The integration returns a larger number than the warehouse recognises, and because it is plausible rather than absurd, it can survive a long time before anyone questions it. The general form of this problem, where a call quietly returns records from every company a user can reach, has its own guide; stock quantities are simply its most expensive instance, because the wrong number here becomes a promise to a customer.

If an integration user was given broad company access to make something work, every quantity it reads is now a cross-company total. That is worth checking before anything else.

When the number is right and the shelf still disagrees

Once the field and the context are correct, a remaining gap is usually not a software problem:

  • Reserved stock is still physically present. On hand includes it, free quantity does not. A picker sees boxes that the portal has correctly stopped offering.
  • Moves that were done in the building, not in Odoo. A transfer completed on paper and confirmed later is real stock that Odoo does not know has moved yet.
  • Lots and locations narrow the answer. A quantity read in the context of one lot or one location is answering a narrower question than the one being asked at the shelf.

That last category is exactly why we build picking apps that write back as the work happens rather than after it. When the record is made at the shelf, the gap between Odoo and the building stops accumulating in the first place, which is the point of a warehouse app rather than a paper round followed by data entry.

A short checklist when a quantity looks wrong

  1. Check which field is being read. On hand, free and forecasted answer three different questions and only one of them is yours.
  2. Check the companies in context. A cross-company total is the most common cause of a number that is too high.
  3. Check the warehouse or location scope. No scope means every location the caller can see.
  4. Compare against the same product in the Odoo interface as the same user, not as an administrator. Different users can correctly see different numbers.

If you are reconciling stock figures between Odoo and something else and would rather have it built correctly than keep investigating, tell us what the screen is meant to answer. Which field it should read usually follows directly from that.

Want this looked at for your setup?

Every Odoo instance is configured a little differently. Tell us what you're working with and we'll give you a straight answer.