[FIX] stock: apply putaway on package with one product category
When applying the putaway rules, if we redirect a package (with a specific type), we don't check the category of the package products. To reproduce the issue: 1. In Settings, enable - Packages - Multi-Step Routes - Storage Categories 2. Edit the warehouse: - Receipt: 2 steps 3. Create three categories C1, C2, C3 - Parent: All 4. Create three locations L1, L2, L3 - Parent: WH/Stock 5. For each location Lx, create a putaway rules PRx: - From: WH/Stock - Category: Cx - To: Lx 6. Create a product P - Type: Storable - Category: C2 7. Create a planned receipt R with 1 x P 8. Mark it as todo, set the done quantity and put it in pack 9. Set a type on the package 10. Mark R as done 11. Open the associated internal transfer Error: the destination location of the package is not L2 -> an incorrect putaway rule has been applied When we try to apply the PR on the SML "input to stock", because this is a package with a type, we don't provide the list of the products. Therefore, we have no idea about the product category. This is a shame in case of a package that contains some products of the same category. We should be able to apply the putaway rules related to that category. Moreover, this commit also fixes two other issues explained in [1] and [2]. The FW of these commits have been stopped for the current one because they didn't fix the above use case. Moreover, this commit simplifies/clarifies the filtering and sorting of all putaway rules [1] 03e47b165e7f6c59b41509d9229e8e2074aa3f34 [2] e0098eb281d886797a888a6f8fac474bbacb1b09 OPW-3098452 X-original-commit: c99523bc3a4a5a335f1928cf9bb53bb8483b492e Part-of: odoo/odoo#109669
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user