Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14][IMP] shopfloor: Ensure destination location on moves for allow move create #973

Open
wants to merge 2 commits into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions shopfloor/actions/stock.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
# Copyright 2025 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, fields
from odoo.tools.float_utils import float_round
Expand Down Expand Up @@ -228,3 +229,26 @@ def no_putaway_available(self, picking_types, move_lines):
# when no putaway is found, the move line destination stays the
# default's of the picking type
return any(line.location_dest_id in base_locations for line in move_lines)

def _lock_lines(self, lines):
self._actions_for("lock").for_update(lines)

def _set_destination_on_lines(self, lines, location_dest):
# when writing the destination on the package level, it writes
# on the moves and move lines
lines_with_package_level = lines.package_level_id.move_line_ids
lines_without_package_level = lines - lines_with_package_level
if lines_with_package_level:
lines_with_package_level.package_level_id.location_dest_id = location_dest
mt-software-de marked this conversation as resolved.
Show resolved Hide resolved
if lines_without_package_level:
lines_without_package_level.location_dest_id = location_dest
lines_without_package_level.move_id.location_dest_id = location_dest

def _unload_package(self, lines):
lines.result_package_id = False

def set_destination_and_unload_lines(self, lines, location_dest, unload=False):
self._lock_lines(lines)
self._set_destination_on_lines(lines, location_dest)
if unload:
self._unload_package(lines)
15 changes: 6 additions & 9 deletions shopfloor/services/cluster_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,8 +1147,11 @@ def set_destination_all(self, picking_batch_id, barcode, confirmation=None):
return self._unload_end(batch, completion_info_popup=completion_info_popup)

def _unload_write_destination_on_lines(self, lines, location):
lines.write({"shopfloor_unloaded": True, "location_dest_id": location.id})
lines.package_level_id.location_dest_id = location
stock = self._actions_for("stock")
stock.set_destination_and_unload_lines(
lines, location, self.work.menu.unload_package_at_destination
)
lines.write({"shopfloor_unloaded": True})
for line in lines:
# We set the picking to done only when the last line is
# unloaded to avoid backorders.
Expand All @@ -1158,8 +1161,6 @@ def _unload_write_destination_on_lines(self, lines, location):
picking_lines = picking.mapped("move_line_ids")
if all(line.shopfloor_unloaded for line in picking_lines):
picking._action_done()
if self.work.menu.unload_package_at_destination:
lines.result_package_id = False

def _unload_end(self, batch, completion_info_popup=None):
"""Try to close the batch if all transfers are done.
Expand Down Expand Up @@ -1279,15 +1280,11 @@ def unload_scan_destination(
batch, package, lines, barcode, confirmation=confirmation
)

def _lock_lines(self, lines):
"""Lock move lines"""
self._actions_for("lock").for_update(lines)

def _unload_scan_destination_lines(
self, batch, package, lines, barcode, confirmation=None
):
# Lock move lines that will be updated
self._lock_lines(lines)
self._actions_for("lock").for_update(lines)
first_line = fields.first(lines)
scanned_location = self._actions_for("search").location_from_scan(barcode)
if not scanned_location:
Expand Down
5 changes: 2 additions & 3 deletions shopfloor/services/location_content_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,9 @@ def _find_transfer_move_lines(self, location):
)
return lines

# hook used in module shopfloor_checkout_sync
def _write_destination_on_lines(self, lines, location, package=None):
lines.location_dest_id = location
lines.package_level_id.location_dest_id = location
stock = self._actions_for("stock")
stock.set_destination_and_unload_lines(lines, location)
if package:
lines.result_package_id = package

Expand Down
7 changes: 4 additions & 3 deletions shopfloor/services/single_pack_transfer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2020-2021 Camptocamp SA (http://www.camptocamp.com)
# Copyright 2020-2021 Jacques-Etienne Baudoux (BCIM) <[email protected]>
# Copyright 2020 Akretion (http://www.akretion.com)
# Copyright 2025 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields

Expand Down Expand Up @@ -266,10 +267,10 @@ def _router_validate_success(self, package_level):
return self._response_for_start(message=message, popup=completion_info_popup)

def _set_destination_and_done(self, package_level, scanned_location):
# when writing the destination on the package level, it writes
# on the move lines
package_level.location_dest_id = scanned_location
stock = self._actions_for("stock")
stock.set_destination_and_unload_lines(
package_level.move_line_ids, scanned_location
)
stock.put_package_level_in_move(package_level)
stock.validate_moves(package_level.move_line_ids.move_id)

Expand Down
15 changes: 5 additions & 10 deletions shopfloor/services/zone_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ def _set_destination_package(self, move_line, quantity, package):
)
return (package_changed, response)
stock = self._actions_for("stock")
self._lock_lines(move_line)
stock._lock_lines(move_line)
try:
stock.mark_move_line_as_picked(
move_line, quantity, package, check_user=True
Expand Down Expand Up @@ -1591,11 +1591,10 @@ def set_destination_all(self, barcode, confirmation=None):
return self._set_destination_all_response(buffer_lines, message=message)

def _write_destination_on_lines(self, lines, location):
self._lock_lines(lines)
lines.location_dest_id = location
lines.package_level_id.location_dest_id = location
if self.work.menu.unload_package_at_destination:
lines.result_package_id = False
stock = self._actions_for("stock")
stock.set_destination_and_unload_lines(
lines, location, unload=self.work.menu.unload_package_at_destination
)

def unload_split(self):
"""Indicates that now the buffer must be treated line per line
Expand Down Expand Up @@ -1676,10 +1675,6 @@ def unload_scan_pack(self, package_id, barcode):
unload_single_message=self.msg_store.barcode_no_match(package.name),
)

