[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
This commit is contained in:
Tiffany Chang (tic)
2020-06-18 08:33:22 +00:00
parent 943ee28070
commit d25d320e0e
6 changed files with 84 additions and 18 deletions
+2
View File
@@ -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):
+1 -2
View File
@@ -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})
+11
View File
@@ -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]
+57 -3
View File
@@ -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),
+11 -10
View File
@@ -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',
+2 -3
View File
@@ -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")