diff --git a/addons/mrp/models/stock_warehouse.py b/addons/mrp/models/stock_warehouse.py index d958379e476..a21b0f1277f 100644 --- a/addons/mrp/models/stock_warehouse.py +++ b/addons/mrp/models/stock_warehouse.py @@ -116,7 +116,8 @@ class StockWarehouse(models.Model): 'active': self.manufacture_to_resupply, 'name': self._format_rulename(location_id, False, 'Production'), 'location_id': location_id.id, - } + 'propagate_cancel': self.manufacture_steps == 'pbm_sam' + }, }, 'pbm_mto_pull_id': { 'depends': ['manufacture_steps', 'manufacture_to_resupply'], @@ -125,7 +126,6 @@ class StockWarehouse(models.Model): 'company_id': self.company_id.id, 'action': 'pull', 'auto': 'manual', - 'propagate_cancel': True, 'route_id': self._find_global_route('stock.route_warehouse0_mto', _('Make To Order')).id, 'name': self._format_rulename(self.lot_stock_id, self.pbm_loc_id, 'MTO'), 'location_id': self.pbm_loc_id.id, @@ -149,7 +149,6 @@ class StockWarehouse(models.Model): 'company_id': self.company_id.id, 'action': 'pull', 'auto': 'manual', - 'propagate_cancel': True, 'route_id': self._find_global_route('mrp.route_warehouse0_manufacture', _('Manufacture')).id, 'name': self._format_rulename(self.sam_loc_id, self.lot_stock_id, False), 'location_id': self.lot_stock_id.id, diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py index a0d78d3cc78..5c9da70426c 100644 --- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py +++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py @@ -203,3 +203,53 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): ('picking_type_id', '=', self.warehouse.pick_type_id.id) ]).picking_id self.assertEqual(picking_stock_pick.move_lines.move_orig_ids.picking_id, picking_stock_postprod) + + def test_cancel_propagation(self): + """ Test cancelling moves in a 'picking before + manufacturing' and 'store after manufacturing' process. The propagation of + cancel depends on the default values on each rule of the chain. + """ + self.warehouse.manufacture_steps = 'pbm_sam' + self.env['stock.quant']._update_available_quantity(self.raw_product, self.warehouse.lot_stock_id, 4.0) + picking_customer = self.env['stock.picking'].create({ + 'location_id': self.warehouse.lot_stock_id.id, + 'location_dest_id': self.customer_location, + 'partner_id': self.env['ir.model.data'].xmlid_to_res_id('base.res_partner_4'), + 'picking_type_id': self.warehouse.out_type_id.id, + }) + self.env['stock.move'].create({ + 'name': self.finished_product.name, + 'product_id': self.finished_product.id, + 'product_uom_qty': 2, + 'picking_id': picking_customer.id, + 'product_uom': self.uom_unit.id, + 'location_id': self.warehouse.lot_stock_id.id, + 'location_dest_id': self.customer_location, + 'procure_method': 'make_to_order', + }) + picking_customer.action_confirm() + production_order = self.env['mrp.production'].search([('product_id', '=', self.finished_product.id)]) + self.assertTrue(production_order) + + move_stock_preprod = self.env['stock.move'].search([ + ('product_id', '=', self.raw_product.id), + ('location_id', '=', self.warehouse.lot_stock_id.id), + ('location_dest_id', '=', self.warehouse.pbm_loc_id.id), + ('picking_type_id', '=', self.warehouse.pbm_type_id.id) + ]) + move_stock_postprod = self.env['stock.move'].search([ + ('product_id', '=', self.finished_product.id), + ('location_id', '=', self.warehouse.sam_loc_id.id), + ('location_dest_id', '=', self.warehouse.lot_stock_id.id), + ('picking_type_id', '=', self.warehouse.sam_type_id.id) + ]) + + self.assertTrue(move_stock_preprod) + self.assertTrue(move_stock_postprod) + self.assertEqual(move_stock_preprod.state, 'confirmed') + self.assertEqual(move_stock_postprod.state, 'waiting') + + move_stock_preprod._action_cancel() + self.assertEquals(production_order.state, 'confirmed') + production_order.action_cancel() + self.assertTrue(move_stock_postprod.state, 'cancel') diff --git a/addons/mrp_subcontracting/models/stock_warehouse.py b/addons/mrp_subcontracting/models/stock_warehouse.py index d23b0dc457b..a9d0cd1713b 100644 --- a/addons/mrp_subcontracting/models/stock_warehouse.py +++ b/addons/mrp_subcontracting/models/stock_warehouse.py @@ -70,7 +70,6 @@ class StockWarehouse(models.Model): 'company_id': self.company_id.id, 'action': 'pull', 'auto': 'manual', - 'propagate_cancel': True, 'route_id': self._find_global_route('stock.route_warehouse0_mto', _('Make To Order')).id, 'name': self._format_rulename(self.lot_stock_id, subcontract_location_id, 'MTO'), 'location_id': subcontract_location_id.id, @@ -88,7 +87,6 @@ class StockWarehouse(models.Model): 'company_id': self.company_id.id, 'action': 'pull', 'auto': 'manual', - 'propagate_cancel': True, 'route_id': self._find_global_route('mrp_subcontracting.route_resupply_subcontractor_mto', _('Resupply Subcontractor on Order')).id, 'name': self._format_rulename(self.lot_stock_id, subcontract_location_id, False), diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py index ee565177ff9..fb5393795f1 100644 --- a/addons/purchase_stock/models/stock.py +++ b/addons/purchase_stock/models/stock.py @@ -119,12 +119,14 @@ class StockWarehouse(models.Model): 'picking_type_id': self.in_type_id.id, 'group_propagation_option': 'none', 'company_id': self.company_id.id, - 'route_id': self._find_global_route('purchase_stock.route_warehouse0_buy', _('Buy')).id + 'route_id': self._find_global_route('purchase_stock.route_warehouse0_buy', _('Buy')).id, + 'propagate_cancel': self.reception_steps != 'one_step', }, 'update_values': { 'active': self.buy_to_resupply, 'name': self._format_rulename(location_id, False, 'Buy'), 'location_id': location_id.id, + 'propagate_cancel': self.reception_steps != 'one_step', } } }) diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 784a19afa44..aad9f297179 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -338,7 +338,6 @@ class Warehouse(models.Model): 'company_id': self.company_id.id, 'action': 'pull', 'auto': 'manual', - 'propagate_cancel': True, 'route_id': self._find_global_route('stock.route_warehouse0_mto', _('Make To Order')).id }, 'update_values': { @@ -434,7 +433,8 @@ class Warehouse(models.Model): }, 'rules_values': { 'active': True, - 'procure_method': 'make_to_order' + 'procure_method': 'make_to_order', + 'propagate_cancel': True, } }, 'delivery_route_id': { @@ -675,11 +675,21 @@ class Warehouse(models.Model): 'procure_method': first_rule and 'make_to_stock' or 'make_to_order', 'warehouse_id': self.id, 'company_id': self.company_id.id, - 'propagate_cancel': routing.picking_type != self.pick_type_id, } route_rule_values.update(values or {}) rules_list.append(route_rule_values) first_rule = False + if values.get('propagate_cancel') and rules_list: + # In case of rules chain with cancel propagation set, we need to stop + # the cancellation for the last step in order to avoid cancelling + # any other move after the chain. + # Example: In the following flow: + # Input -> Quality check -> Stock -> Customer + # We want that cancelling I->GC cancel QC -> S but not S -> C + # which means: + # Input -> Quality check should have propagate_cancel = True + # Quality check -> Stock should have propagate_cancel = False + rules_list[-1]['propagate_cancel'] = False return rules_list def _get_supply_pull_rules_values(self, route_values, values=None):