mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[FIX] stock_vertical_lift: handles multiple move lines on pick
If there is two move lines for the same product in the vertical lift (stored in2 differents trays for instance), the pick scenario was failing when the user was processing the first line. To circumvent this, instead of validating directly the move, we put the line in its own stock move, then we put the stock move in its own transfer and validate this one. Methods used to do that have been copied from the `shopfloor` module, they probably deserves their own module as they are quite generic.
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Vertical Lift",
|
"name": "Vertical Lift",
|
||||||
"summary": "Provides the core for integration with Vertical Lifts",
|
"summary": "Provides the core for integration with Vertical Lifts",
|
||||||
"version": "13.0.1.1.1",
|
"version": "13.0.1.1.2",
|
||||||
"category": "Stock",
|
"category": "Stock",
|
||||||
"author": "Camptocamp, Odoo Community Association (OCA)",
|
"author": "Camptocamp, Odoo Community Association (OCA)",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
|
|||||||
@@ -7,7 +7,27 @@
|
|||||||
<field name="product_id" ref="product_running_socks" />
|
<field name="product_id" ref="product_running_socks" />
|
||||||
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||||
<field name="inventory_id" ref="stock_inventory_vertical_lift_0" />
|
<field name="inventory_id" ref="stock_inventory_vertical_lift_0" />
|
||||||
<field name="product_qty">30.0</field>
|
<field name="product_qty">10.0</field>
|
||||||
|
<field
|
||||||
|
name="location_id"
|
||||||
|
ref="stock_location_vertical_lift_demo_tray_1b_x1y2"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
|
<record id="stock_inventory_vertical_lift_line_2" model="stock.inventory.line">
|
||||||
|
<field name="product_id" ref="product_running_socks" />
|
||||||
|
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||||
|
<field name="inventory_id" ref="stock_inventory_vertical_lift_0" />
|
||||||
|
<field name="product_qty">10.0</field>
|
||||||
|
<field
|
||||||
|
name="location_id"
|
||||||
|
ref="stock_location_vertical_lift_demo_tray_1b_x2y2"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
|
<record id="stock_inventory_vertical_lift_line_3" model="stock.inventory.line">
|
||||||
|
<field name="product_id" ref="product_running_socks" />
|
||||||
|
<field name="product_uom_id" ref="uom.product_uom_unit" />
|
||||||
|
<field name="inventory_id" ref="stock_inventory_vertical_lift_0" />
|
||||||
|
<field name="product_qty">10.0</field>
|
||||||
<field
|
<field
|
||||||
name="location_id"
|
name="location_id"
|
||||||
ref="stock_location_vertical_lift_demo_tray_1b_x3y2"
|
ref="stock_location_vertical_lift_demo_tray_1b_x3y2"
|
||||||
|
|||||||
@@ -125,6 +125,14 @@ msgstr ""
|
|||||||
msgid "Created by"
|
msgid "Created by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: stock_vertical_lift
|
||||||
|
#: code:addons/stock_vertical_lift/models/vertical_lift_operation_base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Created from backorder <a href=# data-oe-model=stock.picking data-oe-"
|
||||||
|
"id=%d>%s</a>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: stock_vertical_lift
|
#. module: stock_vertical_lift
|
||||||
#: model:ir.model.fields,field_description:stock_vertical_lift.field_vertical_lift_command__create_date
|
#: model:ir.model.fields,field_description:stock_vertical_lift.field_vertical_lift_command__create_date
|
||||||
#: model:ir.model.fields,field_description:stock_vertical_lift.field_vertical_lift_operation_inventory__create_date
|
#: model:ir.model.fields,field_description:stock_vertical_lift.field_vertical_lift_operation_inventory__create_date
|
||||||
|
|||||||
@@ -11,6 +11,79 @@ from odoo.addons.base_sparse_field.models.fields import Serialized
|
|||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# The following methods have been copied from 'shopfloor' module (OCA/wms)
|
||||||
|
# TODO: we should move them in a generic module
|
||||||
|
|
||||||
|
|
||||||
|
def split_other_move_lines(move, move_lines):
|
||||||
|
"""Substract `move_lines` from `move.move_line_ids`, put the result
|
||||||
|
in a new move and returns it.
|
||||||
|
"""
|
||||||
|
other_move_lines = move.move_line_ids - move_lines
|
||||||
|
if other_move_lines or move.state == "partially_available":
|
||||||
|
qty_to_split = move.product_uom_qty - sum(move_lines.mapped("product_uom_qty"))
|
||||||
|
backorder_move_id = move._split(qty_to_split)
|
||||||
|
backorder_move = move.browse(backorder_move_id)
|
||||||
|
backorder_move.move_line_ids = other_move_lines
|
||||||
|
backorder_move._recompute_state()
|
||||||
|
backorder_move._action_assign()
|
||||||
|
move._recompute_state()
|
||||||
|
return backorder_move
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def extract_and_action_done(move):
|
||||||
|
"""Extract the moves in a separate transfer and validate them.
|
||||||
|
|
||||||
|
You can combine this method with `split_other_move_lines` method
|
||||||
|
to first extract some move lines in a separate move, then validate it
|
||||||
|
with this method.
|
||||||
|
"""
|
||||||
|
# Put remaining qty to process from partially available moves
|
||||||
|
# in their own move (which will be then 'confirmed')
|
||||||
|
partial_moves = move.filtered(lambda m: m.state == "partially_available")
|
||||||
|
for partial_move in partial_moves:
|
||||||
|
partial_move.split_other_move_lines(partial_move.move_line_ids)
|
||||||
|
# Process assigned moves
|
||||||
|
moves = move.filtered(lambda m: m.state == "assigned")
|
||||||
|
if not moves:
|
||||||
|
return False
|
||||||
|
for picking in moves.picking_id:
|
||||||
|
moves_todo = picking.move_lines & moves
|
||||||
|
if moves_todo == picking.move_lines:
|
||||||
|
# No need to create a new transfer if we are processing all moves
|
||||||
|
new_picking = picking
|
||||||
|
else:
|
||||||
|
new_picking = picking.copy(
|
||||||
|
{
|
||||||
|
"name": "/",
|
||||||
|
"move_lines": [],
|
||||||
|
"move_line_ids": [],
|
||||||
|
"backorder_id": picking.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
new_picking.message_post(
|
||||||
|
body=_(
|
||||||
|
"Created from backorder "
|
||||||
|
"<a href=# data-oe-model=stock.picking data-oe-id=%d>%s</a>."
|
||||||
|
)
|
||||||
|
% (picking.id, picking.name)
|
||||||
|
)
|
||||||
|
moves_todo.write({"picking_id": new_picking.id})
|
||||||
|
moves_todo.package_level_id.write({"picking_id": new_picking.id})
|
||||||
|
moves_todo.move_line_ids.write({"picking_id": new_picking.id})
|
||||||
|
moves_todo.move_line_ids.package_level_id.write(
|
||||||
|
{"picking_id": new_picking.id}
|
||||||
|
)
|
||||||
|
new_picking.action_assign()
|
||||||
|
assert new_picking.state == "assigned"
|
||||||
|
new_picking.action_done()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# /methods
|
||||||
|
|
||||||
|
|
||||||
class VerticalLiftOperationBase(models.AbstractModel):
|
class VerticalLiftOperationBase(models.AbstractModel):
|
||||||
"""Base model for shuttle operations (pick, put, inventory)"""
|
"""Base model for shuttle operations (pick, put, inventory)"""
|
||||||
|
|
||||||
@@ -412,7 +485,9 @@ class VerticalLiftOperationTransfer(models.AbstractModel):
|
|||||||
line = self.current_move_line_id
|
line = self.current_move_line_id
|
||||||
if line.state in ("assigned", "partially_available"):
|
if line.state in ("assigned", "partially_available"):
|
||||||
line.qty_done = line.product_qty
|
line.qty_done = line.product_qty
|
||||||
line.move_id._action_done()
|
# if the move has other move lines, it is split to have only this move line
|
||||||
|
split_other_move_lines(line.move_id, line)
|
||||||
|
extract_and_action_done(line.move_id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def fetch_tray(self):
|
def fetch_tray(self):
|
||||||
|
|||||||
@@ -154,10 +154,11 @@ class VerticalLiftCase(common.LocationTrayTypeCase):
|
|||||||
inventory.action_start()
|
inventory.action_start()
|
||||||
return inventory
|
return inventory
|
||||||
|
|
||||||
def _test_button_release(self, move_line, expected_state):
|
def _test_button_release(self, move_lines, expected_state):
|
||||||
# for the test, we'll consider our last line has been delivered
|
# for the test, we'll consider all the lines has been delivered
|
||||||
move_line.qty_done = move_line.product_qty
|
for move_line in move_lines:
|
||||||
move_line.move_id._action_done()
|
move_line.qty_done = move_line.product_qty
|
||||||
|
move_lines.picking_id.action_done()
|
||||||
# release, no further operation in queue
|
# release, no further operation in queue
|
||||||
operation = self.shuttle._operation_for_mode()
|
operation = self.shuttle._operation_for_mode()
|
||||||
# the release button can be used only in the state... release
|
# the release button can be used only in the state... release
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class TestPick(VerticalLiftCase):
|
|||||||
)
|
)
|
||||||
# we have a move line to pick created by demo picking
|
# we have a move line to pick created by demo picking
|
||||||
# stock_picking_out_demo_vertical_lift_1
|
# stock_picking_out_demo_vertical_lift_1
|
||||||
cls.out_move_line = cls.picking_out.move_line_ids
|
cls.out_move_line = cls.picking_out.move_line_ids[0]
|
||||||
|
|
||||||
def test_switch_pick(self):
|
def test_switch_pick(self):
|
||||||
self.shuttle.switch_pick()
|
self.shuttle.switch_pick()
|
||||||
@@ -159,7 +159,7 @@ class TestPick(VerticalLiftCase):
|
|||||||
|
|
||||||
def test_button_release(self):
|
def test_button_release(self):
|
||||||
self._open_screen("pick")
|
self._open_screen("pick")
|
||||||
self._test_button_release(self.out_move_line, "noop")
|
self._test_button_release(self.picking_out.move_line_ids, "noop")
|
||||||
|
|
||||||
def test_process_current_pick(self):
|
def test_process_current_pick(self):
|
||||||
operation = self._open_screen("pick")
|
operation = self._open_screen("pick")
|
||||||
@@ -183,7 +183,7 @@ class TestPick(VerticalLiftCase):
|
|||||||
# fmt: off
|
# fmt: off
|
||||||
'cells': [
|
'cells': [
|
||||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
[0, 0, 1, 0, 0, 0, 0, 0],
|
[1, 1, 1, 0, 0, 0, 0, 0],
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user