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:
stock.move
from 'my stock' to 'truck' of '5' 'Product A' in the 'assigned' state.stock.move
from 'my stock' to 'truck' of '5' 'Product B' in the 'assigned' state.stock.move
from 'truck' to 'warehouse_b/incoming' of '5' 'Product A' in the 'waiting' state.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:
stock.location.usage
gains a new option, perhapspack
.- The default location structure gains a new
stock.location
entry called "Packs", whose parentlocation_id
is "Virtual Locations" - To create a new pack, one creates a new
stock.location
whoseusage
ispack
. - To put an
assigned
stock.move
into a "Pack", one creates a newstock.move
whoselocation_id
is the previousstock_move
'slocation_dest_id
, whoselocation_dest_id
is the pack'sid
, and whosemove_dest_id
is the previous move'smove_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?