[FIX] stock_reserve_rule: allow removal strategy based on lots and/or owner

The lot is ignored in _apply_strategy which makes it not possible to
create removal rules based on it. A practical example is to create a
rule with a quant_domain on lot_id.use_date (Best Before Date). Even
though the quants will be filtered, the lots wont be taken into
consideration in _apply_strategy and so random lots will be reserved.
With tis the signature for the _apply_strategy_%s methods is changed
to require returning the lot also or set it to None if it is not used.

The same rationale applies for the owner of the quant.
This commit is contained in:
Kiril Vangelovski
2022-09-07 13:43:12 +02:00
committed by Alessandro Uffreduzzi
parent f313930916
commit f53db888b3
3 changed files with 80 additions and 8 deletions

View File

@@ -85,16 +85,22 @@ class StockMove(models.Model):
next_quant = strategy.send(still_need)
if not next_quant:
continue
location, location_quantity, to_take = next_quant
(
location,
location_quantity,
to_take,
strat_lot_id,
strat_owner_id,
) = next_quant
taken_in_loc = super()._update_reserved_quantity(
# in this strategy, we take as much as we can
# from this bin
to_take,
location_quantity,
location_id=location,
lot_id=lot_id,
lot_id=strat_lot_id,
package_id=package_id,
owner_id=owner_id,
owner_id=strat_owner_id,
strict=strict,
)
still_need -= taken_in_loc

View File

@@ -195,7 +195,7 @@ class StockReserveRuleRemoval(models.Model):
each time the strategy decides to take quantities in a location,
it has to yield and retrieve the remaining needed using:
need = yield location, location_quantity, quantity_to_take
need = yield location, location_quantity, quantity_to_take, lot, owner
See '_apply_strategy_default' for a short example.
@@ -212,6 +212,8 @@ class StockReserveRuleRemoval(models.Model):
quant.location_id,
quant.quantity - quant.reserved_quantity,
need,
quant.lot_id,
quant.owner_id,
)
def _apply_strategy_empty_bin(self, quants):
@@ -247,7 +249,7 @@ class StockReserveRuleRemoval(models.Model):
continue
if float_compare(need, location_quantity, rounding) != -1:
need = yield location, location_quantity, need
need = yield location, location_quantity, need, None, None
def _apply_strategy_packaging(self, quants):
need = yield
@@ -293,4 +295,4 @@ class StockReserveRuleRemoval(models.Model):
if enough_for_packaging and asked_at_least_packaging_qty:
# compute how much packaging we can get
take = (need // pack_quantity) * pack_quantity
need = yield location, location_quantity, take
need = yield location, location_quantity, take, None, None

View File

@@ -105,9 +105,16 @@ class TestReserveRule(common.SavepointCase):
)
return picking
def _update_qty_in_location(self, location, product, quantity, in_date=None):
def _update_qty_in_location(
self, location, product, quantity, in_date=None, lot_id=None, owner_id=None
):
self.env["stock.quant"]._update_available_quantity(
product, location, quantity, in_date=in_date
product,
location,
quantity,
in_date=in_date,
lot_id=lot_id,
owner_id=owner_id,
)
def _create_rule(self, rule_values, removal_values):
@@ -371,6 +378,63 @@ class TestReserveRule(common.SavepointCase):
)
self.assertEqual(move.state, "assigned")
def test_quant_domain_lot_and_owner(self):
lot = self.env["stock.production.lot"].create(
{
"name": "P0001",
"product_id": self.product1.id,
"company_id": self.env.user.company_id.id,
}
)
owner = self.env["res.partner"].create({"name": "Owner", "ref": "C0001"})
self._update_qty_in_location(self.loc_zone1_bin1, self.product1, 100)
self._update_qty_in_location(
self.loc_zone1_bin1, self.product1, 100, lot_id=lot
)
self._update_qty_in_location(self.loc_zone2_bin1, self.product1, 100)
self._update_qty_in_location(
self.loc_zone2_bin1, self.product1, 100, owner_id=owner
)
picking = self._create_picking(self.wh, [(self.product1, 200)])
picking.owner_id = owner
self._create_rule(
{},
[
{
"location_id": self.loc_zone1.id,
"sequence": 1,
"quant_domain": [("lot_id.name", "ilike", "P%")],
},
{
"location_id": self.loc_zone2.id,
"sequence": 2,
"quant_domain": [("owner_id.ref", "ilike", "C%")],
},
],
)
picking.action_assign()
move = picking.move_lines
ml = move.move_line_ids
self.assertRecordValues(
ml,
[
{
"location_id": self.loc_zone1_bin1.id,
"product_qty": 100,
"lot_id": lot.id,
"owner_id": False,
},
{
"location_id": self.loc_zone2_bin1.id,
"product_qty": 100,
"lot_id": False,
"owner_id": owner.id,
},
],
)
self.assertEqual(move.state, "assigned")
def test_rule_empty_bin(self):
self._update_qty_in_location(self.loc_zone1_bin1, self.product1, 300)
self._update_qty_in_location(self.loc_zone1_bin2, self.product1, 150)