From b6c629922d9ebf2d2fb0f52f13b1648c49bca36f Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Tue, 24 Mar 2020 09:14:40 +0000 Subject: [PATCH] [FIX] stock,mrp: change procure method at cancellation Usecase : sell a mto/manufacture product. Confirm the sale order. Cancel the delivery and the production then duplicate the delivery. - if the delivery was cancelled before the production, another production is created. - if the delivery was cancelled after the production, no other production is created. In the second case, the production cancellation will reset the procure method of the delivery. The duplication will not trigger the mto procurement. This commit will override the procure method of each cancelled moves and set it to `make_to_stock'. This way, we have the same behavior independently of the flow. Task : 2212125 closes odoo/odoo#48366 Signed-off-by: Simon Lejeune (sle) --- addons/sale_mrp/tests/test_sale_mrp_flow.py | 108 ++++++++++++++++++++ addons/stock/models/stock_move.py | 9 +- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 870869da553..e06fe685ce3 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -1539,3 +1539,111 @@ class TestSaleMrpFlow(AccountTestCommon): mo = self.env['mrp.production'].search([('product_id', '=', finished_product.id)]) self.assertTrue(mo, 'Manufacturing order created.') + + def test_cancel_flow_1(self): + """ Sell a MTO/manufacture product. + + Cancel the delivery and the production order. Then duplicate + the delivery. Another production order should be created.""" + route_manufacture = self.warehouse.manufacture_pull_id.route_id.id + route_mto = self.warehouse.mto_pull_id.route_id.id + self.uom_unit = self.env.ref('uom.product_uom_unit') + + # Create finished product + finished_product = self.env['product.product'].create({ + 'name': 'Geyser', + 'type': 'product', + 'route_ids': [(4, route_mto), (4, route_manufacture)], + }) + + product_raw = self.env['product.product'].create({ + 'name': 'raw Geyser', + 'type': 'product', + }) + + # Create bom for finish product + bom = self.env['mrp.bom'].create({ + 'product_id': finished_product.id, + 'product_tmpl_id': finished_product.product_tmpl_id.id, + 'product_uom_id': self.env.ref('uom.product_uom_unit').id, + 'product_qty': 1.0, + 'type': 'normal', + 'bom_line_ids': [(5, 0), (0, 0, {'product_id': product_raw.id})] + }) + + # Create sale order + sale_form = Form(self.env['sale.order']) + sale_form.partner_id = self.env['res.partner'].create({'name': 'My Test Partner'}) + with sale_form.order_line.new() as line: + line.name = finished_product.name + line.product_id = finished_product + line.product_uom_qty = 1.0 + line.product_uom = self.uom_unit + line.price_unit = 10.0 + sale_order = sale_form.save() + + sale_order.action_confirm() + + mo = self.env['mrp.production'].search([('product_id', '=', finished_product.id)]) + delivery = sale_order.picking_ids + delivery.action_cancel() + mo.action_cancel() + copied_delivery = delivery.copy() + copied_delivery.action_confirm() + mos = self.env['mrp.production'].search([('product_id', '=', finished_product.id)]) + self.assertEqual(len(mos), 1) + self.assertEqual(mos.state, 'cancel') + + def test_cancel_flow_2(self): + """ Sell a MTO/manufacture product. + + Cancel the production order and the delivery. Then duplicate + the delivery. Another production order should be created.""" + route_manufacture = self.warehouse.manufacture_pull_id.route_id.id + route_mto = self.warehouse.mto_pull_id.route_id.id + self.uom_unit = self.env.ref('uom.product_uom_unit') + + # Create finished product + finished_product = self.env['product.product'].create({ + 'name': 'Geyser', + 'type': 'product', + 'route_ids': [(4, route_mto), (4, route_manufacture)], + }) + + product_raw = self.env['product.product'].create({ + 'name': 'raw Geyser', + 'type': 'product', + }) + + # Create bom for finish product + bom = self.env['mrp.bom'].create({ + 'product_id': finished_product.id, + 'product_tmpl_id': finished_product.product_tmpl_id.id, + 'product_uom_id': self.env.ref('uom.product_uom_unit').id, + 'product_qty': 1.0, + 'type': 'normal', + 'bom_line_ids': [(5, 0), (0, 0, {'product_id': product_raw.id})] + }) + + # Create sale order + sale_form = Form(self.env['sale.order']) + sale_form.partner_id = self.env['res.partner'].create({'name': 'My Test Partner'}) + with sale_form.order_line.new() as line: + line.name = finished_product.name + line.product_id = finished_product + line.product_uom_qty = 1.0 + line.product_uom = self.uom_unit + line.price_unit = 10.0 + sale_order = sale_form.save() + + sale_order.action_confirm() + + mo = self.env['mrp.production'].search([('product_id', '=', finished_product.id)]) + delivery = sale_order.picking_ids + mo.action_cancel() + delivery.action_cancel() + copied_delivery = delivery.copy() + copied_delivery.action_confirm() + mos = self.env['mrp.production'].search([('product_id', '=', finished_product.id)]) + self.assertEqual(len(mos), 1) + self.assertEqual(mos.state, 'cancel') diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 5540fdcaf51..38f2d11f830 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -541,7 +541,7 @@ class StockMove(models.Model): self.ensure_one() picking_type_id = self.picking_type_id or self.picking_id.picking_type_id - + # If "show suggestions" is not checked on the picking type, we have to filter out the # reserved move lines. We do this by displaying `move_line_nosuggest_ids`. We use # different views to display one field or another so that the webclient doesn't have to @@ -1291,7 +1291,12 @@ class StockMove(models.Model): if all(state in ('done', 'cancel') for state in siblings_states): move.move_dest_ids.write({'procure_method': 'make_to_stock'}) move.move_dest_ids.write({'move_orig_ids': [(3, move.id, 0)]}) - self.write({'state': 'cancel', 'move_orig_ids': [(5, 0, 0)], 'delay_alert_date': False}) + self.write({ + 'state': 'cancel', + 'move_orig_ids': [(5, 0, 0)], + 'delay_alert_date': False, + 'procure_method': 'make_to_stock', + }) return True def _prepare_extra_move_vals(self, qty):