Skip to main content
Odoo Community Association

Stock Secondary Unit

Production/Stable License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runboat

This module lets a product be tracked, throughout the whole stock flow, in a secondary unit of measure alongside its normal one - typically a count (pieces, boxes) next to a product that is actually measured by weight or volume. A classic case: fish sold by weight, but an order is placed for “40 pieces” because each of a fixed number of guests gets exactly one - the real weight per piece varies, but the piece count must travel through the whole warehouse route exactly as ordered.

It builds on product_secondary_unit’s per-product configuration (the conversion factor, and the dependency_type that decides how the two quantities relate to each other) and extends it to stock.move and stock.move.line, so a secondary quantity can be demanded on a transfer, counted on its operations, and shown on delivery documents - not just converted once on the product form.

Two independent secondary-unit settings live on the product:

  • secondary_uom_ids / the sale-facing secondary unit (from product_secondary_unit) - the one that ends up on stock.move / stock.move.line records for an actual transfer.

  • stock_secondary_uom_id (“Second unit for inventory”, added by this module) - purely for the on-hand quantity display: pick one of the product’s secondary units and a “Secondary Unit” smart button appears on the product form next to “On Hand”, showing the current stock quantity converted into it. This is read-only, informational, and independent of whatever secondary unit an individual move ends up using.

Why ``dependency_type`` matters here in particular: a stock move’s product_uom_qty (demand) and a move line’s quantity (what was actually done) are two different fields with different needs:

  • dependent: the secondary quantity is always a factor conversion of the primary one, in both directions - fine for informational units where nothing needs to survive an inexact conversion.

  • independent: the secondary quantity is entered on its own and never recomputed from the primary one, and vice versa - used when the two units don’t relate to each other at all.

  • secondary_priority: the primary quantity is estimated from the secondary one (like dependent), but the secondary one is never recomputed back from the primary (like independent) - this is the one built for the fish-by-weight/pieces-by-count case: pieces are the real order, weight is only ever a convenience estimate derived from them, and must never silently drift because of how much something happened to weigh.

For independent and secondary_priority (together, the “count-preserving” types), the module makes sure the secondary quantity survives every stock operation exactly, instead of being silently re-derived from the primary quantity and losing precision or meaning:

  • Splitting a move for a backorder carries over exactly what’s left of the secondary demand (e.g. 40 pieces demanded, 30 actually picked -> the backorder is for exactly 10 pieces, never a weight-based guess), and keeps the original move’s own secondary quantity in sync with what was actually processed.

  • Merging moves back together (e.g. two operations for the same product ending up as one line, or a backorder’s pushed leg merging back into an already-existing move) sums the secondary quantities the same way core sums the primary one - core has no notion of this field at all, so without this the count would silently be lost on every merge.

  • A negative move (e.g. a decreased sale order line) absorbed into - or absorbing - an existing one rebalances the secondary quantity the same way core rebalances the primary one for that specific code path, which is separate from the ordinary merge above. When the negative move is bigger than the pending demand, the remaining return gets a positive secondary quantity, as core only flips the primary one.

  • A pull-chain procurement (e.g. a 2-step reception, or an internal make-to-order replenishment) carries the secondary unit forward into the next move it creates, the same way the primary quantity already does.

  • ``secondary_uom_qty_done``, a new aggregate field on stock.move, mirrors what core’s own quantity field does for the primary unit: it sums what was actually counted across the move’s lines, so other code needing “how many pieces were actually done” doesn’t have to re-derive that sum ad hoc every time.

Table of contents

Usage

Displaying stock on hand in a secondary unit

  1. On a product, go to Inventory tab > Secondary unit and choose which of the product’s configured secondary units should be used for the on-hand display (“Second unit for inventory”).

  2. A “Secondary Unit” smart button appears on the product form (next to “On Hand”), showing the current stock quantity converted into that unit. This is purely informational: it does not affect any transfer.

Demanding and counting a secondary quantity on a transfer

  1. On a transfer’s operations (the move lines list, or a move’s own detail form when Detailed Operations is enabled), the Secondary Qty and its unit are editable columns/fields (needs the “Units of Measure” feature enabled, uom.group_uom).

  2. What happens when the secondary quantity is entered depends on the product’s secondary unit dependency_type:

    • dependent: entering either quantity recomputes the other through the conversion factor.

    • independent / secondary_priority: the secondary quantity is entered on its own; for secondary_priority the primary quantity is estimated from it, but never the other way round once a secondary quantity has actually been set.

Validating a transfer that doesn’t match the secondary demand exactly

For a count-preserving secondary unit (independent or secondary_priority), if what was actually counted is less than demanded and a backorder gets created:

  • The backorder is created for exactly what’s left of the secondary demand (e.g. 40 pieces demanded, 30 counted -> the backorder demands exactly 10 pieces) - never a proportional estimate derived from whatever the primary quantity (e.g. weight) happened to be.

  • The original, now-done transfer keeps its secondary quantity in sync with what was actually counted, not the original demand.

If instead more was counted than demanded, the move’s own demand is left untouched by this module alone; reconciling that difference is out of scope for this module.

Decreasing the demand of a confirmed transfer

When the quantity of a confirmed sale order line is decreased, core creates a move with a negative quantity and merges it into the pending one. For a count-preserving secondary unit, the surviving move’s secondary quantity reflects the net effect of the decrease, not just whatever it already had before. If the decrease is bigger than the pending demand, the remaining quantity becomes a return whose secondary quantity is positive too.

Chained transfers (multi-step routes, make-to-order replenishment)

When a transfer is part of a pull chain (e.g. the “Input -> Stock” leg of a 2-step reception, or an internal make-to-order move), whatever secondary unit and quantity is set on it is carried forward into the next transfer created for that chain - the destination move doesn’t start without one.

Delivery documents

The delivery slip report shows the secondary quantity and unit next to the primary one for every line, both for a picking still being processed and once it is done.

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

Credits

Authors

  • Tecnativa

Contributors

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

This module is part of the OCA/stock-logistics-warehouse project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

Metadata

Release files for odoo-addon-stock-secondary-unit 18.0.1.2.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for odoo-addon-stock-secondary-unit 18.0.1.2.1.1
File Interpreter ABI Platform
odoo_addon_stock_secondary_unit-18.0.1.2.1.1-py3-none-any.whl Python 3 none any Details

Release files / odoo_addon_stock_secondary_unit-18.0.1.2.1.1-py3-none-any.whl

Download URL odoo_addon_stock_secondary_unit-18.0.1.2.1.1-py3-none-any.whl
Size 62.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab4aefd7edf5d2c249d249f11c978cf1fd658210194a570d8581b7ff122afc19
BLAKE2b-256 checksum
How to use checksums
20a4f19693a09083c0e9a05404cc35ff982fb921f640ea66c289dfe1370eaddc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

18.0.1.2.1.1 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page