diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py index 471626eec98..457353425e8 100644 --- a/addons/stock/models/stock_location.py +++ b/addons/stock/models/stock_location.py @@ -256,6 +256,8 @@ class Location(models.Model): no package is specified. """ self = self._check_access_putaway() + products = self.env.context.get('products', self.env['product.product']) + products |= product # find package type on package or packaging package_type = self.env['stock.package.type'] if package: @@ -263,14 +265,22 @@ class Location(models.Model): elif packaging: package_type = packaging.package_type_id - putaway_rules = self.env['stock.putaway.rule'] - putaway_rules |= self.putaway_rule_ids.filtered(lambda x: x.product_id == product and (package_type in x.package_type_ids or package_type == x.package_type_ids)) - categ = product.categ_id - while categ: - putaway_rules |= self.putaway_rule_ids.filtered(lambda x: x.category_id == categ and (package_type in x.package_type_ids or package_type == x.package_type_ids)) - categ = categ.sudo().parent_id - if package_type: - putaway_rules |= self.putaway_rule_ids.filtered(lambda x: not x.product_id and (package_type in x.package_type_ids or package_type == x.package_type_ids)) + categ = products.categ_id if len(products.categ_id) == 1 else self.env['product.category'] + categs = categ + while categ.parent_id: + categ = categ.parent_id + categs |= categ + + putaway_rules = self.putaway_rule_ids.filtered(lambda rule: + (not rule.product_id or rule.product_id in products) and + (not rule.category_id or rule.category_id in categs) and + (not rule.package_type_ids or package_type in rule.package_type_ids)) + + putaway_rules = putaway_rules.sorted(lambda rule: (rule.package_type_ids, + rule.product_id, + rule.category_id == categs[:1], # same categ, not a parent + rule.category_id), + reverse=True) putaway_location = None locations = self.child_internal_location_ids diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 9a8e7d80ae4..3f1b9d1ec41 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -234,7 +234,7 @@ class StockMoveLine(models.Model): smls = self.env['stock.move.line'].concat(*smls) excluded_smls = smls if package.package_type_id: - best_loc = smls.move_id.location_dest_id.with_context(exclude_sml_ids=excluded_smls.ids)._get_putaway_strategy(self.env['product.product'], package=package) + best_loc = smls.move_id.location_dest_id.with_context(exclude_sml_ids=excluded_smls.ids, products=smls.product_id)._get_putaway_strategy(self.env['product.product'], package=package) smls.location_dest_id = smls.package_level_id.location_dest_id = best_loc elif package: used_locations = set() diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 7f31c06fe4f..81ca2541f79 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -939,6 +939,144 @@ class StockMove(TransactionCase): # check if the putaway was rightly applied self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf2_location.id) + def test_putaway_7(self): + """ + Putaway with one package type and one product + """ + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + warehouse.reception_steps = 'two_steps' + child_loc = self.stock_location.child_ids[0] + + package_type = self.env['stock.package.type'].create({ + 'name': 'Super Package Type', + }) + + package = self.env['stock.quant.package'].create({'package_type_id': package_type.id}) + + self.env['stock.putaway.rule'].create({ + 'product_id': self.product.id, + 'package_type_ids': [(6, 0, package_type.ids)], + 'location_in_id': self.stock_location.id, + 'location_out_id': child_loc.id, + }) + + move_input = self.env['stock.move'].create({ + 'name': self.product.name, + 'location_id': self.supplier_location.id, + 'location_dest_id': warehouse.wh_input_stock_loc_id.id, + 'product_id': self.product.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + 'warehouse_id': warehouse.id, + }) + move_input._action_confirm() + move_input.move_line_ids.qty_done = 1 + move_input.move_line_ids.result_package_id = package + move_input._action_done() + + move_stock = move_input.move_dest_ids + self.assertEqual(move_stock.move_line_ids.location_dest_id, child_loc) + + def test_putaway_8(self): + """ + Putaway with product P + Receive 1 x P in a package with a specific type + """ + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + warehouse.reception_steps = 'two_steps' + child_loc = self.stock_location.child_ids[0] + + package_type = self.env['stock.package.type'].create({ + 'name': 'Super Package Type', + }) + + package = self.env['stock.quant.package'].create({'package_type_id': package_type.id}) + + self.env['stock.putaway.rule'].create({ + 'product_id': self.product.id, + 'location_in_id': self.stock_location.id, + 'location_out_id': child_loc.id, + }) + + move_input = self.env['stock.move'].create({ + 'name': self.product.name, + 'location_id': self.supplier_location.id, + 'location_dest_id': warehouse.wh_input_stock_loc_id.id, + 'product_id': self.product.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + 'warehouse_id': warehouse.id, + }) + move_input._action_confirm() + move_input.move_line_ids.qty_done = 1 + move_input.move_line_ids.result_package_id = package + move_input._action_done() + + move_stock = move_input.move_dest_ids + self.assertEqual(move_stock.move_line_ids.location_dest_id, child_loc) + + def test_putaway_9(self): + """ + Putaway with one category C + 2 steps receive + Receive one C-type product in a package with a specific type + The putaway should be selected + """ + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1) + warehouse.reception_steps = 'two_steps' + + basic_category = self.env.ref('product.product_category_all') + child_locations = self.env['stock.location'] + categs = self.env['product.category'] + + for i in range(3): + loc = self.env['stock.location'].create({ + 'name': 'shelf %s' % i, + 'usage': 'internal', + 'location_id': self.stock_location.id, + }) + child_locations |= loc + + categ = self.env['product.category'].create({ + 'name': 'Category %s' % i, + 'parent_id': basic_category.id + }) + categs |= categ + + self.env['stock.putaway.rule'].create({ + 'category_id': categ.id, + 'location_in_id': self.stock_location.id, + 'location_out_id': loc.id, + }) + + second_child_location = child_locations[1] + second_categ = categs[1] + self.product.categ_id = second_categ + + package_type = self.env['stock.package.type'].create({ + 'name': 'Super Package Type', + }) + package = self.env['stock.quant.package'].create({ + 'package_type_id': package_type.id, + }) + + move_input = self.env['stock.move'].create({ + 'name': self.product.name, + 'location_id': self.supplier_location.id, + 'location_dest_id': warehouse.wh_input_stock_loc_id.id, + 'product_id': self.product.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + 'warehouse_id': warehouse.id, + }) + move_input._action_confirm() + move_input.move_line_ids.qty_done = 1 + move_input.move_line_ids.result_package_id = package + move_input._action_done() + + move_stock = move_input.move_dest_ids + self.assertEqual(move_stock.move_line_ids.location_dest_id, second_child_location) + def test_putaway_with_storage_category_1(self): """Receive a product. Test the product will be move to a child location with correct storage category.