[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:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user