From c453358c885eddf0bfef0d345aaefcc590474305 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 17 Jan 2017 11:34:43 +0100 Subject: [PATCH] [FIX] mrp: intermediate post inventory during a MO When posting inventory during a manufacturing order, all raw material and finished product moves are used, also those done from a previous post inventory. This is no problem for the moving of quants because a filtering is done in the move_validate method, but it is a problem for the price calculation in mrp_account and for linking the consumed/produced_quant_ids of the quants of the raw and finished moves. Before this fix, it would calculate already consumed materials a second time in the price of the finished products and would link quants produced with previously consumed quants (and vice versa) when doing a second post inventory. We add a test to show the issue. --- addons/mrp/models/mrp_production.py | 5 +- addons/mrp/tests/test_order.py | 84 ++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 3b6d10f34da..db904b64628 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -486,11 +486,10 @@ class MrpProduction(models.Model): @api.multi def post_inventory(self): for order in self: - moves_to_do = order.move_raw_ids + moves_to_do = order.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves_to_do.action_done() - #order.move_finished_ids.filtered(lambda x: x.state not in ('done','cancel')).move_validate() order._cal_price(moves_to_do) - moves_to_finish = order.move_finished_ids + moves_to_finish = order.move_finished_ids.filtered(lambda x: x.state not in ('done','cancel')) moves_to_finish.action_done() for move in moves_to_finish: #Group quants by lots diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 3477eaae8b7..64a42fbb136 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -344,4 +344,86 @@ class TestMrpOrder(TestMrpCommon): 'bom_id': self.bom_3.id, 'product_uom_id': self.product_6.uom_id.id,}) self.assertEqual(production.routing_id.id, False, 'The routing field should be empty on the mo') - self.assertEqual(production.move_raw_ids[0].location_id.id, self.warehouse_1.wh_input_stock_loc_id.id, 'Raw moves start location should have altered.') \ No newline at end of file + self.assertEqual(production.move_raw_ids[0].location_id.id, self.warehouse_1.wh_input_stock_loc_id.id, 'Raw moves start location should have altered.') + + def test_multiple_post_inventory(self): + """ Check the consumed quants of the produced quants when intermediate calls to `post_inventory` during a MO.""" + + # create a bom for `custom_laptop` with components that aren't tracked + unit = self.ref("product.product_uom_unit") + custom_laptop = self.env.ref("product.product_product_27") + custom_laptop.tracking = 'none' + product_charger = self.env['product.product'].create({ + 'name': 'Charger', + 'type': 'product', + 'uom_id': unit, + 'uom_po_id': unit}) + product_keybord = self.env['product.product'].create({ + 'name': 'Usb Keybord', + 'type': 'product', + 'uom_id': unit, + 'uom_po_id': unit}) + bom_custom_laptop = self.env['mrp.bom'].create({ + 'product_tmpl_id': custom_laptop.product_tmpl_id.id, + 'product_qty': 1, + 'product_uom_id': unit, + 'bom_line_ids': [(0, 0, { + 'product_id': product_charger.id, + 'product_qty': 1, + 'product_uom_id': unit + }), (0, 0, { + 'product_id': product_keybord.id, + 'product_qty': 1, + 'product_uom_id': unit + })] + }) + + # put the needed products in stock + source_location_id = self.ref('stock.stock_location_14') + inventory = self.env['stock.inventory'].create({ + 'name': 'Inventory Product Table', + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': product_charger.id, + 'product_uom_id': product_charger.uom_id.id, + 'product_qty': 2, + 'location_id': source_location_id + }), (0, 0, { + 'product_id': product_keybord.id, + 'product_uom_id': product_keybord.uom_id.id, + 'product_qty': 2, + 'location_id': source_location_id + })] + }) + inventory.action_done() + + # create a mo for this bom + mo_custom_laptop = self.env['mrp.production'].create({ + 'product_id': custom_laptop.id, + 'product_qty': 2, + 'product_uom_id': unit, + 'bom_id': bom_custom_laptop.id + }) + mo_custom_laptop.action_assign() + self.assertEqual(mo_custom_laptop.availability, 'assigned') + + # produce one item, call `post_inventory` + context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id} + custom_laptop_produce = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 1.00}) + custom_laptop_produce.do_produce() + mo_custom_laptop.post_inventory() + + # check the consumed quants of the produced quant + first_move = mo_custom_laptop.move_finished_ids.filtered(lambda mo: mo.state == 'done') + self.assertEquals(sum(first_move.quant_ids.mapped('consumed_quant_ids').mapped('qty')), 2) + + second_move = mo_custom_laptop.move_finished_ids.filtered(lambda mo: mo.state == 'confirmed') + + # produce the second item, call `post_inventory` + context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id} + custom_laptop_produce = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 1.00}) + custom_laptop_produce.do_produce() + mo_custom_laptop.post_inventory() + + # check the consumed quants of the newly produced quant + self.assertEquals(sum(second_move.quant_ids.mapped('consumed_quant_ids').mapped('qty')), 2)