[MERGE] Merge from trunk-wms

bzr revid: jco@openerp.com-20140226161012-r6f96eh40rgw2g1h
This commit is contained in:
Josse Colpaert
2014-02-26 17:10:12 +01:00
12 changed files with 109 additions and 39 deletions
+1 -1
View File
@@ -560,7 +560,7 @@
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='lot']" position="inside">
<field name="track_production" groups="stock.group_tracking_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<field name="track_production" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</xpath>
</field>
</record>
+2 -2
View File
@@ -49,7 +49,7 @@
<field name="product_qty" class="oe_inline"/>
<field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(product_id, product_uom)" class="oe_inline"/>
</div>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_tracking_lot"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id,address_id)" attrs="{'required':[('invoice_method','!=','none')]}"/>
<field name="address_id" groups="sale.group_delivery_invoice_address"/>
</group>
@@ -96,7 +96,7 @@
<field name="type" on_change="onchange_operation_type(type,parent.guarantee_limit,parent.company_id,context)"/>
<field name="product_id" on_change="product_id_change(parent.pricelist_id,product_id,product_uom,product_uom_qty, parent.partner_id)"/>
<field name='name'/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_tracking_lot"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="location_dest_id" groups="stock.group_locations"/>
<field name="product_uom_qty" string="Quantity"/>
+4 -1
View File
@@ -67,7 +67,10 @@ This installs the module product_expiry."""),
'group_stock_packaging': fields.boolean("Allow to define several packaging methods on products",
implied_group='product.group_stock_packaging',
help="""Allows you to create and manage your packaging dimensions and types you want to be maintained in your system."""),
'group_stock_tracking_lot': fields.boolean("Track serial number on products",
'group_stock_production_lot': fields.boolean("Track lots or serial numbers",
implied_group='stock.group_production_lot',
help="""This allows you to assign a lot (or serial number) to the pickings and moves. This can make it possible to know which production lot was sent to a certain client, ..."""),
'group_stock_tracking_lot': fields.boolean("Use packages: pallets, boxes, ...",
implied_group='stock.group_tracking_lot',
help="""This allows you to manage products by using serial numbers. When you select a serial number on product moves, you can get the traceability of that product."""),
'group_stock_tracking_owner': fields.boolean("Manage owner on stock",
+7 -3
View File
@@ -27,13 +27,17 @@
<label for="id" string="Traceability"/>
<div>
<div>
<field name="group_stock_tracking_lot" class="oe_inline"/>
<label for="group_stock_tracking_lot"/>
<field name="group_stock_production_lot" class="oe_inline"/>
<label for="group_stock_production_lot"/>
</div>
<div attrs="{'invisible': [('group_stock_tracking_lot','=',False)]}">
<div attrs="{'invisible': [('group_stock_production_lot','=',False)]}">
<field name="module_product_expiry" class="oe_inline"/>
<label for="module_product_expiry"/>
</div>
<div>
<field name="group_stock_tracking_lot" class="oe_inline"/>
<label for="group_stock_tracking_lot"/>
</div>
<div>
<field name="group_stock_tracking_owner" class="oe_inline"/>
<label for="group_stock_tracking_owner"/>
+6 -1
View File
@@ -19,8 +19,13 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
<record id="group_production_lot" model="res.groups">
<field name="name">Manage Lots / Serial Numbers</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="group_tracking_lot" model="res.groups">
<field name="name">Manage Serial Numbers</field>
<field name="name">Manage Packages</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
+63 -12
View File
@@ -683,6 +683,7 @@ class stock_picking(osv.osv):
for move in move_lines:
if move[1]:
res[pick.id] = 'partially_available'
break
return res
def _get_pickings(self, cr, uid, ids, context=None):
@@ -3444,11 +3445,11 @@ class stock_pack_operation(osv.osv):
res = {}
for ops in self.browse(cr, uid, ids, context=context):
res[ops.id] = 0
if ops.package_id:
if ops.package_id and not ops.product_id:
#dont try to compute the remaining quantity for packages because it's not relevant (a package could include different products).
#should use _get_remaining_prod_quantities instead
continue
elif ops.product_id:
else:
qty = ops.product_qty
if ops.product_uom_id:
qty = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, ops.product_qty, ops.product_id.uom_id.id)
@@ -3533,24 +3534,74 @@ class stock_pack_operation(osv.osv):
if qty_to_assign <= 0:
break
def _check_quants_reserved(ops):
if ops.package_id and not ops.product_id:
for quant in quant_obj.browse(cr, uid, package_obj.get_content(cr, uid, [ops.package_id.id]), context=context):
if quant.reservation_id and quant.reservation_id.id in [x.id for x in ops.picking_id.move_lines] and (not quants_done.get(quant.id)):
#Entire packages means entire quants from those packages
if not quants_done.get(quant.id):
quants_done[quant.id] = 0
link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': quant.qty}, context=context)
else:
qty = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, ops.product_qty, ops.product_id.uom_id.id)
#Check moves with same product
for move in [x for x in ops.picking_id.move_lines if ops.product_id.id == x.product_id.id]:
for quant in move.reserved_quant_ids:
if not qty > 0:
break
if ops.package_id:
flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id]), ('id', '=', quant.package_id.id)], context=context)) or False
else:
flag = not quant.package_id.id
flag = flag and ((ops.lot_id and ops.lot_id.id == quant.lot_id.id) or not ops.lot_id)
flag = flag and (ops.owner_id.id == quant.owner_id.id)
if flag:
quant_qty = quant.qty
if quants_done.get(quant.id):
if quants_done[quant.id] == 0:
continue
quant_qty = quants_done[quant.id]
if quant_qty > qty:
qty_todo = qty
quants_done[quant.id] = quant_qty - qty
else:
qty_todo = quant_qty
quants_done[quant.id] = 0
qty -= qty_todo
link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': qty_todo}, context=context)
link_obj = self.pool.get('stock.move.operation.link')
uom_obj = self.pool.get('product.uom')
package_obj = self.pool.get('stock.quant.package')
if op_ids:
#sort moves in order to process first the ones that have already reserved quants
ops = self.browse(cr, uid, op_ids[0], context=context)
sorted_moves = ops.picking_id.move_lines
sorted_moves.sort(key = lambda x: x.product_qty - x.reserved_availability)
for op in self.browse(cr, uid, op_ids, context=context):
quant_obj = self.pool.get('stock.quant')
quants_done = {}
operations = self.browse(cr, uid, op_ids, context=context)
operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
sorted_moves = []
for op in operations:
if not sorted_moves:
#sort moves in order to process first the ones that have already reserved quants
sorted_moves = op.picking_id.move_lines
sorted_moves.sort(key=lambda x: x.product_qty - x.reserved_availability)
to_unlink_ids = [x.id for x in op.linked_move_operation_ids]
if to_unlink_ids:
link_obj.unlink(cr, uid, to_unlink_ids, context=context)
_check_quants_reserved(op)
for op in operations:
op.refresh()
if op.product_id:
normalized_qty = uom_obj._compute_qty(cr, uid, op.product_uom_id.id, op.product_qty, op.product_id.uom_id.id)
_create_link_for_product(op.product_id.id, normalized_qty)
#TODO: Remaining qty: UoM conversions are done twice
normalized_qty = uom_obj._compute_qty(cr, uid, op.product_uom_id.id, op.remaining_qty, op.product_id.uom_id.id)
if normalized_qty > 0:
_create_link_for_product(op.product_id.id, normalized_qty)
elif op.package_id:
for product_id, qty in package_obj._get_all_products_quantities(cr, uid, op.package_id.id, context=context).items():
_create_link_for_product(product_id, qty)
prod_quants = self._get_remaining_prod_quantities(cr, uid, op, context=context)
for product_id, qty in prod_quants.items():
if qty > 0:
_create_link_for_product(product_id, qty)
def process_packaging(self, cr, uid, operation, quants, context=None):
''' Process the packaging of a given operation, after the quants have been moved. If there was not enough quants found
+12 -12
View File
@@ -117,7 +117,7 @@
</group>
<group>
<field name="product_id" domain="[('type','=','product')]" attrs="{'invisible': [('filter', 'not in', ('product', 'product_owner'))]}"/>
<field name="lot_id" attrs="{'invisible': [('filter', '!=', 'lot')]}" groups="stock.group_tracking_lot" />
<field name="lot_id" attrs="{'invisible': [('filter', '!=', 'lot')]}" groups="stock.group_production_lot" />
<field name="partner_id" attrs="{'invisible': [('filter', 'not in', ('owner', 'product_owner'))]}" groups="stock.group_tracking_owner"/>
<field name="package_id" attrs="{'invisible': [('filter', '!=', 'pack')]}" groups="stock.group_tracking_lot"/>
</group>
@@ -130,7 +130,7 @@
<field context="{'location':location_id, 'uom':product_uom_id, 'to_date':parent.date}" name="product_id" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)" domain="[('type','=','product')]"/>
<field name="product_uom_id" groups="product.group_uom" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)"/>
<field domain="[('usage','=','internal')]" name="location_id" groups="stock.group_locations" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)"/>
<field name="prod_lot_id" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_tracking_lot"/>
<field name="prod_lot_id" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)" groups="stock.group_tracking_lot"/>
<field name="partner_id" on_change="on_change_product_id(location_id,product_id,product_uom_id,partner_id,prod_lot_id,package_id)" groups="stock.group_tracking_owner"/>
<field name="th_qty" readonly="1"/>
@@ -276,7 +276,7 @@
</field>
</record>
<menuitem action="action_production_lot_form" id="menu_action_production_lot_form"
parent="menu_traceability" groups="stock.group_tracking_lot"
parent="menu_traceability" groups="stock.group_production_lot"
sequence="1"/>
#
@@ -773,7 +773,7 @@
<tree editable="top">
<field name="product_id" on_change='product_id_change(product_id, product_uom_id, product_qty, context)'/>
<field name="product_uom_id" groups="product.group_uom" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_tracking_lot"/>
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="product_qty" attrs="{'required': [('product_id', '!=', False)]}" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
@@ -1739,7 +1739,7 @@
<field name="product_id"/>
<field name="location_id"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<group expand='0' string='Filters'>
<filter name='internal_loc' string="Internal Locations" domain="[('location_id.usage','=', 'internal')]"/>
@@ -1747,7 +1747,7 @@
<group expand='0' string='Group by...'>
<filter name="productgroup" string='Product' context="{'group_by' : 'product_id'}"/>
<filter string='Owner' context="{'group_by' : 'owner_id'}" groups="stock.group_tracking_owner"/>
<filter string='Lot' context="{'group_by' : 'lot_id'}" groups="stock.group_tracking_lot"/>
<filter string='Lot' context="{'group_by' : 'lot_id'}" groups="stock.group_production_lot"/>
<filter name="locationgroup" string='Location' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'location_id'}"/>
<filter string='Packaging' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'package_id'}" groups="stock.group_tracking_lot"/>
<filter string='Packaging Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'packaging_type_id'}" groups="stock.group_tracking_lot"/>
@@ -1777,7 +1777,7 @@
<group>
<field name="product_id"/>
<field name="qty"/>
<field name="lot_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="location_id"/>
<field name="in_date"/>
@@ -1801,7 +1801,7 @@
<field name="qty"/>
<field name="location_id"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="lot_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="packaging_type_id" invisible="1"/>
<field name="in_date"/>
@@ -1941,10 +1941,10 @@
<field name="outgoing_qty" class="oe_inline"/>
<field name="virtual_available" class="oe_inline"/>
</group>
<group name="lot" groups="stock.group_tracking_lot,stock.group_tracking_lot" string="Lots">
<field name="track_all" groups="stock.group_tracking_lot"/>
<field name="track_incoming" groups="stock.group_tracking_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<field name="track_outgoing" groups="stock.group_tracking_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<group name="lot" groups="stock.group_tracking_lot,stock.group_production_lot" string="Lots">
<field name="track_all" groups="stock.group_production_lot"/>
<field name="track_incoming" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
<field name="track_outgoing" groups="stock.group_production_lot" attrs="{'invisible': [('track_all', '=', True)]}"/>
</group>
</group>
</field>
+5 -1
View File
@@ -139,7 +139,11 @@
backorder_ids = self.search(cr, uid, [('backorder_id', '=', ref('delivery_order1'))], context=context)
assert backorder_ids, "Backorder should have been created"
self.do_prepare_partial(cr, uid, backorder_ids, context=context)
stock_pack = self.pool.get('stock.pack.operation').create(cr, uid, {'picking_id': backorder_ids[0], 'product_qty': 20})
picking = self.browse(cr, uid, backorder_ids[0])
assert len(picking.pack_operation_ids) == 2, "Wrong number of pack operation"
for pack_op in picking.pack_operation_ids:
assert pack_op.product_qty == 1, "Wrong quantity in pack operation (%s found instead of 1)" % (pack_op.product_qty)
assert pack_op.package_id.name in ('Pallet 2', 'Pallet 3'), "Wrong pallet info in pack operation (%s found)" % (pack_op.package_id.name)
self.action_assign(cr, uid, backorder_ids, context=context)
self.do_transfer(cr, uid, backorder_ids, context=context)
-
+4 -2
View File
@@ -93,10 +93,11 @@
!python {model: stock.picking}: |
self.do_transfer(cr, uid, [ref('delivery_order_neg')], context=context)
-
Check the quants that you have 120 pieces pallet 1 in customers, -20 pieces pallet 2 in stock and 120 and 20 with customers and 20/30 in stock, 10 in customers from pallet 3
Check the quants that you have 120 pieces pallet 1 in customers, -20 pieces pallet 2 in stock, 120 + 20 pieces 2 in customer with lot, and a total quantity of 50 in stock from pallet 3 (should be 20+30, as it has been split by reservation), finally 10 in customers from pallet 3
-
!python {model: stock.quant}: |
reco_id = self.search(cr ,uid , [('product_id','=',ref('product_neg'))], context=context)
pallet_3_stock_qty = 0
for rec in self.browse(cr, uid, reco_id, context=context):
if rec.package_id.name == 'Palneg 1' and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 120, "Should have 120 pieces on pallet 1"
@@ -106,11 +107,12 @@
elif rec.lot_id.name == 'Lot neg' and rec.location_id.id == ref('stock_location_customers'):
assert ((rec.qty == 20 or rec.qty == 120) and not rec.package_id), "Should have 140 pieces (120+20) in customer location from pallet 2 and lot A"
elif rec.package_id.name == 'Palneg 3' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 30 or rec.qty == 20, "Should have 30 and 20 pieces in stock on pallet 3"
pallet_3_stock_qty += rec.qty
elif not rec.package_id and not rec.lot_id and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 10, "Should have 10 pieces in customer location from pallet 3"
else:
assert False, "Unrecognized quant"
assert pallet_3_stock_qty == 50, "Should have 50 pieces in stock on pallet 3"
-
Create a picking for reconciling the negative quant
-
@@ -10,7 +10,7 @@
<field name="new_quantity" />
<field name="product_id" invisible="1"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="lot_id" context="{'search_default_product_id':product_id,'default_product_id':product_id}" groups="stock.group_tracking_lot"/>
<field name="lot_id" context="{'search_default_product_id':product_id,'default_product_id':product_id}" groups="stock.group_production_lot"/>
</group>
<footer>
<button name="change_product_qty" string="_Apply" type="object" class="oe_highlight"/>
+2 -1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Scrap move -->
<record id="view_stock_move_scrap_wizard" model="ir.ui.view">
@@ -15,7 +16,7 @@
<field name="product_qty" class="oe_inline"/>
<field name="product_uom" class="oe_inline" readonly="1" groups="product.group_uom"/>
</div>
<field name="restrict_lot_id" domain="[('product_id','=',product_id)]" groups="stock.group_tracking_lot"
<field name="restrict_lot_id" domain="[('product_id','=',product_id)]" groups="stock.group_production_lot"
context="{'default_product_id': product_id}"/>
<field name="location_id" string="Scrap Location" widget="selection"
domain="[('usage','!=','view'),('scrap_location','=',True)]" groups="stock.group_locations"/>
@@ -16,7 +16,7 @@
<field name="arch" type="xml">
<tree editable="bottom" string="Product Moves">
<field name="product_id" />
<field name="lot_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="quantity" />
</tree>
</field>
@@ -29,7 +29,7 @@
<form string="Return Picking Memory" version="7.0">
<group col="4">
<field name="product_id" />
<field name="lot_id" groups="stock.group_tracking_lot"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="quantity" />
</group>
</form>