Last active
June 23, 2022 08:50
-
-
Save gdgellatly/3894ef4637877bd61991f99c6870fdb9 to your computer and use it in GitHub Desktop.
Unreserve during update_reserved_quantity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2022 Graeme Gellatly | |
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | |
from odoo import api, models | |
from odoo.tools.float_utils import float_compare | |
class StockQuant(models.Model): | |
_inherit = "stock.quant" | |
@api.model | |
def _update_reserved_quantity( | |
self, | |
product_id, | |
location_id, | |
quantity, | |
lot_id=None, | |
package_id=None, | |
owner_id=None, | |
strict=False, | |
): | |
"""Override to fix reservation errors before updating reserved quantity""" | |
self = self.sudo() | |
rounding = product_id.uom_id.rounding | |
quants = self._gather( | |
product_id, | |
location_id, | |
lot_id=lot_id, | |
package_id=package_id, | |
owner_id=owner_id, | |
strict=strict, | |
) | |
if float_compare(quantity, 0, precision_rounding=rounding) < 0: | |
# if we want to unreserve | |
available_quantity = sum(quants.mapped("reserved_quantity")) | |
if ( | |
float_compare( | |
abs(quantity), available_quantity, precision_rounding=rounding | |
) | |
> 0 | |
): | |
if quants: | |
quantity = 0 if quants._fix_unreserve() else quantity | |
else: | |
# Handle the case of quant already cycled out | |
move_lines = self.env["stock.move.line"].search( | |
[ | |
("product_id", "=", product_id), | |
("location_id", "=", location_id), | |
("lot_id", "=", lot_id), | |
("package_id", "=", package_id), | |
("owner_id", "=", owner_id), | |
("product_qty", "!=", 0), | |
] | |
) | |
move_lines.write({"product_uom_qty": 0}) | |
quantity = 0 | |
return super()._update_reserved_quantity( | |
product_id, | |
location_id, | |
quantity, | |
lot_id=lot_id, | |
package_id=package_id, | |
owner_id=owner_id, | |
strict=strict, | |
) | |
def _fix_unreserve(self): | |
all_log = "" | |
line_qty_updated = False | |
def gen_log_message(msg): | |
logging = ( | |
"Problematic quant found %s in %s: %s " | |
"(quantity: %s, reserved_quantity: %s)\n" | |
% ( | |
quant.product_id.name, | |
quant.location_id.complete_name, | |
quant.id, | |
quant.quantity, | |
quant.reserved_quantity, | |
) | |
) | |
logging += msg | |
logging += "******************\n" | |
return logging | |
move_line_to_recompute_ids = [] | |
for quant in self: | |
move_lines = self.env["stock.move.line"].search( | |
[ | |
("product_id", "=", quant.product_id.id), | |
("location_id", "=", quant.location_id.id), | |
("lot_id", "=", quant.lot_id.id), | |
("package_id", "=", quant.package_id.id), | |
("owner_id", "=", quant.owner_id.id), | |
("product_qty", "!=", 0), | |
] | |
) | |
reserved_on_move_lines = sum(move_lines.mapped("product_uom_qty")) | |
move_line_str = str.join( | |
", ", [str(move_line_id) for move_line_id in move_lines.ids] | |
) | |
if quant.location_id.should_bypass_reservation(): | |
# If a quant is in a location that should bypass | |
# the reservation, its `reserved_quantity` field | |
# should be 0. | |
if quant.reserved_quantity != 0: | |
msg = ( | |
"its `reserved_quantity` field is not 0 while its " | |
"location should bypass the reservation\n" | |
) | |
if move_lines: | |
msg += ( | |
"These move lines are reserved on it: " | |
"%s (sum of the reservation: %s)\n" | |
% (move_line_str, reserved_on_move_lines) | |
) | |
else: | |
msg += ( | |
"no move lines are reserved on it, you can " | |
"safely reset its `reserved_quantity` to 0\n" | |
) | |
all_log += gen_log_message(msg) | |
quant.write({"reserved_quantity": 0}) | |
else: | |
# If a quant is in a reservable location, its `reserved_quantity` | |
# should be exactly the sum of the `product_qty` of all the | |
# partially_available / assigned move lines with the same | |
# characteristics. | |
if quant.reserved_quantity == 0: | |
if move_lines: | |
all_log += gen_log_message( | |
"its `reserved_quantity` field is 0 while these move lines " | |
"are reserved on it: %s (sum of the reservation: %s)\n" | |
% (move_line_str, reserved_on_move_lines) | |
) | |
move_lines.with_context( | |
bypass_reservation_update=True | |
).sudo().write({"product_uom_qty": 0}) | |
line_qty_updated = True | |
move_line_to_recompute_ids += move_lines.ids | |
elif quant.reserved_quantity < 0: | |
msg = "reserved_quantity field is negative\n" | |
quant.write({"reserved_quantity": 0}) | |
if move_lines: | |
msg += ( | |
"These move lines are reserved on it: " | |
"%s (sum of the reservation: %s)\n" | |
% (move_line_str, reserved_on_move_lines) | |
) | |
move_lines.with_context( | |
bypass_reservation_update=True | |
).sudo().write({"product_uom_qty": 0}) | |
line_qty_updated = True | |
move_line_to_recompute_ids += move_lines.ids | |
all_log += gen_log_message(msg) | |
else: | |
if reserved_on_move_lines != quant.reserved_quantity: | |
msg = "reserved_quantity does not reflect the reservations\n" | |
msg += ( | |
"These move lines are reserved on it: " | |
"%s (sum of the reservation: %s)\n" | |
% (move_line_str, reserved_on_move_lines) | |
) | |
all_log += gen_log_message(msg) | |
move_lines.with_context( | |
bypass_reservation_update=True | |
).sudo().write({"product_uom_qty": 0}) | |
line_qty_updated = True | |
move_line_to_recompute_ids += move_lines.ids | |
quant.write({"reserved_quantity": 0}) | |
else: | |
if any(move_line.product_qty < 0 for move_line in move_lines): | |
msg = ( | |
"reserved_quantity correctly reflects the move lines " | |
"reservation but some are negatives\n" | |
) | |
msg += ( | |
"These move lines are reserved on it: " | |
"%s (sum of the reservation: %s)\n" | |
% (move_line_str, reserved_on_move_lines) | |
) | |
all_log += gen_log_message(msg) | |
move_lines.with_context( | |
bypass_reservation_update=True | |
).sudo().write({"product_uom_qty": 0}) | |
line_qty_updated = True | |
move_line_to_recompute_ids += move_lines.ids | |
quant.write({"reserved_quantity": 0}) | |
self._create_unreserve_log_entry(all_log) | |
if move_line_to_recompute_ids: | |
self.env["stock.move.line"].browse( | |
move_line_to_recompute_ids | |
).move_id._recompute_state() | |
return line_qty_updated | |
def _create_unreserve_log_entry(self, logging): | |
if logging: | |
self.env["ir.logging"].sudo().create( | |
{ | |
"name": "Unreserve stock.quant and stock.move.line", | |
"type": "server", | |
"level": "INFO", | |
"dbname": self.env.cr.dbname, | |
"message": logging, | |
"func": "_update_reserved_quantity", | |
"path": "addons/stock/models/stock_quant.py", | |
"line": "0", | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment