[FIX] stock,mrp: redirect byproducts with capacity constraints
When redirecting the SML of byproducts, the capacity constraints of
the locations are not considered.
To reproduce the issue:
1. In Settings, enable:
- By-Products
- Storage Locations
- Storage Categories
2. Create three storable products P01, P02, P_comp
3. Create a storage category SG:
- Max Weight: 1000
- Capacity by Products:
- 2 x P02
4. Create two locations L01, L02:
- Parent Location: WH/Stock
- Storage Category: SG
5. Create a putaway rule:
- From: WH/Stock
- Product: P02
- To: WH/Stock
- Having Category: SG
6. Create a BoM:
- Product: P01
- Components: 1 x P_compo
- By-products: 1 x P02
7. Update on hand qty of P02:
- 1 x P02 at L01
8. Create and confirm a MO with 2 x P01
9. Set the producing qty to 2
10. Open the detailed operations of P02
Error: The destination location is L01, this would break the
capacity constraint. The location should be L02
When setting the producing qty, we also set the done quantities of
the finished moves and we redirect them thanks to the putaway rules:
https://github.com/odoo/odoo/blob/cc6e31efe6fdd9dee16de1565afe9e1b502de45d/addons/stock/models/stock_move.py#L1885-L1888
However, the SML created only has the `qty_done` defined and, when
applying the putaway rules, we consider the `product_uom_qty` which
is equal to 0 in such a case.
So, when trying to redirect the SML of P02, we check if the capacity
constraint of L01 would be exceeded with the quantity of the SML.
But, as explained above, that quantity will be 0, so we conclude
that the capacity constraint is ok and we return L01 as best solution.
This is the reason why this commit suggests the use of the maximum
between done quantity and reserved quantity. This logic is already
used in the putaway application process, when we check the forecasted
quantity by location:
https://github.com/odoo/odoo/blob/a217ba27600c64c9104f8c1f003afd74ad06bd05/addons/stock/models/stock_location.py#L297-L301
OPW-3100322
closes odoo/odoo#109669
X-original-commit: 4ee8f974f993d2bbea7d9fc00779ebb1c1fa047d
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
@@ -271,23 +271,39 @@ class TestMrpByProduct(common.TransactionCase):
|
||||
)
|
||||
|
||||
def test_byproduct_putaway(self):
|
||||
""" Test the byproducts are dispatched correctly with putaway rules"""
|
||||
"""
|
||||
Test the byproducts are dispatched correctly with putaway rules. We have
|
||||
a byproduct P and two sublocations L01, L02 with a capacity constraint:
|
||||
max 2 x P by location. There is already 1 x P at L01. Process a MO with
|
||||
2 x P as byproducts. They should be redirected to L02
|
||||
"""
|
||||
|
||||
self.stock_location = self.env.ref('stock.stock_location_stock')
|
||||
stor_category = self.env['stock.storage.category'].create({
|
||||
'name': 'Super Storage Category',
|
||||
'max_weight': 1000,
|
||||
'product_capacity_ids': [(0, 0, {
|
||||
'product_id': self.product_b.id,
|
||||
'quantity': 2,
|
||||
})]
|
||||
})
|
||||
shelf1_location = self.env['stock.location'].create({
|
||||
'name': 'shelf1',
|
||||
'usage': 'internal',
|
||||
'location_id': self.stock_location.id,
|
||||
'storage_category_id': stor_category.id,
|
||||
})
|
||||
shelf2_location = self.env['stock.location'].create({
|
||||
'name': 'shelf2',
|
||||
'usage': 'internal',
|
||||
'location_id': self.stock_location.id,
|
||||
'storage_category_id': stor_category.id,
|
||||
})
|
||||
self.env['stock.putaway.rule'].create({
|
||||
'product_id': self.product_b.id,
|
||||
'location_in_id': self.stock_location.id,
|
||||
'location_out_id': shelf1_location.id,
|
||||
'location_out_id': self.stock_location.id,
|
||||
'storage_category_id': stor_category.id,
|
||||
})
|
||||
self.env['stock.putaway.rule'].create({
|
||||
'product_id': self.product_a.id,
|
||||
@@ -295,6 +311,8 @@ class TestMrpByProduct(common.TransactionCase):
|
||||
'location_out_id': shelf2_location.id,
|
||||
})
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(self.product_b, shelf1_location, 1)
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product_a
|
||||
mo_form.bom_id = self.bom_byproduct
|
||||
@@ -308,5 +326,5 @@ class TestMrpByProduct(common.TransactionCase):
|
||||
mo._post_inventory()
|
||||
byproduct_move_line = mo.move_byproduct_ids.move_line_ids
|
||||
finished_move_line = mo.move_finished_ids.filtered(lambda m: m.product_id == self.product_a).move_line_ids
|
||||
self.assertEqual(byproduct_move_line.location_dest_id, shelf1_location)
|
||||
self.assertEqual(byproduct_move_line.location_dest_id, shelf2_location)
|
||||
self.assertEqual(finished_move_line.location_dest_id, shelf2_location)
|
||||
|
||||
@@ -250,8 +250,9 @@ class StockMoveLine(models.Model):
|
||||
smls.package_level_id.location_dest_id = smls.location_dest_id
|
||||
else:
|
||||
for sml in smls:
|
||||
qty = max(sml.reserved_uom_qty, sml.qty_done)
|
||||
sml.location_dest_id = sml.move_id.location_dest_id.with_context(exclude_sml_ids=excluded_smls.ids)._get_putaway_strategy(
|
||||
sml.product_id, quantity=sml.reserved_uom_qty, packaging=sml.move_id.product_packaging_id,
|
||||
sml.product_id, quantity=qty, packaging=sml.move_id.product_packaging_id,
|
||||
)
|
||||
excluded_smls -= sml
|
||||
|
||||
|
||||
Reference in New Issue
Block a user