mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
276 lines
9.9 KiB
Python
276 lines
9.9 KiB
Python
# Copyright 2019 Camptocamp SA
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.tools import float_compare
|
|
|
|
from odoo.addons.base_sparse_field.models.fields import Serialized
|
|
|
|
# TODO handle autofocus + easy way to validate for the input field
|
|
|
|
|
|
class VerticalLiftOperationInventory(models.Model):
|
|
_name = "vertical.lift.operation.inventory"
|
|
_inherit = "vertical.lift.operation.base"
|
|
_description = "Vertical Lift Operation Inventory"
|
|
|
|
_initial_state = "noop"
|
|
|
|
def _selection_states(self):
|
|
return [
|
|
("noop", "No inventory in progress"),
|
|
("quantity", "Inventory, please enter the amount"),
|
|
("confirm_wrong_quantity", "The quantity does not match, are you sure?"),
|
|
# save is never visible, but save and go to the next or noop directly
|
|
("save", "Save"),
|
|
# no need for release and save button here?
|
|
# ("release", "Release"),
|
|
]
|
|
|
|
def _transitions(self):
|
|
return (
|
|
self.Transition(
|
|
"noop",
|
|
"quantity",
|
|
# transition only if inventory lines are found
|
|
lambda self: self.select_next_inventory_line(),
|
|
),
|
|
self.Transition(
|
|
"quantity", "save", lambda self: self._has_identical_quantity(),
|
|
),
|
|
self.Transition(
|
|
"quantity",
|
|
"confirm_wrong_quantity",
|
|
lambda self: self._start_confirm_wrong_quantity(),
|
|
),
|
|
self.Transition(
|
|
"confirm_wrong_quantity",
|
|
"save",
|
|
lambda self: self.quantity_input == self.last_quantity_input,
|
|
),
|
|
# if the confirmation of the quantity is different, cycle back to
|
|
# the 'quantity' step
|
|
self.Transition(
|
|
"confirm_wrong_quantity",
|
|
"quantity",
|
|
lambda self: self._go_back_to_quantity_input(),
|
|
),
|
|
# go to quantity if we have lines in queue, otherwise, go to noop
|
|
self.Transition(
|
|
"save",
|
|
"quantity",
|
|
lambda self: self.process_current()
|
|
and self.select_next_inventory_line(),
|
|
# when we reach 'save', this transition is directly
|
|
# evaluated
|
|
direct_eval=True,
|
|
),
|
|
self.Transition(
|
|
"save",
|
|
"noop",
|
|
lambda self: self.process_current()
|
|
and self.clear_current_inventory_line(),
|
|
# when we reach 'save', this transition is directly
|
|
# evaluated
|
|
direct_eval=True,
|
|
),
|
|
)
|
|
|
|
current_inventory_line_id = fields.Many2one(
|
|
comodel_name="stock.inventory.line", readonly=True
|
|
)
|
|
|
|
quantity_input = fields.Float()
|
|
# if the quantity is wrong, user has to write 2 times
|
|
# the same quantity to really confirm it's correct
|
|
last_quantity_input = fields.Float()
|
|
|
|
tray_location_id = fields.Many2one(
|
|
comodel_name="stock.location",
|
|
compute="_compute_tray_data",
|
|
string="Tray Location",
|
|
)
|
|
tray_name = fields.Char(compute="_compute_tray_data", string="Tray Name")
|
|
tray_type_id = fields.Many2one(
|
|
comodel_name="stock.location.tray.type",
|
|
compute="_compute_tray_data",
|
|
string="Tray Type",
|
|
)
|
|
tray_type_code = fields.Char(compute="_compute_tray_data", string="Tray Code")
|
|
tray_x = fields.Integer(string="X", compute="_compute_tray_data")
|
|
tray_y = fields.Integer(string="Y", compute="_compute_tray_data")
|
|
tray_matrix = Serialized(string="Cells", compute="_compute_tray_data")
|
|
tray_qty = fields.Float(string="Stock Quantity", compute="_compute_tray_qty")
|
|
|
|
# current operation information
|
|
inventory_id = fields.Many2one(
|
|
related="current_inventory_line_id.inventory_id", readonly=True
|
|
)
|
|
product_id = fields.Many2one(
|
|
related="current_inventory_line_id.product_id", readonly=True
|
|
)
|
|
product_uom_id = fields.Many2one(
|
|
related="current_inventory_line_id.product_uom_id", readonly=True
|
|
)
|
|
product_qty = fields.Float(
|
|
related="current_inventory_line_id.product_qty", readonly=True
|
|
)
|
|
product_packagings = fields.Html(
|
|
string="Packaging", compute="_compute_product_packagings"
|
|
)
|
|
package_id = fields.Many2one(
|
|
related="current_inventory_line_id.package_id", readonly=True
|
|
)
|
|
lot_id = fields.Many2one(
|
|
related="current_inventory_line_id.prod_lot_id", readonly=True
|
|
)
|
|
|
|
@api.depends("current_inventory_line_id")
|
|
def _compute_tray_data(self):
|
|
for record in self:
|
|
location = record.current_inventory_line_id.location_id
|
|
tray_type = location.location_id.tray_type_id
|
|
# this is the current cell
|
|
record.tray_location_id = location.id
|
|
# name of the tray where the cell is
|
|
record.tray_name = location.location_id.name
|
|
record.tray_type_id = tray_type.id
|
|
record.tray_type_code = tray_type.code
|
|
record.tray_x = location.posx
|
|
record.tray_y = location.posy
|
|
record.tray_matrix = location.tray_matrix
|
|
|
|
@api.depends("current_inventory_line_id.product_id.packaging_ids")
|
|
def _compute_product_packagings(self):
|
|
for record in self:
|
|
if not record.current_inventory_line_id:
|
|
record.product_packagings = ""
|
|
continue
|
|
product = record.current_inventory_line_id.product_id
|
|
content = self._render_product_packagings(product)
|
|
record.product_packagings = content
|
|
|
|
@api.depends("tray_location_id", "current_inventory_line_id.product_id")
|
|
def _compute_tray_qty(self):
|
|
for record in self:
|
|
if not (record.tray_location_id and record.current_inventory_line_id):
|
|
record.tray_qty = 0.0
|
|
continue
|
|
product = record.current_inventory_line_id.product_id
|
|
location = record.tray_location_id
|
|
record.tray_qty = self._get_tray_qty(product, location)
|
|
|
|
def _compute_number_of_ops(self):
|
|
for record in self:
|
|
line_model = self.env["stock.inventory.line"]
|
|
record.number_of_ops = line_model.search_count(
|
|
self._domain_inventory_lines_to_do()
|
|
)
|
|
|
|
def _compute_number_of_ops_all(self):
|
|
for record in self:
|
|
line_model = self.env["stock.inventory.line"]
|
|
record.number_of_ops_all = line_model.search_count(
|
|
self._domain_inventory_lines_to_do_all()
|
|
)
|
|
|
|
def _domain_inventory_lines_to_do(self):
|
|
return [
|
|
("location_id", "child_of", self.location_id.id),
|
|
("state", "=", "confirm"),
|
|
("vertical_lift_done", "=", False),
|
|
]
|
|
|
|
def _domain_inventory_lines_to_do_all(self):
|
|
shuttle_locations = self.env["stock.location"].search(
|
|
[("vertical_lift_kind", "=", "view")]
|
|
)
|
|
return [
|
|
("location_id", "child_of", shuttle_locations.ids),
|
|
("state", "=", "confirm"),
|
|
("vertical_lift_done", "=", False),
|
|
]
|
|
|
|
def reset_steps(self):
|
|
self.clear_current_inventory_line()
|
|
super().reset_steps()
|
|
|
|
def _has_identical_quantity(self):
|
|
line = self.current_inventory_line_id
|
|
return (
|
|
float_compare(
|
|
line.theoretical_qty,
|
|
self.quantity_input,
|
|
precision_rounding=line.product_uom_id.rounding,
|
|
)
|
|
== 0
|
|
)
|
|
|
|
def _start_confirm_wrong_quantity(self):
|
|
self.last_quantity_input = self.quantity_input
|
|
self.quantity_input = 0.0
|
|
return True
|
|
|
|
def _go_back_to_quantity_input(self):
|
|
self.last_quantity_input = self.quantity_input
|
|
self.quantity_input = 0.0
|
|
return True
|
|
|
|
def clear_current_inventory_line(self):
|
|
self.write(
|
|
{
|
|
"quantity_input": 0.0,
|
|
"last_quantity_input": 0.0,
|
|
"current_inventory_line_id": False,
|
|
}
|
|
)
|
|
return True
|
|
|
|
def fetch_tray(self):
|
|
location = self.current_inventory_line_id.location_id
|
|
location.fetch_vertical_lift_tray()
|
|
|
|
def select_next_inventory_line(self):
|
|
self.ensure_one()
|
|
previous_line = self.current_inventory_line_id
|
|
next_line = self.env["stock.inventory.line"].search(
|
|
self._domain_inventory_lines_to_do(),
|
|
limit=1,
|
|
order="vertical_lift_tray_id, location_id, id",
|
|
)
|
|
self.current_inventory_line_id = next_line
|
|
if (
|
|
next_line
|
|
and previous_line.vertical_lift_tray_id != next_line.vertical_lift_tray_id
|
|
):
|
|
self.fetch_tray()
|
|
return bool(next_line)
|
|
|
|
def process_current(self):
|
|
line = self.current_inventory_line_id
|
|
if not line.vertical_lift_done:
|
|
line.vertical_lift_done = True
|
|
if self.quantity_input != line.product_qty:
|
|
line.product_qty = self.quantity_input
|
|
inventory = line.inventory_id
|
|
if all(line.vertical_lift_done for line in inventory.line_ids):
|
|
inventory.action_validate()
|
|
self.quantity_input = self.last_quantity_input = 0.0
|
|
return True
|
|
|
|
def button_save(self):
|
|
self.ensure_one()
|
|
if not self.step() in ("quantity", "confirm_wrong_quantity"):
|
|
return
|
|
self.next_step()
|
|
if self.step() == "noop":
|
|
# sorry not sorry
|
|
return {
|
|
"effect": {
|
|
"fadeout": "slow",
|
|
"message": _("Congrats, you cleared the queue!"),
|
|
"img_url": "/web/static/src/img/smile.svg",
|
|
"type": "rainbow_man",
|
|
}
|
|
}
|