From 1de37862d6559c761fa26ef22715ff640f1f35b1 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 15 Nov 2016 10:40:57 +0100 Subject: [PATCH] [FIX] stock: when matching moves with pack operations through quants, it did not match the packages correctly. Courtesy of Pedro Baeza Before, it did a search to see if the package existed, but the only thing it needs to do is see if the package on the pack operation corresponds to that of the quant. (no need to check children also) There is a test added: A picking with 120 pieces incoming, 120 in pack 1, 80 in pack2 When we deliver those in a picking out, with product pack operations: (by taking them out of the pack) 120 from pack 1 and 80 from pack2, we should only have 2 quants and links between moves in the end And before, it generated 3 because it matched the wrong quants and made the wrong links. opw 693760 closes #13836 --- addons/stock/stock.py | 2 +- addons/stock/tests/common.py | 1 + addons/stock/tests/test_stock_flow.py | 127 +++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 2f9c3c9d41e..7d39758aae3 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1314,7 +1314,7 @@ class stock_picking(osv.osv): #check if the quant is matching the operation details if ops.package_id: - flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id])], context=context)) or False + flag = quant.package_id == ops.package_id 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) diff --git a/addons/stock/tests/common.py b/addons/stock/tests/common.py index 432c1e57f88..c868d8c1b84 100644 --- a/addons/stock/tests/common.py +++ b/addons/stock/tests/common.py @@ -35,6 +35,7 @@ class TestStockCommon(common.TransactionCase): self.productB = self.ProductObj.create({'name': 'Product B'}) self.productC = self.ProductObj.create({'name': 'Product C'}) self.productD = self.ProductObj.create({'name': 'Product D'}) + self.productE = self.ProductObj.create({'name': 'Product E', 'type': 'product'}) # Configure unit of measure. self.uom_kg = self.UomObj.create({ diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index bd3302b5be9..09012465fe9 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -1114,6 +1114,9 @@ class TestStockFlow(TestStockCommon): lot1 = lot_obj.create({'name': 'Lot001', 'product_id': lotproduct.id}) move = self.MoveObj.search([('product_id', '=', productKG.id), ('inventory_id', '=', inventory.id)], limit=1) self.assertEqual(len(move), 0, "Partial filter should not create a lines upon prepare") + + + line_vals = [] line_vals += [{'location_id': self.stock_location, 'product_id': packproduct.id, 'product_qty': 10, 'product_uom_id': packproduct.uom_id.id}] @@ -1127,7 +1130,7 @@ class TestStockFlow(TestStockCommon): quants = self.StockQuantObj.search([('product_id', '=', packproduct.id), ('location_id', '=', self.stock_location), ('package_id', '=', pack1.id)]) total_qty = sum([quant.qty for quant in quants]) self.assertEqual(total_qty, 20, 'Expecting 20 units on package 1 of packproduct, but we got %.4f on location stock!' % (total_qty)) - + #Create an inventory that will put the lots without lot to 0 and check that taking without pack will not take it from the pack inventory2 = self.InvObj.create({'name': 'Test Partial Lot and Pack2', 'filter': 'partial', @@ -1146,4 +1149,124 @@ class TestStockFlow(TestStockCommon): self.assertEqual(total_qty, 10, 'Expecting 0 units lot of lotproduct, but we got %.4f on location stock!' % (total_qty)) quants = self.StockQuantObj.search([('product_id', '=', lotproduct.id), ('location_id', '=', self.stock_location), ('lot_id', '=', False)]) total_qty = sum([quant.qty for quant in quants]) - self.assertEqual(total_qty, 0, 'Expecting 0 units lot of lotproduct, but we got %.4f on location stock!' % (total_qty)) \ No newline at end of file + self.assertEqual(total_qty, 0, 'Expecting 0 units lot of lotproduct, but we got %.4f on location stock!' % (total_qty)) + + + def test_30_create_in_out_with_product_pack_lines(self): + picking_in = self.PickingObj.create({ + 'partner_id': self.partner_delta_id, + 'picking_type_id': self.picking_type_in}) + self.MoveObj.create({ + 'name': self.productE.name, + 'product_id': self.productE.id, + 'product_uom_qty': 10, + 'product_uom': self.productE.uom_id.id, + 'picking_id': picking_in.id, + 'location_id': self.supplier_location, + 'location_dest_id': self.stock_location}) + + picking_in.action_confirm() + picking_in.do_prepare_partial() + pack_obj = self.env['stock.quant.package'] + pack1 = pack_obj.create({'name': 'PACKINOUTTEST1'}) + pack2 = pack_obj.create({'name': 'PACKINOUTTEST2'}) + picking_in.pack_operation_ids[0].result_package_id = pack1 + picking_in.pack_operation_ids[0].product_qty = 4 + packop2 = picking_in.pack_operation_ids[0].copy() + packop2.product_qty = 6 + packop2.result_package_id = pack2 + picking_in.do_transfer() + self.assertEqual(sum([x.qty for x in picking_in.move_lines[0].quant_ids]), 10.0, 'Expecting 10 pieces in stock') + #check the quants are in the package + self.assertEqual(sum(x.qty for x in pack1.quant_ids), 4.0, 'Pack 1 should have 4 pieces') + self.assertEqual(sum(x.qty for x in pack2.quant_ids), 6.0, 'Pack 2 should have 6 pieces') + picking_out = self.PickingObj.create({ + 'partner_id': self.partner_agrolite_id, + 'picking_type_id': self.picking_type_out}) + self.MoveObj.create({ + 'name': self.productE.name, + 'product_id': self.productE.id, + 'product_uom_qty': 3, + 'product_uom': self.productE.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location}) + picking_out.action_confirm() + picking_out.action_assign() + picking_out.do_prepare_partial() + packout1 = picking_out.pack_operation_ids[0] + packout2 = picking_out.pack_operation_ids[0].copy() + packout1.product_qty = 2 + packout1.package_id = pack1 + packout2.package_id = pack2 + packout2.product_qty = 1 + picking_out.do_transfer() + #Check there are no negative quants + neg_quants = self.env['stock.quant'].search([('product_id', '=', self.productE.id), ('qty', '<', 0.0)]) + self.assertEqual(len(neg_quants), 0, 'There are negative quants!') + self.assertEqual(len(picking_out.move_lines[0].linked_move_operation_ids), 2, 'We should have 2 links in the matching between the move and the operations') + self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end') + + + def test_40_create_in_out_with_product_pack_lines(self): + picking_in = self.PickingObj.create({ + 'partner_id': self.partner_delta_id, + 'picking_type_id': self.picking_type_in}) + self.MoveObj.create({ + 'name': self.productE.name, + 'product_id': self.productE.id, + 'product_uom_qty': 200, + 'product_uom': self.productE.uom_id.id, + 'picking_id': picking_in.id, + 'location_id': self.supplier_location, + 'location_dest_id': self.stock_location}) + + picking_in.action_confirm() + picking_in.do_prepare_partial() + pack_obj = self.env['stock.quant.package'] + pack1 = pack_obj.create({'name': 'PACKINOUTTEST1'}) + pack2 = pack_obj.create({'name': 'PACKINOUTTEST2'}) + picking_in.pack_operation_ids[0].result_package_id = pack1 + picking_in.pack_operation_ids[0].product_qty = 120 + packop2 = picking_in.pack_operation_ids[0].copy() + packop2.product_qty = 80 + packop2.result_package_id = pack2 + picking_in.do_transfer() + self.assertEqual(sum([x.qty for x in picking_in.move_lines[0].quant_ids]), 200.0, 'Expecting 200 pieces in stock') + #check the quants are in the package + self.assertEqual(sum(x.qty for x in pack1.quant_ids), 120, 'Pack 1 should have 120 pieces') + self.assertEqual(sum(x.qty for x in pack2.quant_ids), 80, 'Pack 2 should have 80 pieces') + picking_out = self.PickingObj.create({ + 'partner_id': self.partner_agrolite_id, + 'picking_type_id': self.picking_type_out}) + self.MoveObj.create({ + 'name': self.productE.name, + 'product_id': self.productE.id, + 'product_uom_qty': 200 , + 'product_uom': self.productE.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location}) + picking_out.action_confirm() + picking_out.action_assign() + picking_out.do_prepare_partial() + #Convert entire packs into taking out of packs + packout0 = picking_out.pack_operation_ids[0] + packout1 = picking_out.pack_operation_ids[1] + packout0.write({'product_id': self.productE.id, + 'product_qty' : 120.0, + 'product_uom_id' : self.productE.uom_id.id, + 'package_id': pack1.id, + }) + packout1.write({'product_id': self.productE.id, + 'product_qty' : 80.0, + 'product_uom_id' : self.productE.uom_id.id, + 'package_id': pack2.id, + }) + picking_out.do_transfer() + #Check there are no negative quants + neg_quants = self.env['stock.quant'].search([('product_id', '=', self.productE.id), ('qty', '<', 0.0)]) + self.assertEqual(len(neg_quants), 0, 'There are negative quants!') + # We should also make sure that when matching stock moves with pack operations, it takes the correct + self.assertEqual(len(picking_out.move_lines[0].linked_move_operation_ids), 2, 'We should only have 2 links beween the move and the 2 operations') + self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end') \ No newline at end of file