From 34c55c5e548d4d111fde68daaabee5f9678ffc07 Mon Sep 17 00:00:00 2001 From: "Adrien Widart (awt)" Date: Thu, 9 Mar 2023 14:01:13 +0000 Subject: [PATCH] [FIX] mrp: find BoM with other picking type To reproduce the issue: 1. In Settings, enable "Multi-Step Routes" 2. Create a second warehouse WH02 - Let WH01 be the existing one 3. Create two storable products P_compo, P_finished 4. Create a BoM: - Product: P_finished - Components: 1 x P_compo - Operation Type: "WH02: Manufacturing" 5. Create a MO: - Product: P_finished Error: Once the product is set, nothing defines the BoM (and therefore the components, picking type, and so on) When opening the MO form, the picking type is directly defined with "WH01: Manufacturing". Therefore, when looking for a BoM, we use that picking type as criteria -> we will not find the BoM of step 4 Notes about the fix: - Small behaviour change: once the picking type is set, it does not change automaticaly (whatever the BoM is) OPW-3122384 closes odoo/odoo#116515 X-original-commit: 93381af48e20a10c3fded88f010f89cd03cc5131 Signed-off-by: William Henrotin (whe) Signed-off-by: Adrien Widart --- addons/mrp/models/mrp_production.py | 16 ++++++-- addons/mrp/tests/test_order.py | 62 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 4e2e5f6608c..3e487e7636c 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -282,7 +282,12 @@ class MrpProduction(models.Model): ] picking_types = self.env['stock.picking.type'].search_read(domain, ['company_id'], load=False, limit=1) picking_type_by_company = {pt['company_id']: pt['id'] for pt in picking_types} + default_picking_type_id = self._context.get('default_picking_type_id') + default_picking_type = default_picking_type_id and self.env['stock.picking.type'].browse(default_picking_type_id) for mo in self: + if default_picking_type and default_picking_type.company_id == mo.company_id: + mo.picking_type_id = default_picking_type_id + continue if mo.bom_id and mo.bom_id.picking_type_id: mo.picking_type_id = mo.bom_id.picking_type_id continue @@ -345,21 +350,24 @@ class MrpProduction(models.Model): ): production.product_id = bom.product_id or bom.product_tmpl_id.product_variant_id - @api.depends('product_id', 'picking_type_id') + @api.depends('product_id') def _compute_bom_id(self): - mo_by_picking_type_and_company_id = defaultdict(lambda: self.env['mrp.production']) + mo_by_company_id = defaultdict(lambda: self.env['mrp.production']) for mo in self: if not mo.product_id and not mo.bom_id: mo.bom_id = False continue - mo_by_picking_type_and_company_id[(mo.picking_type_id, mo.company_id.id)] |= mo + mo_by_company_id[mo.company_id.id] |= mo - for (picking_type, company_id), productions in mo_by_picking_type_and_company_id.items(): + for company_id, productions in mo_by_company_id.items(): + picking_type_id = self._context.get('default_picking_type_id') + picking_type = picking_type_id and self.env['stock.picking.type'].browse(picking_type_id) boms_by_product = self.env['mrp.bom'].with_context(active_test=True)._bom_find(productions.product_id, picking_type=picking_type, company_id=company_id, bom_type='normal') for production in productions: if not production.bom_id or production.bom_id.product_tmpl_id != production.product_tmpl_id or (production.bom_id.product_id and production.bom_id.product_id != production.product_id): bom = boms_by_product[production.product_id] production.bom_id = bom.id or False + self.env.add_to_compute(production._fields['picking_type_id'], production) @api.depends('bom_id') def _compute_product_qty(self): diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 80ec4aad926..3bb2b8c4e6c 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -3163,3 +3163,65 @@ class TestMrpOrder(TestMrpCommon): ('warehouse_id.company_id', 'in', mo_2.company_id.ids), ], ['company_id'], load=False, limit=1) self.assertEqual(mo_2.picking_type_id.id, picking_type_company[0]['id']) + + def test_onchange_bom_ids_and_picking_type(self): + warehouse01 = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + warehouse02, warehouse03 = self.env['stock.warehouse'].create([ + {'name': 'Second Warehouse', 'code': 'WH02'}, + {'name': 'Third Warehouse', 'code': 'WH03'}, + ]) + + finished_product = self.env['product.product'].create({'name': 'finished product'}) + bom_wh01, bom_wh02 = self.env['mrp.bom'].create([{ + 'product_id': finished_product.id, + 'product_tmpl_id': finished_product.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 1.0, + 'bom_line_ids': [(0, 0, {'product_id': self.product.id, 'product_qty': 1})], + 'picking_type_id': wh.manu_type_id.id, + 'sequence': wh.id, + } for wh in [warehouse01, warehouse02]]) + + # Prioritize BoM of WH02 + bom_wh01.sequence = bom_wh02.sequence + 1 + + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = finished_product + self.assertEqual(mo_form.bom_id, bom_wh02, 'Should select the first BoM in the list, whatever the picking type is') + self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id) + + mo_form.bom_id = bom_wh01 + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the found BoM') + + mo_form.bom_id = bom_wh02 + self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id, 'Should be adapted because of the found BoM') + + mo_form.picking_type_id = warehouse01.manu_type_id + self.assertEqual(mo_form.bom_id, bom_wh02, 'Should not change') + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should not change') + + mo_form.picking_type_id = warehouse03.manu_type_id + mo_form.bom_id = bom_wh01 + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the found BoM ' + '(the selected picking type should be ignored)') + + mo_form = Form(self.env['mrp.production'].with_context(default_picking_type_id=warehouse03.manu_type_id.id)) + mo_form.product_id = finished_product + self.assertFalse(mo_form.bom_id, 'Should not find any BoM, because of the defined picking type') + self.assertEqual(mo_form.picking_type_id, warehouse03.manu_type_id) + + mo_form = Form(self.env['mrp.production'].with_context(default_picking_type_id=warehouse01.manu_type_id.id)) + mo_form.product_id = finished_product + self.assertEqual(mo_form.bom_id, bom_wh01, 'Should select the BoM that matches the default picking type') + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be the default one') + + mo_form.bom_id = bom_wh02 + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should not change, because of default value') + + mo_form.picking_type_id = warehouse02.manu_type_id + self.assertEqual(mo_form.bom_id, bom_wh02, 'Should not change') + self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id, 'Should not change') + + mo_form.picking_type_id = warehouse02.manu_type_id + mo_form.bom_id = bom_wh02 + self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the default value')