Files
stock-logistics-warehouse/stock_reserve_rule/models/stock_move.py
Guewen Baconnier bf3e5867dc Remove implicit fallback when rules are used
When rules are configured and have been applied, we should not
have an implicit fallback on the base location, as it would kind
of cancel the benefits of the rules (as it would then take whatever
it wants anywhere in all the locations).
2020-05-28 07:59:43 +02:00

119 lines
4.5 KiB
Python

# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models
from odoo.tools.float_utils import float_compare
class StockMove(models.Model):
_inherit = "stock.move"
def _update_reserved_quantity(
self,
need,
available_quantity,
location_id,
lot_id=None,
package_id=None,
owner_id=None,
strict=True,
):
"""Create or update move lines."""
if strict:
# chained moves must take what was reserved by the previous move
return super()._update_reserved_quantity(
need,
available_quantity,
location_id=location_id,
lot_id=lot_id,
package_id=package_id,
owner_id=owner_id,
strict=strict,
)
rules = self.env["stock.reserve.rule"]._rules_for_location(location_id)
forced_package_id = self.package_level_id.package_id or None
rounding = self.product_id.uom_id.rounding
still_need = need
for rule in rules:
# 1st check if rule is applicable from the move
if not rule._is_rule_applicable(self):
continue
for removal_rule in rule.rule_removal_ids:
# Exclude any rule which does not share the same path as the
# move's location. Example:
# Rule location: Stock
# Removal rule 1: Stock/Zone1
# Removal rule 2: Stock/Zone2
# If we have a stock.move with "Stock" as source location,
# it can use both rules.
# If we have a stock.move with "Stock/Zone2" as source location,
# it should never use "Stock/Zone1"
if not removal_rule.location_id.is_sublocation_of(location_id):
continue
quants = self.env["stock.quant"]._gather(
self.product_id,
removal_rule.location_id,
lot_id=lot_id,
package_id=forced_package_id,
owner_id=owner_id,
strict=strict,
)
# get quants allowed by the rule
rule_quants = removal_rule._filter_quants(self, quants)
if not rule_quants:
continue
# Apply the advanced removal strategy, if any. Even within the
# application of the removal strategy, the original company's
# one should be respected (eg. if we remove quants that would
# empty bins first, in case of equality, we should remove the
# fifo or fefo first depending of the configuration).
strategy = removal_rule._apply_strategy(rule_quants)
next(strategy)
while True:
try:
next_quant = strategy.send(still_need)
if not next_quant:
continue
location, location_quantity, to_take = 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,
package_id=package_id,
owner_id=owner_id,
strict=strict,
)
still_need -= taken_in_loc
except StopIteration:
break
need_zero = (
float_compare(still_need, 0, precision_rounding=rounding) != 1
)
if need_zero:
# useless to eval the other rules when still_need <= 0
break
reserved = need - still_need
return reserved
# We fall here if there is no rule or they have all been
# excluded by 'rule._is_rule_applicable'
return super()._update_reserved_quantity(
need,
available_quantity,
location_id=location_id,
lot_id=lot_id,
package_id=package_id,
owner_id=owner_id,
strict=strict,
)