def _lock_lines(self, lines):
"""Lock move lines"""
self._actions_for("lock").for_update(lines)

def unload_set_destination(self, package_id, barcode, confirmation=None):
"""Scan the final destination for move lines in the buffer with the
destination package
Expand Down
74 changes: 71 additions & 3 deletions shopfloor/tests/test_single_pack_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ def test_start_no_operation_create(self):
package_level = move_line.package_level_id

self.assertTrue(package_level.is_done)

self.assertEqual(move_line.location_id, self.pack_a.location_id)
self.assertEqual(move_line.move_id.location_id, self.pack_a.location_id)
expected_data = {
"id": package_level.id,
"name": package_level.package_id.name,
Expand All @@ -204,6 +205,60 @@ def test_start_no_operation_create(self):

self.assert_response(response, next_state="scan_location", data=expected_data)

def test_start_validate_no_operation_create(self):
self.menu.sudo().allow_move_create = True
self.picking.do_unreserve()
barcode = self.pack_a.name
params = {"barcode": barcode}

# Simulate the client scanning a package's barcode, which
# in turns should start the operation in odoo
response = self.service.dispatch("start", params=params)

move_line = self.env["stock.move.line"].search(
[("package_id", "=", self.pack_a.id)]
)
package_level = move_line.package_level_id

response = self.service.dispatch(
"validate",
params={
"package_level_id": package_level.id,
"location_barcode": self.shelf2.barcode,
},
)

self.assert_response(
response,
next_state="start",
message={
"message_type": "success",
"body": "The pack has been moved, you can scan a new pack.",
},
)

self.assertRecordValues(
package_level.move_line_ids,
[
{
"qty_done": 1.0,
"location_dest_id": self.shelf2.id,
"location_id": self.shelf1.id,
"state": "done",
}
],
)
self.assertRecordValues(
package_level.move_line_ids.move_id,
[
{
"location_dest_id": self.shelf2.id,
"location_id": self.shelf1.id,
"state": "done",
}
],
)

def test_start_barcode_not_known(self):
"""Test /start when the barcode is unknown

Expand Down Expand Up @@ -449,11 +504,24 @@ def test_validate(self):

self.assertRecordValues(
package_level.move_line_ids,
[{"qty_done": 1.0, "location_dest_id": self.shelf2.id, "state": "done"}],
[
{
"qty_done": 1.0,
"location_dest_id": self.shelf2.id,
"location_id": self.shelf1.id,
"state": "done",
}
],
)
self.assertRecordValues(
package_level.move_line_ids.move_id,
[{"location_dest_id": self.shelf2.id, "state": "done"}],
[
{
"location_dest_id": self.shelf2.id,
"location_id": self.shelf1.location_id.id,
"state": "done",
}
],
)

def test_validate_completion_info(self):
Expand Down
1 change: 0 additions & 1 deletion shopfloor_checkout_sync/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from . import actions
from . import services
1 change: 1 addition & 0 deletions shopfloor_checkout_sync/actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import checkout_sync
from . import stock
23 changes: 12 additions & 11 deletions shopfloor_checkout_sync/actions/checkout_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ def _has_to_sync_destination(self, lines):
# the sync has already been done
return any(line.location_dest_id.child_ids for line in lines)

def _all_lines_to_lock(self, lines):
def _all_moves(self, lines):
if self._has_to_sync_destination(lines):
dest_pickings = lines.move_id._moves_to_sync_checkout()
all_moves = self.env["stock.move"].union(*dest_pickings.values())
# add lock on all the lines that will be synchronized on the
# destination so other transactions will wait before trying to
# change the destination
lines = lines | all_moves.move_line_ids
return lines
return all_moves
return lines.move_id

def _all_lines_to_lock(self, lines):
# add lock on all the lines that will be synchronized on the
# destination so other transactions will wait before trying to
# change the destination
all_moves = self._all_moves(lines)
return lines | all_moves.move_line_ids

def _sync_checkout(self, lines, location):
moves = lines.mapped("move_id")
if self._has_to_sync_destination(lines):
dest_pickings = moves._moves_to_sync_checkout()
all_moves = self.env["stock.move"].union(*dest_pickings.values())
all_moves.sync_checkout_destination(location)
all_moves = self._all_moves(lines)
all_moves.sync_checkout_destination(location)
18 changes: 18 additions & 0 deletions shopfloor_checkout_sync/actions/stock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.addons.component.core import Component


class StockAction(Component):
_inherit = "shopfloor.stock.action"

def _set_destination_on_lines(self, lines, location_dest):
checkout_sync = self._actions_for("checkout.sync")
checkout_sync._sync_checkout(lines, location_dest)
super()._set_destination_on_lines(lines, location_dest)

def set_destination_and_unload_lines(self, lines, location_dest, unload=False):
checkout_sync = self._actions_for("checkout.sync")
all_lines = checkout_sync._all_lines_to_lock(lines)
super().set_destination_and_unload_lines(all_lines, location_dest, unload)
1 change: 1 addition & 0 deletions shopfloor_checkout_sync/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
* Guewen Baconnier <[email protected]>
* `Trobz <https://trobz.com>`_:
* Michael Tietz (MT Software) <[email protected]>
3 changes: 0 additions & 3 deletions shopfloor_checkout_sync/services/__init__.py

This file was deleted.

19 changes: 0 additions & 19 deletions shopfloor_checkout_sync/services/cluster_picking.py

This file was deleted.

19 changes: 0 additions & 19 deletions shopfloor_checkout_sync/services/location_content_transfer.py

This file was deleted.

19 changes: 0 additions & 19 deletions shopfloor_checkout_sync/services/zone_picking.py

This file was deleted.

Loading
Loading