Skip to content

Commit

Permalink
[FIX] stock_reserve_rule: Module is restructured.
Browse files Browse the repository at this point in the history
  • Loading branch information
geomer198 committed Nov 18, 2023
1 parent 9fe2fbb commit 9a62218
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 127 deletions.
2 changes: 1 addition & 1 deletion stock_reserve_rule/demo/stock_reserve_rule_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
<field name="rule_id" ref="stock_reserve_rule_1_demo" />
<field name="sequence">4</field>
<field name="location_id" ref="stock_location_zone_d_demo" />
<field name="removal_strategy">single_lot</field>
<field name="removal_strategy">full_lot</field>
</record>
</odoo>
10 changes: 10 additions & 0 deletions stock_reserve_rule/models/stock_quant.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,13 @@ def _group_by_location(self):
else:
seen[location] = quant
return [(loc, quants) for loc, quants in seen.items()]

def group_by_lot(self):
seen = OrderedDict()
for quant in self:
lot = quant.lot_id
if lot in seen:
seen[lot] = seen[lot] | quant

Check warning on line 37 in stock_reserve_rule/models/stock_quant.py

View check run for this annotation

Codecov / codecov/patch

stock_reserve_rule/models/stock_quant.py#L37

Added line #L37 was not covered by tests
else:
seen[lot] = quant
return [(lot, quants) for lot, quants in seen.items()]
143 changes: 54 additions & 89 deletions stock_reserve_rule/models/stock_reserve_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class StockReserveRuleRemoval(models.Model):
("empty_bin", "Empty Bins"),
("packaging", "Full Packaging"),
("full_bin", "Full Bin"),
("single_lot", "Single lot"),
("full_lot", "Full Lot"),
],
required=True,
default="default",
Expand Down Expand Up @@ -190,35 +190,6 @@ class StockReserveRuleRemoval(models.Model):
compute="_compute_tolerance_display", store=True, string="Tolerance"
)

@api.constrains("tolerance_requested_limit")
def _check_tolerance_requested_limit(self):
if self.filtered(
lambda r: (
r.tolerance_requested_limit == "no_tolerance"
and (
r.tolerance_requested_computation != "percentage"
or r.tolerance_requested_value != 0.0
)
)
):
raise models.UserError(
_(
'"No Tolerance" must be have values -'
' "Tolerance computation" with value Percentage (%)\' '
"and \"Tolerance value\" with value '0.0'"
)
)

@api.onchange("tolerance_requested_limit")
def _onchange_tolerance_requested_limit(self):
if self.tolerance_requested_limit == "no_tolerance":
self.update(
{
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 0.0,
}
)

@api.depends(
"tolerance_requested_limit",
"tolerance_requested_computation",
Expand All @@ -228,9 +199,6 @@ def _compute_tolerance_display(self):
for rec in self:
tolerance_on = rec.tolerance_requested_limit
tolerance_computation = rec.tolerance_requested_computation
if not tolerance_computation or not tolerance_on:
rec.tolerance_display = ""
continue
value = rec.tolerance_requested_value
if value == 0.0:
rec.tolerance_display = "Requested Qty = Lot Qty"
Expand Down Expand Up @@ -431,70 +399,67 @@ def _apply_strategy_full_bin(self, quants):
if float_compare(need, location_quantity, rounding) != -1:
need = yield location, location_quantity, need, None, None

@api.model
def _get_requested_comparisons(self, rounding):
def _compare_with_tolerance(self, need, product_qty, rounding):
tolerance = self.tolerance_requested_value
limit = self.tolerance_requested_limit
if limit == "no_tolerance" or float_compare(tolerance, 0, rounding) == 0:
return lambda product_qty, need: product_qty == need
computation = self.tolerance_requested_computation
if limit == "upper_limit":

def wrap(product_qty, need):
if computation == "percentage":
return (
need + rounding < product_qty <= need * (100 + tolerance) / 100
)
# computation == "absolute"
else:
return need + rounding < product_qty <= need + tolerance

return wrap

if limit == "lower_limit":

def wrap(product_qty, need):
if computation == "percentage":
return (
need * (100 - tolerance) / 100 <= product_qty < need - rounding
if limit == "no_tolerance" or float_compare(tolerance, 0, rounding) == 0:
return float_compare(need, product_qty, rounding) == 0
elif limit == "upper_limit":
if computation == "percentage":
# need + rounding < product_qty <= need * (100 + tolerance) / 100
return (
float_compare(need, product_qty, rounding) == -1
and float_compare(
product_qty, need * (100 + tolerance) / 100, rounding
)
# computation == "absolute"
else:
return need - tolerance <= product_qty < need - rounding

return wrap
<= 0
)
else:
# need + rounding < product_qty <= need + tolerance
return (
float_compare(need, product_qty, rounding) == -1
and float_compare(product_qty, need + tolerance, rounding) <= 0
)
elif limit == "lower_limit":
if computation == "percentage":
# need * (100 - tolerance) / 100 <= product_qty < need - rounding
return (
float_compare(need * (100 - tolerance) / 100, product_qty, rounding)
<= 0
and float_compare(product_qty, need, rounding) == -1
)
# computation == "absolute"
else:
# need - tolerance <= product_qty < need - rounding
return (

Check warning on line 435 in stock_reserve_rule/models/stock_reserve_rule.py

View check run for this annotation

Codecov / codecov/patch

stock_reserve_rule/models/stock_reserve_rule.py#L435

Added line #L435 was not covered by tests
float_compare(need - tolerance, product_qty, rounding) <= 0
and float_compare(product_qty, need, rounding) == -1
)

def _apply_strategy_single_lot(self, quants):
def _apply_strategy_full_lot(self, quants):
need = yield
# We take goods only if we empty the bin.
# The original ordering (fefo, fifo, ...) must be kept.
product = fields.first(quants).product_id
rounding = product.uom_id.rounding
comparison = self._get_requested_comparisons(rounding)
if product.tracking == "lot" or comparison is not None:
for location, location_quants in quants._group_by_location():
grouped_quants = self.env["stock.quant"].read_group(
[
("lot_id", "!=", False),
("location_id", "in", location.ids),
("product_id", "=", product.id),
("quantity", ">", 0),
],
["lot_id", "quantity"],
["lot_id"],
orderby="id",
)
lot_ids_with_quantity = {
group["lot_id"][0]: group["quantity"] for group in grouped_quants
}
location_quantity = sum(location_quants.mapped("quantity")) - sum(
location_quants.mapped("reserved_quantity")
if product.tracking == "lot":
for lot, lot_quants in quants.filtered(
lambda quant, product_id=product.id: quant.product_id.id == product_id
).group_by_lot():
product_qty = sum(lot_quants.mapped("quantity"))
lot_quantity = sum(lot_quants.mapped("quantity")) - sum(
lot_quants.mapped("reserved_quantity")
)
lot_id = False
for rec_id, product_qty in lot_ids_with_quantity.items():
if comparison(product_qty, need):
lot_id = rec_id
break
if location_quantity > 0 and lot_id:
lot_id = self.env["stock.production.lot"].browse(lot_id)
need = yield location, location_quantity, need, lot_id, None
if (
lot
and self._compare_with_tolerance(need, product_qty, rounding)
and lot_quantity > 0
):
need = (
yield fields.first(lot_quants).location_id,
lot_quantity,
need,
lot,
None,
)
42 changes: 7 additions & 35 deletions stock_reserve_rule/tests/test_reserve_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,35 +844,7 @@ def test_rule_excluded_not_child_location(self):
)
self.assertEqual(move.state, "assigned")

def test_rule_no_tolerance_error(self):
with self.assertRaises(exceptions.UserError):
self._create_rule(
{"picking_type_ids": [(6, 0, self.wh.out_type_id.ids)], "sequence": 1},
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"tolerance_requested_limit": "no_tolerance",
"tolerance_requested_computation": "absolute",
"tolerance_requested_value": 0.0,
}
],
)
with self.assertRaises(exceptions.UserError):
self._create_rule(
{"picking_type_ids": [(6, 0, self.wh.out_type_id.ids)], "sequence": 1},
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"tolerance_requested_limit": "no_tolerance",
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 1.0,
}
],
)

def test_rule_single_lot_equals(self):
def test_rule_full_lot_equals(self):
self._update_qty_in_location(
self.loc_zone4_bin1, self.product3, 100, lot_id=self.lot0_id
)
Expand All @@ -883,7 +855,7 @@ def test_rule_single_lot_equals(self):
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "no_tolerance",
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 0.0,
Expand Down Expand Up @@ -930,7 +902,7 @@ def test_rule_validation(self):
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "lower_limit",
"tolerance_requested_computation": "absolute",
}
Expand All @@ -952,7 +924,7 @@ def test_rule_tolerance_absolute(self):
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "upper_limit",
"tolerance_requested_computation": "absolute",
"tolerance_requested_value": 1.0,
Expand Down Expand Up @@ -992,7 +964,7 @@ def test_rule_tolerance_percent(self):
[
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "upper_limit",
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 50.0,
Expand Down Expand Up @@ -1028,14 +1000,14 @@ def test_rule_tolerance_lower_limit(self):
[
{
"location_id": self.loc_zone1.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "lower_limit",
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 50.0,
},
{
"location_id": self.loc_zone4.id,
"removal_strategy": "single_lot",
"removal_strategy": "full_lot",
"tolerance_requested_limit": "no_tolerance",
"tolerance_requested_computation": "percentage",
"tolerance_requested_value": 0.0,
Expand Down
4 changes: 2 additions & 2 deletions stock_reserve_rule/views/stock_reserve_rule_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@
/>
<field
name="tolerance_requested_limit"
attrs="{'invisible': [('removal_strategy', '!=', 'single_lot')]}"
attrs="{'invisible': [('removal_strategy', '!=', 'full_lot')]}"
/>
<field
name="tolerance_requested_computation"
attrs="{'invisible': [('tolerance_requested_limit', '=', 'no_tolerance')], 'required': [('removal_strategy', '=', 'single_lot')], 'readonly': [('tolerance_requested_limit', '=', 'no_tolerance')]}"
attrs="{'invisible': [('tolerance_requested_limit', '=', 'no_tolerance')], 'required': [('removal_strategy', '=', 'full_lot')], 'readonly': [('tolerance_requested_limit', '=', 'no_tolerance')]}"
/>
<field
name="tolerance_requested_value"
Expand Down

0 comments on commit 9a62218

Please sign in to comment.