Skip to content

Commit

Permalink
[FIX] shopfloor: change lot
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaudoux committed Jan 3, 2024
1 parent a5062fc commit f7c397d
Showing 1 changed file with 84 additions and 65 deletions.
149 changes: 84 additions & 65 deletions shopfloor/actions/change_package_lot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
# Copyright 2024 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, exceptions
from odoo.tools.float_utils import float_compare, float_is_zero
Expand Down Expand Up @@ -63,107 +64,125 @@ def is_lesser(value, other, rounding):

inventory = self._actions_for("inventory")
product = move_line.product_id
rounding = product.uom_id.rounding
if lot.product_id != product:
return response_error_func(
move_line, message=self.msg_store.lot_on_wrong_product(lot.name)
)
previous_lot = move_line.lot_id
# Changing the lot on the move line updates the reservation on the quants

message_parts = []

# Changing the lot on the move line updates the reservation on the quants
values = {"lot_id": lot.id}

available_quantity = self.env["stock.quant"]._get_available_quantity(
product, move_line.location_id, lot_id=lot, strict=True
)

if move_line.package_id:
move_line.package_level_id.explode_package()
values["package_id"] = False

to_assign_moves = self.env["stock.move"]
if float_is_zero(
available_quantity, precision_rounding=product.uom_id.rounding
):
quants = self.env["stock.quant"]._gather(
product, move_line.location_id, lot_id=lot, strict=True
quants = (
self.env["stock.quant"]
._gather(product, move_line.location_id, lot_id=lot, strict=True)
.filtered(
lambda q: float_compare(q.quantity, 0, precision_rounding=rounding) > 0
)
if quants:
# we have quants but they are all reserved by other lines:
# unreserve the other lines and reserve them again after
unreservable_lines = self.env["stock.move.line"].search(
)

available_quantity = 0
if quants:
for quant in quants:
quant_available = quant.quantity - quant.reserved_quantity
if float_compare(quant_available, 0, precision_rounding=rounding) > 0:
available_quantity += quant_available

if is_lesser(available_quantity, move_line.reserved_qty, rounding):
# We switch reservations with other move lines that have not
# yet been processed
other_lines = self.env["stock.move.line"].search(
[
("lot_id", "=", lot.id),
("product_id", "=", product.id),
("location_id", "=", move_line.location_id.id),
("state", "in", ("partially_available", "assigned")),
("reserved_uom_qty", ">=", 0),
("qty_done", "=", 0),
]
)
if not unreservable_lines:
return response_error_func(
move_line,
message=self.msg_store.cannot_change_lot_already_picked(lot),
)
available_quantity = sum(unreservable_lines.mapped("reserved_qty"))
to_assign_moves = unreservable_lines.move_id
# if we leave the package level, it will try to reserve the same
# one again
unreservable_lines.package_level_id.explode_package()
# unreserve qties of other lines
unreservable_lines.unlink()
else:
# * we have *no* quant:
# The lot is not found at all, but the user scanned it, which means
# it's an error in the stock data! To allow the user to continue,
# we post an inventory to add the missing quantity, and a second
# draft inventory to check later
inventory.create_stock_correction(
move_line.move_id,
move_line.location_id,
self.env["stock.quant.package"].browse(),
lot,
move_line.reserved_qty,
)
inventory.create_control_stock(
move_line.location_id,
move_line.product_id,
move_line.package_id,
move_line.lot_id,
_("Pick: stock issue on lot: {} found in {}").format(
lot.name, move_line.location_id.name
),
)
message_parts.append(
_("A draft inventory has been created for control.")
],
order="reserved_uom_qty desc",
)
to_reassign_moves = self.env["stock.move"]
other_available = 0
# Favor lines from non-printed pickings. Stop when required
# quantity is reached
for line in other_lines:
if (
line.picking_id != move_line.picking_id
and line.picking_id.printed
):
# don't modify other printed picking
other_available += line.reserved_qty
continue
if line.move_id.quantity_done:
# don't modify if related move has a quantity done
other_available += line.reserved_qty
continue
available_quantity += line.reserved_qty
to_reassign_moves |= line.move_id
# if we leave the package level, it will try to reserve the same
# one again
line.package_level_id.explode_package()
# unreserve qties of other lines
line.unlink()
if (
float_compare(
available_quantity,
move_line.reserved_qty,
precision_rounding=rounding,
)
>= 0
):
# We reached the required quantity
break
else:
# we still lack quantity
available_quantity += other_available

if float_is_zero(available_quantity, precision_rounding=rounding):
# The lot is not found at all, but the user scanned it, which means
# it's an error in the stock data!
message = self.msg_store.cannot_change_lot_already_picked(lot)
# We post an draft inventory to control the stock
inventory.create_control_stock(
move_line.location_id,
move_line.product_id,
move_line.package_id,
move_line.lot_id,
_("Pick: stock issue on lot: {} found in {}").format(
lot.name, move_line.location_id.name
),
)
return response_error_func(move_line, message=message)

# re-evaluate float_is_zero because we may have changed available_quantity
if not float_is_zero(
available_quantity, precision_rounding=product.uom_id.rounding
) and is_lesser(
available_quantity, move_line.reserved_qty, product.uom_id.rounding
):
if is_lesser(available_quantity, move_line.reserved_qty, rounding):
new_uom_qty = product.uom_id._compute_quantity(
available_quantity, move_line.product_uom_id, rounding_method="HALF-UP"
)
values["reserved_uom_qty"] = new_uom_qty

move_line.write(values)

if "reserved_uom_qty" in values:
message_parts.append(_("The quantity to do has changed!"))
move_line.write(values)
# when we change the quantity of the move, the state
# will still be "assigned" and be skipped by "_action_assign",
# recompute the state to be "partially_available"
move_line.move_id._recompute_state()
else:
move_line.write(values)

# if the new package has less quantities, assign will create new move
# lines
move_line.move_id._action_assign()

# Find other available goods for the lines which were using the
# lot before...
to_assign_moves._action_assign()
if to_reassign_moves:
to_reassign_moves._action_assign()

message = self.msg_store.lot_replaced_by_lot(previous_lot, lot)
if message_parts:
Expand Down

0 comments on commit f7c397d

Please sign in to comment.