diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 2cb44472f36..5e4fb1ff29c 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -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):