[FIX] stock: Allow better overriding of _action_assign

This commit aims to make easier the overriding of _action_assign by extracting the inner function that retrieves available moves out of it and splitting it in two parts to be able to have a better control on the available moves in and the available moves out.

It is currently not possible to modify the way _action_assign get available moves to create new stock move lines due to the fact that the algorithm is an inner function.

We are currently making a custo that uses consumables and need to change the reserved quantity based on some conditions therefore, the function returns every done stock move line in previous picking and get the wrong result in the end.

For example, let's say we have 3 PACK pickings containing 2, 1 and 3 units of a consumable product respectively. Let the two first PACKS be done, so the OUT picking contains two stock move line, one with 2 units, one with 1 unit. When confirming the last PACK, instead of getting a new stock move line with 3 units, we get two lines, one with 2 units and one with 1 unit because the function looped over all the done stock moves without taking quants into account.

This behaviour is correct but cannot be overriden properly as of now apart from copying the whole function and change what we need.

closes odoo/odoo#110787

X-original-commit: b3d869a3a1ef7591ab846e347f261518fc09f12d
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
This commit is contained in:
alcl-odoo
2023-01-24 10:20:50 +01:00
committed by Arnold Moyaux
parent c420d87af7
commit 95ce7a3003
+51 -41
View File
@@ -1509,51 +1509,61 @@ Please change the quantity done or the rounding precision of your unit of measur
return self.product_qty
return self.env['stock.quant']._get_available_quantity(self.product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict, allow_negative=allow_negative)
def _get_available_move_lines_in(self):
move_lines_in = self.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids')
def _keys_in_groupby(ml):
return (ml.location_dest_id, ml.lot_id, ml.result_package_id, ml.owner_id)
grouped_move_lines_in = {}
for k, g in groupby(move_lines_in, key=_keys_in_groupby):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_in[k] = qty_done
return grouped_move_lines_in
def _get_available_move_lines_out(self, assigned_moves_ids, partially_available_moves_ids):
move_lines_out_done = (self.move_orig_ids.mapped('move_dest_ids') - self)\
.filtered(lambda m: m.state in ['done'])\
.mapped('move_line_ids')
# As we defer the write on the stock.move's state at the end of the loop, there
# could be moves to consider in what our siblings already took.
StockMove = self.env['stock.move']
moves_out_siblings = self.move_orig_ids.mapped('move_dest_ids') - self
moves_out_siblings_to_consider = moves_out_siblings & (StockMove.browse(assigned_moves_ids) + StockMove.browse(partially_available_moves_ids))
reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned'])
move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids')
def _keys_out_groupby(ml):
return (ml.location_id, ml.lot_id, ml.package_id, ml.owner_id)
grouped_move_lines_out = {}
for k, g in groupby(move_lines_out_done, key=_keys_out_groupby):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_out[k] = qty_done
for k, g in groupby(move_lines_out_reserved, key=_keys_out_groupby):
grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('reserved_qty'))
return grouped_move_lines_out
def _get_available_move_lines(self, assigned_moves_ids, partially_available_moves_ids):
grouped_move_lines_in = self._get_available_move_lines_in()
grouped_move_lines_out = self._get_available_move_lines_out(assigned_moves_ids, partially_available_moves_ids)
available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in}
# pop key if the quantity available amount to 0
rounding = self.product_id.uom_id.rounding
return dict((k, v) for k, v in available_move_lines.items() if float_compare(v, 0, precision_rounding=rounding) > 0)
def _action_assign(self, force_qty=False):
""" Reserve stock moves by creating their stock move lines. A stock move is
considered reserved once the sum of `reserved_qty` for all its move lines is
equal to its `product_qty`. If it is less, the stock move is considered
partially available.
"""
def _get_available_move_lines(move):
move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids')
def _keys_in_groupby(ml):
return (ml.location_dest_id, ml.lot_id, ml.result_package_id, ml.owner_id)
grouped_move_lines_in = {}
for k, g in groupby(move_lines_in, key=_keys_in_groupby):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_in[k] = qty_done
move_lines_out_done = (move.move_orig_ids.mapped('move_dest_ids') - move)\
.filtered(lambda m: m.state in ['done'])\
.mapped('move_line_ids')
# As we defer the write on the stock.move's state at the end of the loop, there
# could be moves to consider in what our siblings already took.
moves_out_siblings = move.move_orig_ids.mapped('move_dest_ids') - move
moves_out_siblings_to_consider = moves_out_siblings & (StockMove.browse(assigned_moves_ids) + StockMove.browse(partially_available_moves_ids))
reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned'])
move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids')
def _keys_out_groupby(ml):
return (ml.location_id, ml.lot_id, ml.package_id, ml.owner_id)
grouped_move_lines_out = {}
for k, g in groupby(move_lines_out_done, key=_keys_out_groupby):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_out[k] = qty_done
for k, g in groupby(move_lines_out_reserved, key=_keys_out_groupby):
grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*g).mapped('reserved_qty'))
available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in}
# pop key if the quantity available amount to 0
rounding = move.product_id.uom_id.rounding
return dict((k, v) for k, v in available_move_lines.items() if float_compare(v, 0, precision_rounding=rounding) > 0)
StockMove = self.env['stock.move']
assigned_moves_ids = OrderedSet()
partially_available_moves_ids = OrderedSet()
@@ -1579,7 +1589,7 @@ Please change the quantity done or the rounding precision of your unit of measur
if move._should_bypass_reservation():
# create the move line(s) but do not impact quants
if move.move_orig_ids:
available_move_lines = _get_available_move_lines(move)
available_move_lines = move._get_available_move_lines(assigned_moves_ids, partially_available_moves_ids)
for (location_id, lot_id, package_id, owner_id), quantity in available_move_lines.items():
qty_added = min(missing_reserved_quantity, quantity)
move_line_vals = move._prepare_move_line_vals(qty_added)
@@ -1642,7 +1652,7 @@ Please change the quantity done or the rounding precision of your unit of measur
# `qty_done` is in `ml.product_uom_id` and, as we will later increase
# the reserved quantity on the quants, convert it here in
# `product_id.uom_id` (the UOM of the quants is the UOM of the product).
available_move_lines = _get_available_move_lines(move)
available_move_lines = move._get_available_move_lines(assigned_moves_ids, partially_available_moves_ids)
if not available_move_lines:
continue
for move_line in move.move_line_ids.filtered(lambda m: m.reserved_qty):