odoo-addon-shopfloor 16.0.1.0.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/shopfloor/README.rst +160 -0
- odoo/addons/shopfloor/__init__.py +4 -0
- odoo/addons/shopfloor/__manifest__.py +65 -0
- odoo/addons/shopfloor/actions/__init__.py +15 -0
- odoo/addons/shopfloor/actions/change_package_lot.py +164 -0
- odoo/addons/shopfloor/actions/completion_info.py +42 -0
- odoo/addons/shopfloor/actions/data.py +329 -0
- odoo/addons/shopfloor/actions/data_detail.py +154 -0
- odoo/addons/shopfloor/actions/inventory.py +150 -0
- odoo/addons/shopfloor/actions/location_content_transfer_sorter.py +89 -0
- odoo/addons/shopfloor/actions/message.py +846 -0
- odoo/addons/shopfloor/actions/move_line_search.py +119 -0
- odoo/addons/shopfloor/actions/packaging.py +59 -0
- odoo/addons/shopfloor/actions/savepoint.py +44 -0
- odoo/addons/shopfloor/actions/schema.py +182 -0
- odoo/addons/shopfloor/actions/schema_detail.py +98 -0
- odoo/addons/shopfloor/actions/search.py +187 -0
- odoo/addons/shopfloor/actions/stock.py +239 -0
- odoo/addons/shopfloor/actions/stock_unreserve.py +66 -0
- odoo/addons/shopfloor/components/__init__.py +5 -0
- odoo/addons/shopfloor/components/scan_handler_location.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_lot.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_package.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_product.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_transfer.py +26 -0
- odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +73 -0
- odoo/addons/shopfloor/demo/shopfloor_app_demo.xml +12 -0
- odoo/addons/shopfloor/demo/shopfloor_menu_demo.xml +64 -0
- odoo/addons/shopfloor/demo/shopfloor_profile_demo.xml +8 -0
- odoo/addons/shopfloor/demo/stock_picking_type_demo.xml +93 -0
- odoo/addons/shopfloor/docs/checkout_diag_seq.plantuml +61 -0
- odoo/addons/shopfloor/docs/checkout_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/cluster_picking_diag_seq.plantuml +112 -0
- odoo/addons/shopfloor/docs/cluster_picking_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/delivery_diag_seq.plantuml +56 -0
- odoo/addons/shopfloor/docs/delivery_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.plantuml +66 -0
- odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/oca_logo.png +0 -0
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.plantuml +36 -0
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/zone_picking_diag_seq.plantuml +85 -0
- odoo/addons/shopfloor/docs/zone_picking_diag_seq.png +0 -0
- odoo/addons/shopfloor/exceptions.py +6 -0
- odoo/addons/shopfloor/i18n/ca.po +1802 -0
- odoo/addons/shopfloor/i18n/de.po +1791 -0
- odoo/addons/shopfloor/i18n/es_AR.po +2147 -0
- odoo/addons/shopfloor/i18n/pt_BR.po +1791 -0
- odoo/addons/shopfloor/i18n/shopfloor.pot +1877 -0
- odoo/addons/shopfloor/models/__init__.py +12 -0
- odoo/addons/shopfloor/models/priority_postpone_mixin.py +41 -0
- odoo/addons/shopfloor/models/shopfloor_app.py +9 -0
- odoo/addons/shopfloor/models/shopfloor_menu.py +436 -0
- odoo/addons/shopfloor/models/stock_location.py +76 -0
- odoo/addons/shopfloor/models/stock_move.py +119 -0
- odoo/addons/shopfloor/models/stock_move_line.py +307 -0
- odoo/addons/shopfloor/models/stock_package_level.py +50 -0
- odoo/addons/shopfloor/models/stock_picking.py +118 -0
- odoo/addons/shopfloor/models/stock_picking_batch.py +41 -0
- odoo/addons/shopfloor/models/stock_picking_type.py +26 -0
- odoo/addons/shopfloor/models/stock_quant.py +31 -0
- odoo/addons/shopfloor/models/stock_quant_package.py +101 -0
- odoo/addons/shopfloor/readme/CONTRIBUTORS.rst +18 -0
- odoo/addons/shopfloor/readme/CREDITS.rst +5 -0
- odoo/addons/shopfloor/readme/DESCRIPTION.rst +17 -0
- odoo/addons/shopfloor/readme/HISTORY.rst +4 -0
- odoo/addons/shopfloor/readme/ROADMAP.rst +4 -0
- odoo/addons/shopfloor/readme/USAGE.rst +6 -0
- odoo/addons/shopfloor/security/groups.xml +17 -0
- odoo/addons/shopfloor/services/__init__.py +16 -0
- odoo/addons/shopfloor/services/checkout.py +1763 -0
- odoo/addons/shopfloor/services/cluster_picking.py +1628 -0
- odoo/addons/shopfloor/services/delivery.py +828 -0
- odoo/addons/shopfloor/services/forms/__init__.py +1 -0
- odoo/addons/shopfloor/services/forms/picking_form.py +78 -0
- odoo/addons/shopfloor/services/location_content_transfer.py +1194 -0
- odoo/addons/shopfloor/services/menu.py +60 -0
- odoo/addons/shopfloor/services/picking_batch.py +126 -0
- odoo/addons/shopfloor/services/service.py +101 -0
- odoo/addons/shopfloor/services/single_pack_transfer.py +366 -0
- odoo/addons/shopfloor/services/zone_picking.py +1938 -0
- odoo/addons/shopfloor/static/description/icon.png +0 -0
- odoo/addons/shopfloor/static/description/index.html +500 -0
- odoo/addons/shopfloor/tests/__init__.py +83 -0
- odoo/addons/shopfloor/tests/common.py +324 -0
- odoo/addons/shopfloor/tests/models.py +29 -0
- odoo/addons/shopfloor/tests/test_actions_change_package_lot.py +1175 -0
- odoo/addons/shopfloor/tests/test_actions_data.py +376 -0
- odoo/addons/shopfloor/tests/test_actions_data_base.py +244 -0
- odoo/addons/shopfloor/tests/test_actions_data_detail.py +322 -0
- odoo/addons/shopfloor/tests/test_actions_search.py +248 -0
- odoo/addons/shopfloor/tests/test_actions_stock.py +48 -0
- odoo/addons/shopfloor/tests/test_checkout_auto_post.py +67 -0
- odoo/addons/shopfloor/tests/test_checkout_base.py +81 -0
- odoo/addons/shopfloor/tests/test_checkout_cancel_line.py +154 -0
- odoo/addons/shopfloor/tests/test_checkout_change_packaging.py +184 -0
- odoo/addons/shopfloor/tests/test_checkout_done.py +133 -0
- odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +131 -0
- odoo/addons/shopfloor/tests/test_checkout_list_package.py +327 -0
- odoo/addons/shopfloor/tests/test_checkout_new_package.py +88 -0
- odoo/addons/shopfloor/tests/test_checkout_no_package.py +95 -0
- odoo/addons/shopfloor/tests/test_checkout_scan.py +174 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line.py +377 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line_base.py +25 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line_no_prefill_qty.py +91 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +451 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action_no_prefill_qty.py +107 -0
- odoo/addons/shopfloor/tests/test_checkout_select.py +74 -0
- odoo/addons/shopfloor/tests/test_checkout_select_line.py +130 -0
- odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +64 -0
- odoo/addons/shopfloor/tests/test_checkout_set_qty.py +257 -0
- odoo/addons/shopfloor/tests/test_checkout_summary.py +69 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_base.py +83 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_batch.py +109 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_change_pack_lot.py +111 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_is_zero.py +98 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination.py +376 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination_no_prefill_qty.py +115 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line.py +402 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_location_or_pack_first.py +114 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_no_prefill_qty.py +70 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_select.py +387 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_skip.py +90 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_stock_issue.py +364 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_unload.py +911 -0
- odoo/addons/shopfloor/tests/test_delivery_base.py +155 -0
- odoo/addons/shopfloor/tests/test_delivery_done.py +108 -0
- odoo/addons/shopfloor/tests/test_delivery_list_stock_picking.py +49 -0
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_line.py +119 -0
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_pack.py +107 -0
- odoo/addons/shopfloor/tests/test_delivery_scan_deliver.py +557 -0
- odoo/addons/shopfloor/tests/test_delivery_select.py +38 -0
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_line.py +91 -0
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_pack.py +135 -0
- odoo/addons/shopfloor/tests/test_delivery_sublocation.py +180 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +136 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_get_work.py +125 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_mix.py +509 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_putaway.py +143 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_scan_location.py +34 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_all.py +343 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +1074 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +748 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_start.py +359 -0
- odoo/addons/shopfloor/tests/test_menu_base.py +261 -0
- odoo/addons/shopfloor/tests/test_menu_counters.py +61 -0
- odoo/addons/shopfloor/tests/test_misc.py +25 -0
- odoo/addons/shopfloor/tests/test_move_action_assign.py +87 -0
- odoo/addons/shopfloor/tests/test_openapi.py +21 -0
- odoo/addons/shopfloor/tests/test_picking_form.py +62 -0
- odoo/addons/shopfloor/tests/test_scan_anything.py +49 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer.py +1121 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer_base.py +32 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer_putaway.py +104 -0
- odoo/addons/shopfloor/tests/test_stock_split.py +204 -0
- odoo/addons/shopfloor/tests/test_user.py +42 -0
- odoo/addons/shopfloor/tests/test_zone_picking_base.py +608 -0
- odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +140 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +723 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py +207 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py.bak +202 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_no_prefill_qty.py +107 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_picking_type.py +26 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +643 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_no_prefill_qty.py +146 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +241 -0
- odoo/addons/shopfloor/tests/test_zone_picking_start.py +206 -0
- odoo/addons/shopfloor/tests/test_zone_picking_stock_issue.py +121 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +353 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_buffer_lines.py +113 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +374 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_single.py +123 -0
- odoo/addons/shopfloor/tests/test_zone_picking_zero_check.py +43 -0
- odoo/addons/shopfloor/utils.py +13 -0
- odoo/addons/shopfloor/views/shopfloor_menu.xml +167 -0
- odoo/addons/shopfloor/views/stock_location.xml +20 -0
- odoo/addons/shopfloor/views/stock_move_line.xml +52 -0
- odoo/addons/shopfloor/views/stock_picking_type.xml +19 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/METADATA +192 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/RECORD +182 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/WHEEL +5 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/top_level.txt +1 -0
@@ -0,0 +1,509 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
3
|
+
|
4
|
+
from .test_location_content_transfer_base import LocationContentTransferCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class LocationContentTransferMixCase(LocationContentTransferCommonCase):
|
10
|
+
"""Tests where we mix location content transfer with other scenarios."""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassUsers(cls):
|
14
|
+
super().setUpClassUsers()
|
15
|
+
Users = (
|
16
|
+
cls.env["res.users"]
|
17
|
+
.sudo()
|
18
|
+
.with_context(
|
19
|
+
**{"no_reset_password": True, "mail_create_nosubscribe": True}
|
20
|
+
)
|
21
|
+
)
|
22
|
+
cls.stock_user2 = Users.create(
|
23
|
+
{
|
24
|
+
"name": "Paul Posichon",
|
25
|
+
"login": "paulposichon",
|
26
|
+
"email": "paul.posichon@example.com",
|
27
|
+
"notification_type": "inbox",
|
28
|
+
"groups_id": [(6, 0, [cls.env.ref("stock.group_stock_user").id])],
|
29
|
+
}
|
30
|
+
)
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def setUpClassVars(cls, *args, **kwargs):
|
34
|
+
super().setUpClassVars(*args, **kwargs)
|
35
|
+
cls.zp_menu = cls.env.ref("shopfloor.shopfloor_menu_demo_zone_picking")
|
36
|
+
cls.wh.sudo().delivery_steps = "pick_pack_ship"
|
37
|
+
cls.pack_location = cls.wh.wh_pack_stock_loc_id
|
38
|
+
cls.ship_location = cls.wh.wh_output_stock_loc_id
|
39
|
+
# Allows zone picking to process PICK picking type
|
40
|
+
cls.zp_menu.sudo().picking_type_ids += cls.wh.pick_type_id
|
41
|
+
# Allows location content transfer to process PACK picking type
|
42
|
+
cls.menu.sudo().picking_type_ids = cls.wh.pack_type_id
|
43
|
+
cls.wh.pack_type_id.sudo().default_location_dest_id = cls.env.ref(
|
44
|
+
"stock.stock_location_output"
|
45
|
+
)
|
46
|
+
|
47
|
+
@classmethod
|
48
|
+
def setUpClassBaseData(cls):
|
49
|
+
super().setUpClassBaseData()
|
50
|
+
cls.packing_location.sudo().active = True
|
51
|
+
products = cls.product_a
|
52
|
+
for product in products:
|
53
|
+
cls.env["stock.putaway.rule"].sudo().create(
|
54
|
+
{
|
55
|
+
"product_id": product.id,
|
56
|
+
"location_in_id": cls.stock_location.id,
|
57
|
+
"location_out_id": cls.shelf1.id,
|
58
|
+
}
|
59
|
+
)
|
60
|
+
|
61
|
+
# Put product_a quantities in different packages to get
|
62
|
+
# two stock move lines (6 and 4 to satisfy 10 qties)
|
63
|
+
cls.package_1 = cls.env["stock.quant.package"].create({"name": "PACKAGE_1"})
|
64
|
+
cls.package_2 = cls.env["stock.quant.package"].create({"name": "PACKAGE_2"})
|
65
|
+
cls._update_qty_in_location(
|
66
|
+
cls.stock_location, cls.product_a, 6, package=cls.package_1
|
67
|
+
)
|
68
|
+
cls._update_qty_in_location(
|
69
|
+
cls.stock_location, cls.product_a, 4, package=cls.package_2
|
70
|
+
)
|
71
|
+
# Create the pick/pack/ship transfers
|
72
|
+
cls.ship_move_a = cls.env["stock.move"].create(
|
73
|
+
{
|
74
|
+
"name": cls.product_a.display_name,
|
75
|
+
"product_id": cls.product_a.id,
|
76
|
+
"product_uom_qty": 10.0,
|
77
|
+
"product_uom": cls.product_a.uom_id.id,
|
78
|
+
"location_id": cls.ship_location.id,
|
79
|
+
"location_dest_id": cls.customer_location.id,
|
80
|
+
"warehouse_id": cls.wh.id,
|
81
|
+
"picking_type_id": cls.wh.out_type_id.id,
|
82
|
+
"procure_method": "make_to_order",
|
83
|
+
"state": "draft",
|
84
|
+
}
|
85
|
+
)
|
86
|
+
cls.ship_move_a._assign_picking()
|
87
|
+
cls.ship_move_a._action_confirm()
|
88
|
+
cls.pack_move_a = cls.ship_move_a.move_orig_ids[0]
|
89
|
+
cls.pick_move_a = cls.pack_move_a.move_orig_ids[0]
|
90
|
+
cls.picking1 = cls.pick_move_a.picking_id
|
91
|
+
cls.packing1 = cls.pack_move_a.picking_id
|
92
|
+
cls.picking1.action_assign()
|
93
|
+
|
94
|
+
def setUp(self):
|
95
|
+
super().setUp()
|
96
|
+
with self.work_on_services(menu=self.zp_menu, profile=self.profile) as work:
|
97
|
+
self.zp_service = work.component(usage="zone_picking")
|
98
|
+
|
99
|
+
def _zone_picking_process_line(self, move_line, dest_location=None):
|
100
|
+
picking = move_line.picking_id
|
101
|
+
zone_location = picking.location_id
|
102
|
+
picking_type = picking.picking_type_id
|
103
|
+
self.zp_service.work.current_zone_location = zone_location
|
104
|
+
self.zp_service.work.current_picking_type = picking_type
|
105
|
+
move_lines = picking.move_line_ids.filtered(
|
106
|
+
lambda m: m.state not in ("cancel", "done")
|
107
|
+
)
|
108
|
+
# Select the picking type
|
109
|
+
response = self.zp_service.scan_location(barcode=zone_location.barcode)
|
110
|
+
available_picking_type_ids = [
|
111
|
+
r["id"] for r in response["data"]["select_picking_type"]["picking_types"]
|
112
|
+
]
|
113
|
+
assert picking_type.id in available_picking_type_ids
|
114
|
+
assert "message" not in response
|
115
|
+
# Check the move lines related to the picking type
|
116
|
+
response = self.zp_service.list_move_lines()
|
117
|
+
available_move_line_ids = [
|
118
|
+
r["id"] for r in response["data"]["select_line"]["move_lines"]
|
119
|
+
]
|
120
|
+
assert not set(move_lines.ids) - set(available_move_line_ids)
|
121
|
+
assert "message" not in response
|
122
|
+
# Set the destination on the move line
|
123
|
+
if not dest_location:
|
124
|
+
dest_location = move_line.location_dest_id
|
125
|
+
qty = move_line.reserved_uom_qty
|
126
|
+
response = self.zp_service.set_destination(
|
127
|
+
move_line.id,
|
128
|
+
dest_location.barcode,
|
129
|
+
qty,
|
130
|
+
confirmation=True,
|
131
|
+
)
|
132
|
+
assert response["message"]["message_type"] == "success"
|
133
|
+
self.assertEqual(move_line.state, "done")
|
134
|
+
self.assertEqual(move_line.move_id.product_uom_qty, qty)
|
135
|
+
|
136
|
+
def _location_content_transfer_process_line(
|
137
|
+
self, move_line, set_destination=False, user=None
|
138
|
+
):
|
139
|
+
service = self.service
|
140
|
+
if user:
|
141
|
+
env = self.env(user=user)
|
142
|
+
service = self.get_service(
|
143
|
+
"location_content_transfer",
|
144
|
+
env=env,
|
145
|
+
menu=self.menu,
|
146
|
+
profile=self.profile,
|
147
|
+
)
|
148
|
+
|
149
|
+
pack_location = move_line.location_id
|
150
|
+
out_location = move_line.location_dest_id
|
151
|
+
# Scan the location
|
152
|
+
response = service.scan_location(pack_location.barcode)
|
153
|
+
# Set the destination
|
154
|
+
if set_destination:
|
155
|
+
assert response["next_state"] in ("scan_destination_all", "start_single")
|
156
|
+
qty = move_line.reserved_uom_qty
|
157
|
+
if response["next_state"] == "scan_destination_all":
|
158
|
+
response = service.set_destination_all(
|
159
|
+
pack_location.id, out_location.barcode
|
160
|
+
)
|
161
|
+
self.assert_response_start(
|
162
|
+
response,
|
163
|
+
message=service.msg_store.location_content_transfer_complete(
|
164
|
+
pack_location,
|
165
|
+
out_location,
|
166
|
+
),
|
167
|
+
)
|
168
|
+
self.assertEqual(move_line.state, "done")
|
169
|
+
self.assertEqual(move_line.move_id.product_uom_qty, qty)
|
170
|
+
elif response["next_state"] == "start_single":
|
171
|
+
response = service.scan_line(
|
172
|
+
pack_location.id, move_line.id, move_line.product_id.barcode
|
173
|
+
)
|
174
|
+
assert response["message"]["message_type"] == "success"
|
175
|
+
response = service.set_destination_line(
|
176
|
+
pack_location.id, move_line.id, qty, out_location.barcode
|
177
|
+
)
|
178
|
+
assert response["message"]["message_type"] == "success"
|
179
|
+
assert move_line.state == "done"
|
180
|
+
assert move_line.qty_done == qty
|
181
|
+
return response
|
182
|
+
|
183
|
+
def test_with_zone_picking1(self):
|
184
|
+
"""Test the following scenario:
|
185
|
+
|
186
|
+
1) Operator-1 processes the first pallet with the "zone picking" scenario:
|
187
|
+
|
188
|
+
move1 PICK -> PACK 'done'
|
189
|
+
|
190
|
+
2) Operator-2 with the "location content transfer" scenario scan
|
191
|
+
the location where this first pallet is (so the move line is still not
|
192
|
+
done, the operator is currently moving the goods to the destination location):
|
193
|
+
|
194
|
+
move1 PACK -> SHIP 'assigned' while the operator is moving it
|
195
|
+
|
196
|
+
3) Operator-1 process the second pallet with the "zone picking" scenario:
|
197
|
+
|
198
|
+
move2 PICK -> PACK 'done'
|
199
|
+
|
200
|
+
4) Operator-3 with the "location content transfer" scenario scan
|
201
|
+
the location where this second pallet is, Odoo should return only this
|
202
|
+
second pallet as the first one, even if not fully processed (done)
|
203
|
+
is not physically available in the scanned location.
|
204
|
+
|
205
|
+
move2 PACK -> SHIP 'assigned' is proposed to the operator
|
206
|
+
move1 PACK -> SHIP while still 'assigned' is not proposed to the operator
|
207
|
+
"""
|
208
|
+
picking = self.picking1
|
209
|
+
move_lines = picking.move_line_ids
|
210
|
+
pick_move_line1 = move_lines.filtered(
|
211
|
+
lambda ml: ml.result_package_id == self.package_1
|
212
|
+
)
|
213
|
+
pick_move_line2 = move_lines.filtered(
|
214
|
+
lambda ml: ml.result_package_id == self.package_2
|
215
|
+
)
|
216
|
+
# Operator-1 process the first pallet with the "zone picking" scenario
|
217
|
+
self._zone_picking_process_line(pick_move_line1)
|
218
|
+
# Operator-2 with the "location content transfer" scenario scan
|
219
|
+
# the location where this first pallet is (so the move line is still not
|
220
|
+
# done, the operator is currently moving the goods to the destination location)
|
221
|
+
pack_move_line1 = pick_move_line1.move_id.move_dest_ids.filtered(
|
222
|
+
lambda m: m.state not in ("cancel", "done")
|
223
|
+
).move_line_ids.filtered(lambda l: not l.shopfloor_user_id)
|
224
|
+
self._location_content_transfer_process_line(pack_move_line1)
|
225
|
+
# Operator-1 process the second pallet with the "zone picking" scenario
|
226
|
+
self._zone_picking_process_line(pick_move_line2)
|
227
|
+
# Operator-3 with the "location content transfer" scenario scan
|
228
|
+
# the location where this second pallet is
|
229
|
+
pack_move_line2 = pick_move_line2.move_id.move_dest_ids.filtered(
|
230
|
+
lambda m: m.state not in ("cancel", "done")
|
231
|
+
).move_line_ids.filtered(lambda l: not l.shopfloor_user_id)
|
232
|
+
assert (
|
233
|
+
len(pack_move_line2) == 1
|
234
|
+
), "Operator-3 should end up with one move line taken from {}".format(
|
235
|
+
pack_move_line2.picking_id.name
|
236
|
+
)
|
237
|
+
self._location_content_transfer_process_line(pack_move_line2)
|
238
|
+
|
239
|
+
def test_with_zone_picking2(self):
|
240
|
+
"""Test the following scenario:
|
241
|
+
|
242
|
+
1) Operator-1 processes the first pallet with the "zone picking" scenario
|
243
|
+
to move the goods to PACK-1 and unload in destination location1:
|
244
|
+
|
245
|
+
move1 PICK -> PACK-1 'done'
|
246
|
+
|
247
|
+
2) Operator-1 processes the second pallet with the "zone picking" scenario
|
248
|
+
to move the goods to PACK-2 and unload in destination location2:
|
249
|
+
|
250
|
+
move1 PICK -> PACK-2 'done'
|
251
|
+
|
252
|
+
3) Operator-2 with the "location content transfer" scenario scan
|
253
|
+
the location where the first pallet is (PACK-1):
|
254
|
+
- the app should found one move line
|
255
|
+
- this move line will be put in its own transfer as its sibling lines
|
256
|
+
are in another source location
|
257
|
+
- as such the app should ask the destination location (as there is
|
258
|
+
only one line)
|
259
|
+
|
260
|
+
move1 PACK-2 -> SHIP (still handled by the operator so not 'done')
|
261
|
+
|
262
|
+
4) Operator-3 with the "location content transfer" scenario scan
|
263
|
+
the location where the first pallet is (PACK-1):
|
264
|
+
- nothing is found as the pallet is currently handled by Operator-2
|
265
|
+
|
266
|
+
5) If Operator-2 is unable to finish the flow with the first pallet
|
267
|
+
(barcode device out of battery... etc), he should be able to recover
|
268
|
+
what he started.
|
269
|
+
|
270
|
+
6) Operator-2 then finishes its operation regarding the first pallet, and
|
271
|
+
scan the location where the second pallet is (PACK-2). He should find
|
272
|
+
only this pallet available.
|
273
|
+
"""
|
274
|
+
move_lines = self.picking1.move_line_ids
|
275
|
+
pick_move_line1 = move_lines.filtered(
|
276
|
+
lambda ml: ml.result_package_id == self.package_1
|
277
|
+
)
|
278
|
+
pick_move_line2 = move_lines.filtered(
|
279
|
+
lambda ml: ml.result_package_id == self.package_2
|
280
|
+
)
|
281
|
+
# Operator-1 process the first pallet with the "zone picking" scenario
|
282
|
+
orig_dest_location = pick_move_line1.location_dest_id
|
283
|
+
dest_location1 = pick_move_line1.location_dest_id.sudo().copy(
|
284
|
+
{
|
285
|
+
"name": orig_dest_location.name + "_1",
|
286
|
+
"barcode": orig_dest_location.barcode + "_1",
|
287
|
+
"location_id": orig_dest_location.id,
|
288
|
+
}
|
289
|
+
)
|
290
|
+
self._zone_picking_process_line(pick_move_line1, dest_location=dest_location1)
|
291
|
+
# Operator-1 process the second pallet with the "zone picking" scenario
|
292
|
+
dest_location2 = orig_dest_location.sudo().copy(
|
293
|
+
{
|
294
|
+
"name": orig_dest_location.name + "_2",
|
295
|
+
"barcode": orig_dest_location.barcode + "_2",
|
296
|
+
"location_id": orig_dest_location.id,
|
297
|
+
}
|
298
|
+
)
|
299
|
+
self._zone_picking_process_line(pick_move_line2, dest_location=dest_location2)
|
300
|
+
pack_move_a = pick_move_line1.move_id.move_dest_ids.filtered(
|
301
|
+
lambda m: m.state not in ("cancel", "done")
|
302
|
+
)
|
303
|
+
self.assertEqual(pack_move_a, self.pack_move_a)
|
304
|
+
pack_first_pallet = pack_move_a.move_line_ids.filtered(
|
305
|
+
lambda l: not l.shopfloor_user_id and l.location_id == dest_location1
|
306
|
+
)
|
307
|
+
self.assertEqual(pack_first_pallet.reserved_uom_qty, 6)
|
308
|
+
self.assertEqual(pack_first_pallet.qty_done, 0)
|
309
|
+
pack_second_pallet = pack_move_a.move_line_ids.filtered(
|
310
|
+
lambda l: not l.shopfloor_user_id and l.location_id == dest_location2
|
311
|
+
)
|
312
|
+
self.assertEqual(pack_second_pallet.reserved_uom_qty, 4)
|
313
|
+
self.assertEqual(pack_second_pallet.qty_done, 0)
|
314
|
+
# Operator-2 with the "location content transfer" scenario scan
|
315
|
+
# the location where the first pallet is.
|
316
|
+
# This pallet/move line will be put in its own transfer as its sibling
|
317
|
+
# lines are in another source location.
|
318
|
+
previous_picking = pack_first_pallet.picking_id
|
319
|
+
response = self._location_content_transfer_process_line(pack_first_pallet)
|
320
|
+
new_picking = pack_first_pallet.picking_id
|
321
|
+
self.assertTrue(previous_picking != new_picking)
|
322
|
+
self.assert_response_scan_destination_all(response, new_picking)
|
323
|
+
response_packages = response["data"]["scan_destination_all"]["package_levels"]
|
324
|
+
self.assertEqual(len(response_packages), 1)
|
325
|
+
self.assertEqual(
|
326
|
+
response_packages[0]["package_src"]["id"], pack_first_pallet.package_id.id
|
327
|
+
)
|
328
|
+
# Ensure that the second pallet is untouched
|
329
|
+
self.assertEqual(pack_second_pallet.qty_done, 0)
|
330
|
+
# Operator-3 with the "location content transfer" scenario scan
|
331
|
+
# the location where the first pallet is: he should found nothing
|
332
|
+
response = self._location_content_transfer_process_line(
|
333
|
+
pack_first_pallet, user=self.stock_user2
|
334
|
+
)
|
335
|
+
self.assert_response_start(
|
336
|
+
response, message=self.service.msg_store.new_move_lines_not_assigned()
|
337
|
+
)
|
338
|
+
# Check if Operator-2 is able to recover its session
|
339
|
+
expected_picking = pack_first_pallet.picking_id
|
340
|
+
response = self.service.start_or_recover()
|
341
|
+
self.assert_response_scan_destination_all(
|
342
|
+
response,
|
343
|
+
expected_picking,
|
344
|
+
message=self.service.msg_store.recovered_previous_session(),
|
345
|
+
)
|
346
|
+
# Operator-2 finishes its operation regarding the first pallet
|
347
|
+
qty = pack_first_pallet.reserved_uom_qty
|
348
|
+
response = self.service.set_destination_all(
|
349
|
+
pack_first_pallet.location_id.id, pack_first_pallet.location_dest_id.barcode
|
350
|
+
)
|
351
|
+
self.assert_response_start(
|
352
|
+
response,
|
353
|
+
message=self.service.msg_store.location_content_transfer_complete(
|
354
|
+
pack_first_pallet.location_id,
|
355
|
+
pack_first_pallet.location_dest_id,
|
356
|
+
),
|
357
|
+
)
|
358
|
+
self.assertEqual(pack_first_pallet.qty_done, 6)
|
359
|
+
self.assertEqual(pack_first_pallet.state, "done")
|
360
|
+
self.assertEqual(pack_first_pallet.move_id.product_uom_qty, qty)
|
361
|
+
# Ensure that the second pallet is untouched
|
362
|
+
self.assertEqual(pack_second_pallet.qty_done, 0)
|
363
|
+
# Operator-2 (still with the "location content transfer" scenario) scan
|
364
|
+
# the location where the second pallet is
|
365
|
+
pack_move_a = pick_move_line2.move_id.move_dest_ids.filtered(
|
366
|
+
lambda m: m.state not in ("cancel", "done")
|
367
|
+
)
|
368
|
+
self.assertEqual(pack_move_a, self.pack_move_a)
|
369
|
+
pack_second_pallet = pack_move_a.move_line_ids.filtered(
|
370
|
+
lambda l: not l.shopfloor_user_id and l.location_id == dest_location2
|
371
|
+
)
|
372
|
+
picking_before = pack_second_pallet.picking_id
|
373
|
+
move_lines = self.service._find_location_move_lines(
|
374
|
+
pack_second_pallet.location_id
|
375
|
+
)
|
376
|
+
response = self._location_content_transfer_process_line(pack_second_pallet)
|
377
|
+
response_packages = response["data"]["scan_destination_all"]["package_levels"]
|
378
|
+
self.assertEqual(len(response_packages), 1)
|
379
|
+
self.assertEqual(
|
380
|
+
response_packages[0]["package_src"]["id"], pack_second_pallet.package_id.id
|
381
|
+
)
|
382
|
+
picking_after = pack_second_pallet.picking_id
|
383
|
+
self.assertEqual(picking_before, picking_after)
|
384
|
+
self.assert_response_scan_destination_all(response, picking_after)
|
385
|
+
|
386
|
+
def test_with_zone_picking3(self):
|
387
|
+
"""Test the following scenario:
|
388
|
+
|
389
|
+
1) Operator-1 processes the first pallet with the "zone picking" scenario
|
390
|
+
to move the goods to PACK-1:
|
391
|
+
|
392
|
+
move1 PICK -> PACK-1 'done'
|
393
|
+
|
394
|
+
2) Operator-2 with the "location content transfer" scenario scan
|
395
|
+
the location where the first pallet is (PACK-1):
|
396
|
+
- the app should found one move line
|
397
|
+
- this move line will be put in its own transfer in any case
|
398
|
+
- as such the app should ask the destination location (as there is
|
399
|
+
only one line)
|
400
|
+
|
401
|
+
move1 PACK-1 -> SHIP (still handled by the operator so not 'done')
|
402
|
+
|
403
|
+
3) Operator-1 processes the second pallet with the "zone picking" scenario
|
404
|
+
to move the goods to PACK-2:
|
405
|
+
|
406
|
+
move1 PICK -> PACK-2 'done'
|
407
|
+
|
408
|
+
- this will automatically update the reservation (new move line) in
|
409
|
+
the transfer previously processed by Operator-2.
|
410
|
+
|
411
|
+
4) Operator-2 then finishes its operation regarding the first pallet
|
412
|
+
without any trouble.
|
413
|
+
|
414
|
+
5) Operator-2 with the "location content transfer" scenario scan
|
415
|
+
the location where the second pallet is (PACK-2), etc
|
416
|
+
"""
|
417
|
+
move_lines = self.picking1.move_line_ids
|
418
|
+
pick_move_line1 = move_lines.filtered(
|
419
|
+
lambda ml: ml.result_package_id == self.package_1
|
420
|
+
)
|
421
|
+
pick_move_line2 = move_lines.filtered(
|
422
|
+
lambda ml: ml.result_package_id == self.package_2
|
423
|
+
)
|
424
|
+
orig_dest_location = pick_move_line1.location_dest_id
|
425
|
+
dest_location1 = pick_move_line1.location_dest_id.sudo().copy(
|
426
|
+
{
|
427
|
+
"name": orig_dest_location.name + "_1",
|
428
|
+
"barcode": orig_dest_location.barcode + "_1",
|
429
|
+
"location_id": orig_dest_location.id,
|
430
|
+
}
|
431
|
+
)
|
432
|
+
dest_location2 = orig_dest_location.sudo().copy(
|
433
|
+
{
|
434
|
+
"name": orig_dest_location.name + "_2",
|
435
|
+
"barcode": orig_dest_location.barcode + "_2",
|
436
|
+
"location_id": orig_dest_location.id,
|
437
|
+
}
|
438
|
+
)
|
439
|
+
# Operator-1 process the first pallet with the "zone picking" scenario
|
440
|
+
self._zone_picking_process_line(pick_move_line1, dest_location=dest_location1)
|
441
|
+
pack_move_a1 = pick_move_line1.move_id.move_dest_ids.filtered(
|
442
|
+
lambda m: m.move_line_ids.package_id == self.package_1
|
443
|
+
)
|
444
|
+
self.assertEqual(pack_move_a1, self.pack_move_a)
|
445
|
+
pack_first_pallet = pack_move_a1.move_line_ids.filtered(
|
446
|
+
lambda l: not l.shopfloor_user_id and l.location_id == dest_location1
|
447
|
+
)
|
448
|
+
self.assertEqual(pack_first_pallet.reserved_uom_qty, 6)
|
449
|
+
self.assertEqual(pack_first_pallet.qty_done, 0)
|
450
|
+
# Operator-2 with the "location content transfer" scenario scan
|
451
|
+
# the location where the first pallet is.
|
452
|
+
# This pallet/move line will be put in its own move and transfer by convenience
|
453
|
+
original_pack_transfer = pack_first_pallet.picking_id
|
454
|
+
response = self._location_content_transfer_process_line(pack_first_pallet)
|
455
|
+
new_pack_transfer = pack_first_pallet.picking_id
|
456
|
+
self.assertNotEqual(original_pack_transfer, new_pack_transfer)
|
457
|
+
self.assert_response_scan_destination_all(response, new_pack_transfer)
|
458
|
+
response_packages = response["data"]["scan_destination_all"]["package_levels"]
|
459
|
+
self.assertEqual(len(response_packages), 1)
|
460
|
+
self.assertEqual(
|
461
|
+
response_packages[0]["package_src"]["id"], pack_first_pallet.package_id.id
|
462
|
+
)
|
463
|
+
# All pack lines have been processed until now, so the existing pack
|
464
|
+
# operation is now waiting goods from pick operation
|
465
|
+
self.assertEqual(original_pack_transfer.state, "waiting")
|
466
|
+
# Operator-1 process the second pallet with the "zone picking" scenario
|
467
|
+
self._zone_picking_process_line(pick_move_line2, dest_location=dest_location2)
|
468
|
+
pack_move_a2 = pick_move_line2.move_id.move_dest_ids.filtered(
|
469
|
+
lambda m: m.move_line_ids.package_id == self.package_2
|
470
|
+
)
|
471
|
+
pack_second_pallet = pack_move_a2.move_line_ids.filtered(
|
472
|
+
lambda l: not l.shopfloor_user_id and l.location_id == dest_location2
|
473
|
+
)
|
474
|
+
self.assertEqual(pack_second_pallet.reserved_uom_qty, 4)
|
475
|
+
self.assertEqual(pack_second_pallet.qty_done, 0)
|
476
|
+
# The last action has updated the pack operation (new move line) in the
|
477
|
+
# transfer previously processed by Operator-2.
|
478
|
+
self.assertEqual(original_pack_transfer.state, "assigned")
|
479
|
+
self.assertIn(self.package_2, original_pack_transfer.move_line_ids.package_id)
|
480
|
+
# Operator-2 finishes its operation regarding the first pallet without
|
481
|
+
# any trouble as the processed move line has been put in its own
|
482
|
+
# move+transfer
|
483
|
+
qty = pack_first_pallet.reserved_uom_qty
|
484
|
+
response = self.service.set_destination_all(
|
485
|
+
pack_first_pallet.location_id.id, pack_first_pallet.location_dest_id.barcode
|
486
|
+
)
|
487
|
+
self.assert_response_start(
|
488
|
+
response,
|
489
|
+
message=self.service.msg_store.location_content_transfer_complete(
|
490
|
+
pack_first_pallet.location_id,
|
491
|
+
pack_first_pallet.location_dest_id,
|
492
|
+
),
|
493
|
+
)
|
494
|
+
self.assertEqual(pack_first_pallet.qty_done, 6)
|
495
|
+
self.assertEqual(pack_first_pallet.state, "done")
|
496
|
+
self.assertEqual(pack_first_pallet.move_id.product_uom_qty, qty)
|
497
|
+
# Operator-2 with the "location content transfer" scenario scan
|
498
|
+
# the location where the second pallet is.
|
499
|
+
original_pack_transfer = pack_second_pallet.picking_id
|
500
|
+
response = self._location_content_transfer_process_line(pack_second_pallet)
|
501
|
+
new_pack_transfer = pack_second_pallet.picking_id
|
502
|
+
# Transfer hasn't been split as we were processing the last line/pallet
|
503
|
+
self.assertEqual(original_pack_transfer, new_pack_transfer)
|
504
|
+
self.assert_response_scan_destination_all(response, new_pack_transfer)
|
505
|
+
response_packages = response["data"]["scan_destination_all"]["package_levels"]
|
506
|
+
self.assertEqual(len(response_packages), 1)
|
507
|
+
self.assertEqual(
|
508
|
+
response_packages[0]["package_src"]["id"], pack_second_pallet.package_id.id
|
509
|
+
)
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .test_location_content_transfer_base import LocationContentTransferCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class TestLocationContentTransferPutaway(LocationContentTransferCommonCase):
|
10
|
+
"""Tests with putaway when using option to ignore unavailable putaway locations"""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassVars(cls, *args, **kwargs):
|
14
|
+
super().setUpClassVars(*args, **kwargs)
|
15
|
+
cls.pallets_storage_type = cls.env.ref(
|
16
|
+
"stock_storage_type.package_storage_type_pallets"
|
17
|
+
)
|
18
|
+
cls.main_pallets_location = cls.env.ref(
|
19
|
+
"stock_storage_type.stock_location_pallets"
|
20
|
+
)
|
21
|
+
cls.reserve_pallets_locations = cls.env.ref(
|
22
|
+
"stock_storage_type.stock_location_pallets_reserve"
|
23
|
+
)
|
24
|
+
cls.all_pallets_locations = (
|
25
|
+
cls.main_pallets_location.leaf_location_ids
|
26
|
+
| cls.reserve_pallets_locations.leaf_location_ids
|
27
|
+
)
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
31
|
+
super().setUpClassBaseData(*args, **kwargs)
|
32
|
+
cls.package = cls.env["stock.quant.package"].create(
|
33
|
+
{
|
34
|
+
# this will parameterize the putaway to use pallet locations,
|
35
|
+
# and if not, it will stay on the picking type's default dest.
|
36
|
+
"package_type_id": cls.pallets_storage_type.id,
|
37
|
+
}
|
38
|
+
)
|
39
|
+
cls.package2 = cls.env["stock.quant.package"].create(
|
40
|
+
{
|
41
|
+
# this will parameterize the putaway to use pallet locations,
|
42
|
+
# and if not, it will stay on the picking type's default dest.
|
43
|
+
"package_type_id": cls.pallets_storage_type.id,
|
44
|
+
}
|
45
|
+
)
|
46
|
+
# create a location to be sure it's empty
|
47
|
+
cls.test_loc = (
|
48
|
+
cls.env["stock.location"]
|
49
|
+
.sudo()
|
50
|
+
.create(
|
51
|
+
{
|
52
|
+
"location_id": cls.stock_location.id,
|
53
|
+
"name": "test",
|
54
|
+
"barcode": "test_loc",
|
55
|
+
}
|
56
|
+
)
|
57
|
+
)
|
58
|
+
cls._update_qty_in_location(
|
59
|
+
cls.test_loc, cls.product_a, 10, package=cls.package
|
60
|
+
)
|
61
|
+
cls._update_qty_in_location(
|
62
|
+
cls.test_loc, cls.product_a, 10, package=cls.package2
|
63
|
+
)
|
64
|
+
cls.menu.sudo().allow_move_create = True
|
65
|
+
cls.menu.sudo().ignore_no_putaway_available = True
|
66
|
+
cls.menu.sudo().allow_unreserve_other_moves = True
|
67
|
+
|
68
|
+
def test_normal_putaway(self):
|
69
|
+
"""Ensure putaway is applied on moves"""
|
70
|
+
response = self.service.dispatch(
|
71
|
+
"scan_location", params={"barcode": self.test_loc.barcode}
|
72
|
+
)
|
73
|
+
self.assert_response(
|
74
|
+
response,
|
75
|
+
next_state="start_single",
|
76
|
+
data=self.ANY,
|
77
|
+
)
|
78
|
+
package_level_id = response["data"]["start_single"]["package_level"]["id"]
|
79
|
+
package_level = self.env["stock.package_level"].browse(package_level_id)
|
80
|
+
self.assertIn(package_level.location_dest_id, self.all_pallets_locations)
|
81
|
+
|
82
|
+
def test_ignore_no_putaway_available(self):
|
83
|
+
"""Ignore no putaway available is activated on the menu
|
84
|
+
|
85
|
+
In this case, when no putaway is possible, the changes
|
86
|
+
are rollbacked and an error is returned.
|
87
|
+
"""
|
88
|
+
for location in self.all_pallets_locations:
|
89
|
+
package = self.env["stock.quant.package"].create(
|
90
|
+
{"package_type_id": self.pallets_storage_type.id}
|
91
|
+
)
|
92
|
+
self._update_qty_in_location(location, self.product_a, 10, package=package)
|
93
|
+
|
94
|
+
response = self.service.dispatch(
|
95
|
+
"scan_location", params={"barcode": self.test_loc.barcode}
|
96
|
+
)
|
97
|
+
self.assert_response(
|
98
|
+
response,
|
99
|
+
next_state="scan_location",
|
100
|
+
message=self.service.msg_store.no_putaway_destination_available(),
|
101
|
+
)
|
102
|
+
|
103
|
+
package_levels = self.env["stock.package_level"].search(
|
104
|
+
[("package_id", "in", (self.package.id, self.package2.id))]
|
105
|
+
)
|
106
|
+
# no package level created to move the package
|
107
|
+
self.assertFalse(package_levels)
|
108
|
+
|
109
|
+
def test_putaway_move_dest_not_child_of_picking_type_dest(self):
|
110
|
+
"""Putaway is applied on move but the destination location is not a
|
111
|
+
child of the default picking type destination location.
|
112
|
+
"""
|
113
|
+
# Change the default destination location of the picking type
|
114
|
+
# to get it outside of the putaway destination
|
115
|
+
self.picking_type.sudo().default_location_dest_id = self.main_pallets_location
|
116
|
+
# Create a standard putaway to move the package from pallet storage
|
117
|
+
# to a unrelated one (outside of the pallet storage tree)
|
118
|
+
self.env["stock.putaway.rule"].sudo().create(
|
119
|
+
{
|
120
|
+
"product_id": self.product_a.id,
|
121
|
+
"location_in_id": self.picking_type.default_location_dest_id.id,
|
122
|
+
"location_out_id": self.env.ref("stock.location_refrigerator_small").id,
|
123
|
+
}
|
124
|
+
)
|
125
|
+
# Check the result
|
126
|
+
existing_moves = self.env["stock.move"].search(
|
127
|
+
[("location_id", "=", self.test_loc.id), ("state", "=", "assigned")]
|
128
|
+
)
|
129
|
+
response = self.service.dispatch(
|
130
|
+
"scan_location", params={"barcode": self.test_loc.barcode}
|
131
|
+
)
|
132
|
+
self.assert_response(
|
133
|
+
response,
|
134
|
+
next_state="scan_location",
|
135
|
+
data=self.ANY,
|
136
|
+
message=self.service.msg_store.location_content_unable_to_transfer(
|
137
|
+
self.test_loc
|
138
|
+
),
|
139
|
+
)
|
140
|
+
current_moves = self.env["stock.move"].search(
|
141
|
+
[("location_id", "=", self.test_loc.id), ("state", "=", "assigned")]
|
142
|
+
)
|
143
|
+
self.assertEqual(existing_moves, current_moves)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright 2023 Camptocamp SA
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
3
|
+
|
4
|
+
from .test_location_content_transfer_base import LocationContentTransferCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class TestLocationContentTransferScanLocation(LocationContentTransferCommonCase):
|
10
|
+
@classmethod
|
11
|
+
def setUpClassBaseData(cls):
|
12
|
+
super().setUpClassBaseData()
|
13
|
+
# One picking with shipping policy set on "When all products are ready"
|
14
|
+
# With only one of the move available in the stock
|
15
|
+
cls.picking1 = cls._create_picking(
|
16
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10)]
|
17
|
+
)
|
18
|
+
cls.picking1.move_type = "one"
|
19
|
+
cls.move1 = cls.picking1.move_ids[0]
|
20
|
+
cls._fill_stock_for_moves(cls.move1, in_package=False, location=cls.content_loc)
|
21
|
+
cls.picking1.action_assign()
|
22
|
+
# Another picking available
|
23
|
+
picking2 = cls._create_picking(lines=[(cls.product_c, 5)])
|
24
|
+
cls._fill_stock_for_moves(picking2.move_ids, location=cls.content_loc)
|
25
|
+
picking2.action_assign()
|
26
|
+
|
27
|
+
def test_lines_returned_by_scan_location(self):
|
28
|
+
"""Check that lines from not ready pickings are not offered to work on."""
|
29
|
+
response = self.service.dispatch(
|
30
|
+
"scan_location", params={"barcode": self.content_loc.barcode}
|
31
|
+
)
|
32
|
+
lines = response["data"]["scan_destination_all"]["move_lines"]
|
33
|
+
line_ids = [line["id"] for line in lines]
|
34
|
+
self.assertTrue(self.move1.move_line_ids.id not in line_ids)
|