Odoo Help


Why are Packs (aka logistical units aka stock.tracking) not managed as virtual locations?

on 4/25/13, 6:14 PM 2,468 views

First, a primer in stock.move:

OpenERP current inventory is maintained indirectly by the use of stock.move objects. Each stock.move object has a location_dest_id, which represents where it currently is (or will be), and a location_id which represents where it came from (or will come from).

An addition to stock is represented by a stock.move object whose location_dest_id is the location one is summing the stock for and whose location_id is not, while a reduction in stock is the reverse. The product_qty field indicates how much product is transferred, and will never be negative or zero. To represent a loss of stock, simply swap the location_id and location_dest_id fields.

The state of a stock.move indicates how it contributes to the current stock levels. A stock.move in the done state counts as real and virtual stock while a stock.move in the confirmed, waiting, assigned states counts as virtual stock. In any other state (e.g. draft), the stock move does not contribute to stock levels.


stock.tracking, perhaps better known as 'Packs', 'Logistical Units', and 'Traceability', is a rather strange entity. The documentation for it seems to indicate that it could be used to group a bunch of (physical) products to ship to a customer. However, there are several things that contribute to the current implementation being somewhat... inadequate.

First, the design of stock.tracking can be surmised pretty simply from it's definition in addons/stock/stock.py:

class stock_tracking(osv.osv):
    _name = "stock.tracking"
    _description = "Packs"    
    #...some omitted functions...
    _columns = {
        'name': fields.char('Pack Reference', size=64, required=True, select=True, help="By default, the pack reference is generated following the sscc standard. (Serial number + 1 check digit)"),
        'active': fields.boolean('Active', help="By unchecking the active field, you may hide a pack without deleting it."),
        'serial': fields.char('Additional Reference', size=64, select=True, help="Other reference or serial number"),
        'move_ids': fields.one2many('stock.move', 'tracking_id', 'Moves for this pack', readonly=True),
        'date': fields.datetime('Creation Date', required=True),
    _defaults = {
        'active': 1,
        'name': make_sscc,
        'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
    #...some omitted functions...

It's counterpart is a field on the stock.move object:

'tracking_id': fields.many2one('stock.tracking', 'Pack', select=True, states={'done': [('readonly', True)]}, help="Logistical shipping unit: pallet, box, pack ..."),

So I now have a very obvious question: how can I tell where a Pack is at a given time?

Say I have 5 products of one type and 5 of another. I want to place these in a single box, and then put that box on a truck, send it to another warehouse, and have them take those products, unbox them, and use them for other things. I would imagine that I would create (indirectly, of course) a few objects:

  1. stock.move from 'my stock' to 'truck' of '5' 'Product A' in the 'assigned' state.
  2. stock.move from 'my stock' to 'truck' of '5' 'Product B' in the 'assigned' state.
  3. stock.move from 'truck' to 'warehouse_b/incoming' of '5' 'Product A' in the 'waiting' state.
  4. stock.move from 'truck' to 'warehouse_b/incoming' of '5' 'Product B' in the 'waiting' state.

Now, what do I do about packs? I could create a new pack ('pack_a'), assign some move_ids (indirectly, via the stock_move.tracking_id fields of the first two stock moves). Then I would... ? Move the box? Complete (action_done()) the first two stock moves?

How would I know to complete the first two stock moves at all? Would these be listed as "internal moves"? Is this where I should have assigned the packs from?

Even once I did that, how could I tell where the box is? Suppose I pack my goods in location A. At this point, the goods are in a box. I then physically move that box around my warehouse. How can I answer the question "where is the box?"? How can I track it's current location?

On that note, how is it that I can move things within the box (complete some stock_move()s with action_done() and not others?


With this in mind, wouldn't it make more sense to represent the physical box with a stock.location rather than this haphazard system? Since after all, isn't a box literally that? And what if I wanted to put a box into a larger box and track both of them?

I feel that none of these concerns are adequately addressed. If someone is already using Packs in a system, please let me know how you are handling it.


If I were to redesign packs, i'd design them as follows:

  1. stock.location.usage gains a new option, perhaps pack.
  2. The default location structure gains a new stock.location entry called "Packs", whose parent location_id is "Virtual Locations"
  3. To create a new pack, one creates a new stock.location whose usage is pack.
  4. To put an assigned stock.move into a "Pack", one creates a new stock.move whose location_id is the previous stock_move's location_dest_id, whose location_dest_id is the pack's id, and whose move_dest_id is the previous move's move_dest_id. One would then simply complete the original move.

You can then track a pack's current location (just change the pack's parent), nest packs in packs (nest one pack in another), and so on, using the existing stock.location scheme.

What concerns would someone have when implementing this system?

Why is the existing packs system not doing exactly this?

About This Community

This platform is for beginners and experts willing to share their Odoo knowledge. It's not a forum to discuss ideas, but a knowledge base of questions and their answers.


Odoo Training Center

Access to our E-learning platform and experience all Odoo Apps through learning videos, exercises and Quizz.

Test it now

Question tools

1 follower(s)


Asked: 4/25/13, 6:14 PM
Seen: 2468 times
Last updated: 6/17/15, 4:52 AM