diff options
| author | Tiffany Chang (tic) <tic@odoo.com> | 2020-06-08 08:28:30 +0000 |
|---|---|---|
| committer | Tiffany Chang (tic) <tic@odoo.com> | 2020-06-18 08:33:22 +0000 |
| commit | d25d320e0e870980971de945719f7b7d5deadd3c (patch) | |
| tree | af0cce47127e35712e1035cea776e0f4005ccfab | |
| parent | 943ee2807083a436f83c3492dc36dadb1e34d24a (diff) | |
[IMP] mrp,(purchase_)stock: allow replenishment with only stock module
This commit makes it so products can be replenished without any other
module. This is achieved by adding a "pull" rule to the default receipt
route generated when a warehouse is created. "Pull" rule is added to 1-,
2-, and 3-step receipt routes. Previous to this commit, an additional
module (e.g. purchase, mrp, etc.) was needed for a pull rule to trigger
with the existing receipt route rules. Replenishments can be triggered
via "Replenish" button in product form, recording rules (auto trigger),
and replenishment (manual trigger).
Additionally, this commit makes it so when other modules (mrp, purchase)
that handle replenishment are installed, this additional "pull" rule is
removed from the default receipt route to prevent undesired behavior
(e.g. MTO manufacturing components magically being received from no
where). Unfortunately this means 2 undesirable situations:
1. If a user adds adds/changes rules to the receipt route and then installs
mrp or purchase then their additions/changes will be removed.
2. If a user removes either (when only 1 installed) or both of these
modules then stock module will again not be able to replenish as the
pull rule will not be re-added to the receipt route.
It is expected that these 2 cases do not need to be handled in most
cases or can be handled by advanced users.
Stock tests have also been updated to accomodate this new pull rule.
Specification 4 of overall inventory onboarding improvement task.
Task: 2220129
Related Enterprise PR: odoo/enterprise#10719
| -rw-r--r-- | addons/mrp/models/stock_warehouse.py | 2 | ||||
| -rw-r--r-- | addons/purchase_stock/__init__.py | 3 | ||||
| -rw-r--r-- | addons/purchase_stock/models/stock.py | 11 | ||||
| -rw-r--r-- | addons/stock/models/stock_warehouse.py | 60 | ||||
| -rw-r--r-- | addons/stock/tests/test_proc_rule.py | 21 | ||||
| -rw-r--r-- | addons/stock/tests/test_stock_flow.py | 5 |
6 files changed, 84 insertions, 18 deletions
diff --git a/addons/mrp/models/stock_warehouse.py b/addons/mrp/models/stock_warehouse.py index 8480af0f2cf..4f97f3c38d5 100644 --- a/addons/mrp/models/stock_warehouse.py +++ b/addons/mrp/models/stock_warehouse.py @@ -57,6 +57,7 @@ class StockWarehouse(models.Model): self.Routing(warehouse.sam_loc_id, warehouse.lot_stock_id, warehouse.sam_type_id, 'push'), ], }) + result[warehouse.id].update(warehouse._get_receive_rules_dict()) return result @api.model @@ -88,6 +89,7 @@ class StockWarehouse(models.Model): } } }) + routes.update(self._get_receive_routes_values('manufacture_to_resupply')) return routes def _get_route_name(self, route_type): diff --git a/addons/purchase_stock/__init__.py b/addons/purchase_stock/__init__.py index 6fee2cb6173..849b2eb3699 100644 --- a/addons/purchase_stock/__init__.py +++ b/addons/purchase_stock/__init__.py @@ -14,5 +14,4 @@ def _create_buy_rules(cr, registry): """ env = api.Environment(cr, SUPERUSER_ID, {}) warehouse_ids = env['stock.warehouse'].search([('buy_pull_id', '=', False)]) - for warehouse_id in warehouse_ids: - warehouse_id._create_or_update_global_routes_rules() + warehouse_ids.write({'buy_to_resupply': True}) diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py index 660fb32c0f0..a5e61fecd75 100644 --- a/addons/purchase_stock/models/stock.py +++ b/addons/purchase_stock/models/stock.py @@ -142,6 +142,17 @@ class StockWarehouse(models.Model): routes |= self.filtered(lambda self: self.buy_to_resupply and self.buy_pull_id and self.buy_pull_id.route_id).mapped('buy_pull_id').mapped('route_id') return routes + def get_rules_dict(self): + result = super(StockWarehouse, self).get_rules_dict() + for warehouse in self: + result[warehouse.id].update(warehouse._get_receive_rules_dict()) + return result + + def _get_routes_values(self): + routes = super(StockWarehouse, self)._get_routes_values() + routes.update(self._get_receive_routes_values('buy_to_resupply')) + return routes + def _update_name_and_code(self, name=False, code=False): res = super(StockWarehouse, self)._update_name_and_code(name, code) warehouse = self[0] diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index ead232b7cdd..96e77ba907a 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -468,7 +468,6 @@ class Warehouse(models.Model): }, 'rules_values': { 'active': True, - 'procure_method': 'make_to_order', 'propagate_cancel': True, } }, @@ -511,6 +510,42 @@ class Warehouse(models.Model): } } + def _get_receive_routes_values(self, installed_depends): + """ Return receive route values with 'procure_method': 'make_to_order' + in order to update warehouse routes. + + This function has the same receive route values as _get_routes_values with the addition of + 'procure_method': 'make_to_order' to the 'rules_values'. This is expected to be used by + modules that extend stock and add actions that can trigger receive 'make_to_order' rules (i.e. + we don't want any of the generated rules by get_rules_dict to default to 'make_to_stock'). + Additionally this is expected to be used in conjunction with _get_receive_rules_dict(). + + args: + installed_depends - string value of installed (warehouse) boolean to trigger updating of reception route. + """ + return { + 'reception_route_id': { + 'routing_key': self.reception_steps, + 'depends': ['reception_steps', installed_depends], + 'route_update_values': { + 'name': self._format_routename(route_type=self.reception_steps), + 'active': self.active, + }, + 'route_create_values': { + 'product_categ_selectable': True, + 'warehouse_selectable': True, + 'product_selectable': False, + 'company_id': self.company_id.id, + 'sequence': 9, + }, + 'rules_values': { + 'active': True, + 'propagate_cancel': True, + 'procure_method': 'make_to_order', + } + } + } + def _find_existing_rule_or_create(self, rules_list): """ This method will find existing rules or create new one. """ for rule_vals in rules_list: @@ -664,9 +699,12 @@ class Warehouse(models.Model): customer_loc, supplier_loc = self._get_partner_locations() return { warehouse.id: { - 'one_step': [], - 'two_steps': [self.Routing(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id, 'pull_push')], + 'one_step': [self.Routing(supplier_loc, warehouse.lot_stock_id, warehouse.in_type_id, 'pull')], + 'two_steps': [ + self.Routing(supplier_loc, warehouse.wh_input_stock_loc_id, warehouse.in_type_id, 'pull'), + self.Routing(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id, 'pull_push')], 'three_steps': [ + self.Routing(supplier_loc, warehouse.wh_input_stock_loc_id, warehouse.in_type_id, 'pull'), self.Routing(warehouse.wh_input_stock_loc_id, warehouse.wh_qc_stock_loc_id, warehouse.int_type_id, 'pull_push'), self.Routing(warehouse.wh_qc_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id, 'pull_push')], 'crossdock': [ @@ -684,6 +722,22 @@ class Warehouse(models.Model): } for warehouse in self } + def _get_receive_rules_dict(self): + """ Return receive route rules without initial pull rule in order to update warehouse routes. + + This function has the same receive route rules as get_rules_dict without an initial pull rule. + This is expected to be used by modules that extend stock and add actions that can trigger receive + 'make_to_order' rules (i.e. we don't expect the receive route to be able to pull on its own anymore). + This is also expected to be used in conjuction with _get_receive_routes_values() + """ + return { + 'one_step': [], + 'two_steps': [self.Routing(self.wh_input_stock_loc_id, self.lot_stock_id, self.int_type_id, 'pull_push')], + 'three_steps': [ + self.Routing(self.wh_input_stock_loc_id, self.wh_qc_stock_loc_id, self.int_type_id, 'pull_push'), + self.Routing(self.wh_qc_stock_loc_id, self.lot_stock_id, self.int_type_id, 'pull_push')], + } + def _get_inter_warehouse_route_values(self, supplier_warehouse): return { 'name': _('%s: Supply Product from %s') % (self.name, supplier_warehouse.name), diff --git a/addons/stock/tests/test_proc_rule.py b/addons/stock/tests/test_proc_rule.py index cababaebeef..190eaa25cfc 100644 --- a/addons/stock/tests/test_proc_rule.py +++ b/addons/stock/tests/test_proc_rule.py @@ -176,16 +176,17 @@ class TestProcRule(TransactionCase): orderpoint_form.product_max_qty = 5.0 orderpoint = orderpoint_form.save() - self.env['stock.rule'].create({ - 'name': 'Rule Supplier', - 'route_id': warehouse.reception_route_id.id, - 'location_id': warehouse.lot_stock_id.id, - 'location_src_id': self.env.ref('stock.stock_location_suppliers').id, - 'action': 'pull', - 'delay': 9.0, - 'procure_method': 'make_to_stock', - 'picking_type_id': warehouse.in_type_id.id, - }) + # get auto-created pull rule from when warehouse is created + rule = self.env['stock.rule'].search([ + ('route_id', '=', warehouse.reception_route_id.id), + ('location_id', '=', warehouse.lot_stock_id.id), + ('location_src_id', '=', self.env.ref('stock.stock_location_suppliers').id), + ('action', '=', 'pull'), + ('procure_method', '=', 'make_to_stock'), + ('picking_type_id', '=', warehouse.in_type_id.id)]) + + # add a delay [i.e. lead days] so procurement will be triggered based on forecasted stock + rule.delay = 9.0 delivery_move = self.env['stock.move'].create({ 'name': 'Delivery', diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index e1dc46edb4f..e5e301d723b 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -1723,8 +1723,7 @@ class TestStockFlow(TestStockCommon): 'location_id': self.stock_location, 'location_dest_id': self.customer_location}) - with self.assertRaises(UserError): - move_mto_alone._action_confirm() + move_mto_alone._action_confirm() move_with_ancestors._action_confirm() other_move._action_confirm() @@ -1732,7 +1731,7 @@ class TestStockFlow(TestStockCommon): move_with_ancestors._do_unreserve() other_move._do_unreserve() - self.assertEqual(move_mto_alone.state, "draft") + self.assertEqual(move_mto_alone.state, "waiting") self.assertEqual(move_with_ancestors.state, "waiting") self.assertEqual(other_move.state, "confirmed") |
