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,911 @@
|
|
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_cluster_picking_base import ClusterPickingCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class ClusterPickingUnloadingCommonCase(ClusterPickingCommonCase):
|
10
|
+
@classmethod
|
11
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
12
|
+
super().setUpClassBaseData(*args, **kwargs)
|
13
|
+
|
14
|
+
# activate the computation of this field, so we have a chance to
|
15
|
+
# transition to the 'show completion info' popup.
|
16
|
+
cls.picking_type.sudo().display_completion_info = True
|
17
|
+
|
18
|
+
cls.batch = cls._create_picking_batch(
|
19
|
+
[
|
20
|
+
[
|
21
|
+
cls.BatchProduct(product=cls.product_a, quantity=10),
|
22
|
+
cls.BatchProduct(product=cls.product_b, quantity=10),
|
23
|
+
],
|
24
|
+
[cls.BatchProduct(product=cls.product_a, quantity=10)],
|
25
|
+
]
|
26
|
+
)
|
27
|
+
cls._simulate_batch_selected(cls.batch)
|
28
|
+
|
29
|
+
cls.one_line_picking = cls.batch.picking_ids.filtered(
|
30
|
+
lambda picking: len(picking.move_ids) == 1
|
31
|
+
)
|
32
|
+
cls.two_lines_picking = cls.batch.picking_ids.filtered(
|
33
|
+
lambda picking: len(picking.move_ids) == 2
|
34
|
+
)
|
35
|
+
two_lines_product_a = cls.two_lines_picking.move_line_ids.filtered(
|
36
|
+
lambda line: line.product_id == cls.product_a
|
37
|
+
)
|
38
|
+
two_lines_product_b = cls.two_lines_picking.move_line_ids - two_lines_product_a
|
39
|
+
# force order of move lines to use in tests
|
40
|
+
cls.move_lines = (
|
41
|
+
cls.one_line_picking.move_line_ids
|
42
|
+
+ two_lines_product_a
|
43
|
+
+ two_lines_product_b
|
44
|
+
)
|
45
|
+
|
46
|
+
cls.bin1 = cls.env["stock.quant.package"].create({})
|
47
|
+
cls.bin2 = cls.env["stock.quant.package"].create({})
|
48
|
+
cls.packing_a_location = (
|
49
|
+
cls.env["stock.location"]
|
50
|
+
.sudo()
|
51
|
+
.create(
|
52
|
+
{
|
53
|
+
"name": "Packing A",
|
54
|
+
"barcode": "Packing-A",
|
55
|
+
"location_id": cls.packing_location.id,
|
56
|
+
}
|
57
|
+
)
|
58
|
+
)
|
59
|
+
cls.packing_b_location = (
|
60
|
+
cls.env["stock.location"]
|
61
|
+
.sudo()
|
62
|
+
.create(
|
63
|
+
{
|
64
|
+
"name": "Packing B",
|
65
|
+
"barcode": "Packing-B",
|
66
|
+
"location_id": cls.packing_location.id,
|
67
|
+
}
|
68
|
+
)
|
69
|
+
)
|
70
|
+
|
71
|
+
|
72
|
+
class ClusterPickingPrepareUnloadCase(ClusterPickingUnloadingCommonCase):
|
73
|
+
"""Tests covering the /prepare_unload endpoint
|
74
|
+
|
75
|
+
Destination packages have been set on all the move lines of the batch.
|
76
|
+
The unload operation will start, but we have 2 paths for this:
|
77
|
+
|
78
|
+
1. unload all the destination packages at the same place
|
79
|
+
2. unload the destination packages one by one at different places
|
80
|
+
|
81
|
+
By default, if all the move lines have the same destination, the
|
82
|
+
first path is used. A flag on the batch picking keeps track of which
|
83
|
+
path is used.
|
84
|
+
"""
|
85
|
+
|
86
|
+
def test_prepare_unload_all_same_dest(self):
|
87
|
+
"""All move lines have the same destination location"""
|
88
|
+
move_lines = self.move_lines
|
89
|
+
self._set_dest_package_and_done(move_lines[:2], self.bin1)
|
90
|
+
self._set_dest_package_and_done(move_lines[2:], self.bin2)
|
91
|
+
move_lines.write({"location_dest_id": self.packing_location.id})
|
92
|
+
response = self.service.dispatch(
|
93
|
+
"prepare_unload", params={"picking_batch_id": self.batch.id}
|
94
|
+
)
|
95
|
+
location = self.packing_location
|
96
|
+
data = self._data_for_batch(self.batch, location)
|
97
|
+
self.assert_response(
|
98
|
+
response,
|
99
|
+
next_state="unload_all",
|
100
|
+
data=data,
|
101
|
+
)
|
102
|
+
|
103
|
+
def test_prepare_unload_different_dest(self):
|
104
|
+
"""All move lines have different destination locations"""
|
105
|
+
move_lines = self.move_lines
|
106
|
+
self._set_dest_package_and_done(move_lines[:2], self.bin1)
|
107
|
+
self._set_dest_package_and_done(move_lines[2:], self.bin2)
|
108
|
+
move_lines[:2].write({"location_dest_id": self.packing_a_location.id})
|
109
|
+
move_lines[2:].write({"location_dest_id": self.packing_b_location.id})
|
110
|
+
response = self.service.dispatch(
|
111
|
+
"prepare_unload", params={"picking_batch_id": self.batch.id}
|
112
|
+
)
|
113
|
+
first_line = move_lines[0]
|
114
|
+
location = first_line.location_dest_id
|
115
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
116
|
+
self.assert_response(
|
117
|
+
response,
|
118
|
+
next_state="unload_single",
|
119
|
+
data=data,
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
class ClusterPickingSetDestinationAllCase(ClusterPickingUnloadingCommonCase):
|
124
|
+
"""Tests covering the /set_destination_all endpoint
|
125
|
+
|
126
|
+
All the picked lines go to the same destination, a single call to this
|
127
|
+
endpoint set them as "unloaded" and set the destination. When the last
|
128
|
+
available line of a picking is unloaded, the picking is set to 'done'.
|
129
|
+
"""
|
130
|
+
|
131
|
+
def test_set_destination_all_ok(self):
|
132
|
+
"""Set destination on all lines for the full batch and end the process"""
|
133
|
+
move_lines = self.move_lines
|
134
|
+
# put destination packages, the whole quantity on lines and a similar
|
135
|
+
# destination (when /set_destination_all is called, all the lines to
|
136
|
+
# unload must have the same destination)
|
137
|
+
self._set_dest_package_and_done(move_lines[:2], self.bin1)
|
138
|
+
self._set_dest_package_and_done(move_lines[2:], self.bin2)
|
139
|
+
move_lines.write({"location_dest_id": self.packing_location.id})
|
140
|
+
|
141
|
+
response = self.service.dispatch(
|
142
|
+
"set_destination_all",
|
143
|
+
params={
|
144
|
+
"picking_batch_id": self.batch.id,
|
145
|
+
"barcode": self.packing_location.barcode,
|
146
|
+
},
|
147
|
+
)
|
148
|
+
# since the whole batch is complete, we expect the batch and all
|
149
|
+
# pickings to be 'done'
|
150
|
+
self.assertRecordValues(
|
151
|
+
move_lines.mapped("picking_id"), [{"state": "done"}, {"state": "done"}]
|
152
|
+
)
|
153
|
+
self.assertRecordValues(
|
154
|
+
move_lines,
|
155
|
+
[
|
156
|
+
{
|
157
|
+
"shopfloor_unloaded": True,
|
158
|
+
"qty_done": 10,
|
159
|
+
"state": "done",
|
160
|
+
"location_dest_id": self.packing_location.id,
|
161
|
+
},
|
162
|
+
{
|
163
|
+
"shopfloor_unloaded": True,
|
164
|
+
"qty_done": 10,
|
165
|
+
"state": "done",
|
166
|
+
"location_dest_id": self.packing_location.id,
|
167
|
+
},
|
168
|
+
{
|
169
|
+
"shopfloor_unloaded": True,
|
170
|
+
"qty_done": 10,
|
171
|
+
"state": "done",
|
172
|
+
"location_dest_id": self.packing_location.id,
|
173
|
+
},
|
174
|
+
],
|
175
|
+
)
|
176
|
+
self.assertRecordValues(self.batch, [{"state": "done"}])
|
177
|
+
self.assert_response(
|
178
|
+
response,
|
179
|
+
next_state="start",
|
180
|
+
message={"message_type": "success", "body": "Batch Transfer complete"},
|
181
|
+
)
|
182
|
+
|
183
|
+
def test_set_destination_all_remaining_lines(self):
|
184
|
+
"""Set destination on all lines for a part of the batch"""
|
185
|
+
# Put destination packages, the whole quantity on lines and a similar
|
186
|
+
# destination (when /set_destination_all is called, all the lines to
|
187
|
+
# unload must have the same destination).
|
188
|
+
# However, we keep a line without qty_done and destination package,
|
189
|
+
# so when the dest location is set, the endpoint should route back
|
190
|
+
# to the 'start_line' state to work on the remaining line.
|
191
|
+
lines_to_unload = self.move_lines[:2]
|
192
|
+
self._set_dest_package_and_done(lines_to_unload, self.bin1)
|
193
|
+
lines_to_unload.write({"location_dest_id": self.packing_location.id})
|
194
|
+
|
195
|
+
response = self.service.dispatch(
|
196
|
+
"set_destination_all",
|
197
|
+
params={
|
198
|
+
"picking_batch_id": self.batch.id,
|
199
|
+
"barcode": self.packing_location.barcode,
|
200
|
+
},
|
201
|
+
)
|
202
|
+
# Since the whole batch is not complete, state should not be done.
|
203
|
+
# The picking with one line should be "done" because we unloaded its line.
|
204
|
+
# The second one still has a line to pick.
|
205
|
+
self.assertRecordValues(self.one_line_picking, [{"state": "done"}])
|
206
|
+
self.assertRecordValues(self.two_lines_picking, [{"state": "assigned"}])
|
207
|
+
self.assertRecordValues(
|
208
|
+
self.move_lines,
|
209
|
+
[
|
210
|
+
{
|
211
|
+
"shopfloor_unloaded": True,
|
212
|
+
"qty_done": 10,
|
213
|
+
"state": "done",
|
214
|
+
"picking_id": self.one_line_picking.id,
|
215
|
+
"location_dest_id": self.packing_location.id,
|
216
|
+
},
|
217
|
+
{
|
218
|
+
"shopfloor_unloaded": True,
|
219
|
+
"qty_done": 10,
|
220
|
+
# will be done when the second line of the picking is unloaded
|
221
|
+
"state": "assigned",
|
222
|
+
"picking_id": self.two_lines_picking.id,
|
223
|
+
"location_dest_id": self.packing_location.id,
|
224
|
+
},
|
225
|
+
{
|
226
|
+
"shopfloor_unloaded": False,
|
227
|
+
"qty_done": 0,
|
228
|
+
"state": "assigned",
|
229
|
+
"picking_id": self.two_lines_picking.id,
|
230
|
+
"location_dest_id": self.packing_location.id,
|
231
|
+
},
|
232
|
+
],
|
233
|
+
)
|
234
|
+
self.assertRecordValues(self.batch, [{"state": "in_progress"}])
|
235
|
+
|
236
|
+
self.assert_response(
|
237
|
+
# the remaining move line still needs to be picked
|
238
|
+
response,
|
239
|
+
next_state="start_line",
|
240
|
+
data=self._line_data(self.move_lines[2]),
|
241
|
+
message={"body": "Batch Transfer line done", "message_type": "success"},
|
242
|
+
)
|
243
|
+
|
244
|
+
def test_set_destination_all_picking_unassigned(self):
|
245
|
+
"""Set destination on lines for some transfers of the batch.
|
246
|
+
|
247
|
+
The remaining transfers stay as unavailable (confirmed) and are removed
|
248
|
+
from the batch when this one is validated.
|
249
|
+
The remaining transfers will be processed later in a new batch.
|
250
|
+
"""
|
251
|
+
self.batch.picking_ids.do_unreserve()
|
252
|
+
location = self.one_line_picking.location_id
|
253
|
+
product = self.one_line_picking.move_ids.product_id
|
254
|
+
qty = self.one_line_picking.move_ids.product_uom_qty
|
255
|
+
self._update_qty_in_location(location, product, qty)
|
256
|
+
self.one_line_picking.action_assign()
|
257
|
+
# Prepare lines to process
|
258
|
+
lines = self.one_line_picking.move_line_ids
|
259
|
+
self._set_dest_package_and_done(lines, self.bin1)
|
260
|
+
lines.write({"location_dest_id": self.packing_location.id})
|
261
|
+
|
262
|
+
response = self.service.dispatch(
|
263
|
+
"set_destination_all",
|
264
|
+
params={
|
265
|
+
"picking_batch_id": self.batch.id,
|
266
|
+
"barcode": self.packing_location.barcode,
|
267
|
+
},
|
268
|
+
)
|
269
|
+
# The batch should be done with only one picking.
|
270
|
+
# The remaining picking has been removed from the current batch
|
271
|
+
self.assertRecordValues(self.one_line_picking, [{"state": "done"}])
|
272
|
+
self.assertRecordValues(self.two_lines_picking, [{"state": "confirmed"}])
|
273
|
+
self.assertRecordValues(self.batch, [{"state": "done"}])
|
274
|
+
self.assertEqual(self.one_line_picking.batch_id, self.batch)
|
275
|
+
self.assertFalse(self.two_lines_picking.batch_id)
|
276
|
+
|
277
|
+
self.assert_response(
|
278
|
+
response,
|
279
|
+
next_state="start",
|
280
|
+
message=self.service.msg_store.batch_transfer_complete(),
|
281
|
+
)
|
282
|
+
|
283
|
+
def test_set_destination_all_but_different_dest(self):
|
284
|
+
"""Endpoint was called but destinations are different"""
|
285
|
+
move_lines = self.move_lines
|
286
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
287
|
+
move_lines[:2].write({"location_dest_id": self.packing_a_location.id})
|
288
|
+
move_lines[2:].write({"location_dest_id": self.packing_b_location.id})
|
289
|
+
|
290
|
+
response = self.service.dispatch(
|
291
|
+
"set_destination_all",
|
292
|
+
params={
|
293
|
+
"picking_batch_id": self.batch.id,
|
294
|
+
"barcode": self.packing_location.barcode,
|
295
|
+
},
|
296
|
+
)
|
297
|
+
location = move_lines[0].location_dest_id
|
298
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
299
|
+
self.assert_response(
|
300
|
+
response,
|
301
|
+
next_state="unload_single",
|
302
|
+
data=data,
|
303
|
+
)
|
304
|
+
|
305
|
+
def test_set_destination_all_error_location_not_found(self):
|
306
|
+
"""Endpoint called with a barcode not existing for a location"""
|
307
|
+
move_lines = self.move_lines
|
308
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
309
|
+
move_lines.write({"location_dest_id": self.packing_a_location.id})
|
310
|
+
|
311
|
+
response = self.service.dispatch(
|
312
|
+
"set_destination_all",
|
313
|
+
params={"picking_batch_id": self.batch.id, "barcode": "NOTFOUND"},
|
314
|
+
)
|
315
|
+
location = move_lines[0].location_dest_id
|
316
|
+
data = self._data_for_batch(self.batch, location)
|
317
|
+
self.assert_response(
|
318
|
+
response,
|
319
|
+
next_state="unload_all",
|
320
|
+
data=data,
|
321
|
+
message={
|
322
|
+
"message_type": "error",
|
323
|
+
"body": "No location found for this barcode.",
|
324
|
+
},
|
325
|
+
)
|
326
|
+
|
327
|
+
def test_set_destination_all_error_location_invalid(self):
|
328
|
+
"""Endpoint called with a barcode for an invalid location
|
329
|
+
|
330
|
+
It is invalid when the location is not the destination location or
|
331
|
+
sublocation of the picking type.
|
332
|
+
"""
|
333
|
+
move_lines = self.move_lines
|
334
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
335
|
+
move_lines.write({"location_dest_id": self.packing_a_location.id})
|
336
|
+
|
337
|
+
response = self.service.dispatch(
|
338
|
+
"set_destination_all",
|
339
|
+
params={
|
340
|
+
"picking_batch_id": self.batch.id,
|
341
|
+
"barcode": self.dispatch_location.barcode,
|
342
|
+
},
|
343
|
+
)
|
344
|
+
location = move_lines[0].location_dest_id
|
345
|
+
data = self._data_for_batch(self.batch, location)
|
346
|
+
self.assert_response(
|
347
|
+
response,
|
348
|
+
next_state="unload_all",
|
349
|
+
data=data,
|
350
|
+
message={"message_type": "error", "body": "You cannot place it here"},
|
351
|
+
)
|
352
|
+
|
353
|
+
def test_set_destination_all_error_location_move_invalid(self):
|
354
|
+
"""Endpoint called with a barcode for an invalid location
|
355
|
+
|
356
|
+
It is invalid when the location is not a sublocation of the picking
|
357
|
+
or move destination
|
358
|
+
"""
|
359
|
+
move_lines = self.move_lines
|
360
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
361
|
+
move_lines[0].move_id.location_dest_id = self.packing_a_location
|
362
|
+
move_lines[0].picking_id.location_dest_id = self.packing_a_location
|
363
|
+
|
364
|
+
response = self.service.dispatch(
|
365
|
+
"set_destination_all",
|
366
|
+
params={
|
367
|
+
"picking_batch_id": self.batch.id,
|
368
|
+
"barcode": self.packing_b_location.barcode,
|
369
|
+
},
|
370
|
+
)
|
371
|
+
location = move_lines[0].location_dest_id
|
372
|
+
data = self._data_for_batch(self.batch, location)
|
373
|
+
self.assert_response(
|
374
|
+
response,
|
375
|
+
next_state="unload_all",
|
376
|
+
data=data,
|
377
|
+
message=self.service.msg_store.dest_location_not_allowed(),
|
378
|
+
)
|
379
|
+
|
380
|
+
def test_set_destination_all_need_confirmation(self):
|
381
|
+
"""Endpoint called with a barcode for another (valid) location"""
|
382
|
+
move_lines = self.move_lines
|
383
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
384
|
+
move_lines.write({"location_dest_id": self.packing_a_location.id})
|
385
|
+
|
386
|
+
response = self.service.dispatch(
|
387
|
+
"set_destination_all",
|
388
|
+
params={
|
389
|
+
"picking_batch_id": self.batch.id,
|
390
|
+
"barcode": self.packing_b_location.barcode,
|
391
|
+
},
|
392
|
+
)
|
393
|
+
location = move_lines[0].location_dest_id
|
394
|
+
data = self._data_for_batch(self.batch, location)
|
395
|
+
self.assert_response(
|
396
|
+
response,
|
397
|
+
next_state="confirm_unload_all",
|
398
|
+
data=data,
|
399
|
+
)
|
400
|
+
|
401
|
+
def test_set_destination_all_with_confirmation(self):
|
402
|
+
"""Endpoint called with a barcode for another (valid) location, confirm"""
|
403
|
+
move_lines = self.move_lines
|
404
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
405
|
+
move_lines.write({"location_dest_id": self.packing_a_location.id})
|
406
|
+
|
407
|
+
response = self.service.dispatch(
|
408
|
+
"set_destination_all",
|
409
|
+
params={
|
410
|
+
"picking_batch_id": self.batch.id,
|
411
|
+
"barcode": self.packing_b_location.barcode,
|
412
|
+
"confirmation": True,
|
413
|
+
},
|
414
|
+
)
|
415
|
+
self.assertRecordValues(
|
416
|
+
move_lines,
|
417
|
+
[
|
418
|
+
{"location_dest_id": self.packing_b_location.id},
|
419
|
+
{"location_dest_id": self.packing_b_location.id},
|
420
|
+
{"location_dest_id": self.packing_b_location.id},
|
421
|
+
],
|
422
|
+
)
|
423
|
+
self.assert_response(
|
424
|
+
response,
|
425
|
+
next_state="start",
|
426
|
+
message={"message_type": "success", "body": "Batch Transfer complete"},
|
427
|
+
)
|
428
|
+
|
429
|
+
|
430
|
+
class ClusterPickingUnloadSplitCase(ClusterPickingUnloadingCommonCase):
|
431
|
+
"""Tests covering the /unload_split endpoint
|
432
|
+
|
433
|
+
All the destinations of the bins were the same so the "unload all" screen
|
434
|
+
was presented to the user, but they want different destination, so they hit
|
435
|
+
the "split" button. From now on, the workflow should use the "unload single"
|
436
|
+
screen even if the destinations are the same.
|
437
|
+
"""
|
438
|
+
|
439
|
+
def test_unload_split_ok(self):
|
440
|
+
"""Call /unload_split and continue to unload single"""
|
441
|
+
move_lines = self.move_lines
|
442
|
+
# put destination packages, the whole quantity on lines and a similar
|
443
|
+
# destination (when /set_destination_all is called, all the lines to
|
444
|
+
# unload must have the same destination)
|
445
|
+
self._set_dest_package_and_done(move_lines, self.bin1)
|
446
|
+
move_lines.write({"location_dest_id": self.packing_location.id})
|
447
|
+
|
448
|
+
response = self.service.dispatch(
|
449
|
+
"unload_split", params={"picking_batch_id": self.batch.id}
|
450
|
+
)
|
451
|
+
location = move_lines[0].location_dest_id
|
452
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
453
|
+
self.assert_response(
|
454
|
+
# the remaining move line still needs to be picked
|
455
|
+
response,
|
456
|
+
next_state="unload_single",
|
457
|
+
data=data,
|
458
|
+
)
|
459
|
+
|
460
|
+
|
461
|
+
class ClusterPickingUnloadScanPackCase(ClusterPickingUnloadingCommonCase):
|
462
|
+
"""Tests covering the /unload_scan_pack endpoint
|
463
|
+
|
464
|
+
Goods have been put in the package bins, they have different destinations
|
465
|
+
or /unload_split has been called, now user has to unload package per
|
466
|
+
package. For this, they'll first scan the bin package, which will call the
|
467
|
+
endpoint /unload_scan_pack. (second step will be to set the destination
|
468
|
+
with /unload_scan_destination, in a different test case)
|
469
|
+
"""
|
470
|
+
|
471
|
+
@classmethod
|
472
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
473
|
+
super().setUpClassBaseData(*args, **kwargs)
|
474
|
+
cls._set_dest_package_and_done(cls.move_lines, cls.bin1)
|
475
|
+
cls.move_lines[:2].write({"location_dest_id": cls.packing_a_location.id})
|
476
|
+
cls.move_lines[2:].write({"location_dest_id": cls.packing_b_location.id})
|
477
|
+
|
478
|
+
def test_unload_scan_pack_ok(self):
|
479
|
+
"""Endpoint /unload_scan_pack is called, result ok"""
|
480
|
+
response = self.service.dispatch(
|
481
|
+
"unload_scan_pack",
|
482
|
+
params={
|
483
|
+
"picking_batch_id": self.batch.id,
|
484
|
+
"package_id": self.bin1.id,
|
485
|
+
"barcode": self.bin1.name,
|
486
|
+
},
|
487
|
+
)
|
488
|
+
location = self.move_lines[0].location_dest_id
|
489
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
490
|
+
self.assert_response(
|
491
|
+
response,
|
492
|
+
next_state="unload_set_destination",
|
493
|
+
data=data,
|
494
|
+
)
|
495
|
+
|
496
|
+
def test_unload_scan_pack_wrong_barcode(self):
|
497
|
+
"""Endpoint /unload_scan_pack is called, wrong barcode scanned"""
|
498
|
+
response = self.service.dispatch(
|
499
|
+
"unload_scan_pack",
|
500
|
+
params={
|
501
|
+
"picking_batch_id": self.batch.id,
|
502
|
+
"package_id": self.bin1.id,
|
503
|
+
"barcode": self.bin2.name,
|
504
|
+
},
|
505
|
+
)
|
506
|
+
location = self.move_lines[0].location_dest_id
|
507
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
508
|
+
self.assert_response(
|
509
|
+
response,
|
510
|
+
next_state="unload_single",
|
511
|
+
data=data,
|
512
|
+
message={"message_type": "error", "body": "Wrong bin"},
|
513
|
+
)
|
514
|
+
|
515
|
+
|
516
|
+
class ClusterPickingUnloadScanDestinationCase(ClusterPickingUnloadingCommonCase):
|
517
|
+
"""Tests covering the /unload_scan_destination endpoint
|
518
|
+
|
519
|
+
Goods have been put in the package bins, they have different destinations
|
520
|
+
or /unload_split has been called, now user has to unload package per
|
521
|
+
package. For this, they'll first scanned the bin package already (endpoint
|
522
|
+
/unload_scan_pack), now they have to set the destination with
|
523
|
+
/unload_scan_destination for the scanned pack.
|
524
|
+
"""
|
525
|
+
|
526
|
+
@classmethod
|
527
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
528
|
+
super().setUpClassBaseData(*args, **kwargs)
|
529
|
+
cls.bin1_lines = cls.move_lines[:1]
|
530
|
+
cls.bin2_lines = cls.move_lines[1:]
|
531
|
+
cls._set_dest_package_and_done(cls.bin1_lines, cls.bin1)
|
532
|
+
cls._set_dest_package_and_done(cls.bin2_lines, cls.bin2)
|
533
|
+
cls.bin1_lines.write({"location_dest_id": cls.packing_a_location.id})
|
534
|
+
cls.bin2_lines.write({"location_dest_id": cls.packing_b_location.id})
|
535
|
+
cls.one_line_picking = cls.batch.picking_ids.filtered(
|
536
|
+
lambda picking: len(picking.move_ids) == 1
|
537
|
+
)
|
538
|
+
cls.two_lines_picking = cls.batch.picking_ids.filtered(
|
539
|
+
lambda picking: len(picking.move_ids) == 2
|
540
|
+
)
|
541
|
+
|
542
|
+
def test_unload_scan_destination_ok(self):
|
543
|
+
"""Endpoint /unload_scan_destination is called, result ok"""
|
544
|
+
dest_location = self.bin1_lines[0].location_dest_id
|
545
|
+
|
546
|
+
response = self.service.dispatch(
|
547
|
+
"unload_scan_destination",
|
548
|
+
params={
|
549
|
+
"picking_batch_id": self.batch.id,
|
550
|
+
"package_id": self.bin1.id,
|
551
|
+
"barcode": dest_location.barcode,
|
552
|
+
},
|
553
|
+
)
|
554
|
+
|
555
|
+
# The scan of destination set 'unloaded' to True to track the fact
|
556
|
+
# that we set the destination for the line. In this case, the line
|
557
|
+
# and the stock.picking are 'done' because all the lines of the picking
|
558
|
+
# have been unloaded
|
559
|
+
self.assertRecordValues(self.one_line_picking, [{"state": "done"}])
|
560
|
+
self.assertRecordValues(self.two_lines_picking, [{"state": "assigned"}])
|
561
|
+
self.assertRecordValues(
|
562
|
+
self.bin1_lines,
|
563
|
+
[
|
564
|
+
{
|
565
|
+
"shopfloor_unloaded": True,
|
566
|
+
"qty_done": 10,
|
567
|
+
"state": "done",
|
568
|
+
"picking_id": self.one_line_picking.id,
|
569
|
+
"location_dest_id": self.packing_a_location.id,
|
570
|
+
}
|
571
|
+
],
|
572
|
+
)
|
573
|
+
self.assertRecordValues(
|
574
|
+
self.bin2_lines,
|
575
|
+
[
|
576
|
+
{
|
577
|
+
"shopfloor_unloaded": False,
|
578
|
+
"qty_done": 10,
|
579
|
+
"state": "assigned",
|
580
|
+
"picking_id": self.two_lines_picking.id,
|
581
|
+
"location_dest_id": self.packing_b_location.id,
|
582
|
+
},
|
583
|
+
{
|
584
|
+
"shopfloor_unloaded": False,
|
585
|
+
"qty_done": 10,
|
586
|
+
"state": "assigned",
|
587
|
+
"picking_id": self.two_lines_picking.id,
|
588
|
+
"location_dest_id": self.packing_b_location.id,
|
589
|
+
},
|
590
|
+
],
|
591
|
+
)
|
592
|
+
self.assertRecordValues(self.batch, [{"state": "in_progress"}])
|
593
|
+
|
594
|
+
location = self.bin2_lines[0].location_dest_id
|
595
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin2)
|
596
|
+
self.assert_response(
|
597
|
+
response,
|
598
|
+
next_state="unload_single",
|
599
|
+
data=data,
|
600
|
+
)
|
601
|
+
|
602
|
+
def test_scan_destination_unload_package_enabled(self):
|
603
|
+
"""Endpoint /unload_scan_destination is called, unload_package_at_destination
|
604
|
+
is enabled, lines.result_package_id should be False"""
|
605
|
+
dest_location = self.bin1_lines[0].location_dest_id
|
606
|
+
# Default behavior, result_package_id is kept when package in unloaded
|
607
|
+
self.service.dispatch(
|
608
|
+
"unload_scan_destination",
|
609
|
+
params={
|
610
|
+
"picking_batch_id": self.batch.id,
|
611
|
+
"package_id": self.bin1.id,
|
612
|
+
"barcode": dest_location.barcode,
|
613
|
+
"confirmation": True,
|
614
|
+
},
|
615
|
+
)
|
616
|
+
self.assertRecordValues(
|
617
|
+
self.bin1_lines,
|
618
|
+
[
|
619
|
+
{
|
620
|
+
"result_package_id": self.bin1.id,
|
621
|
+
}
|
622
|
+
],
|
623
|
+
)
|
624
|
+
# Now, if `unload_package_at_destination` is enabled, result_package_id
|
625
|
+
# should be set to False
|
626
|
+
self.menu.sudo().write({"unload_package_at_destination": True})
|
627
|
+
self.service.dispatch(
|
628
|
+
"unload_scan_destination",
|
629
|
+
params={
|
630
|
+
"picking_batch_id": self.batch.id,
|
631
|
+
"package_id": self.bin2.id,
|
632
|
+
"barcode": dest_location.barcode,
|
633
|
+
"confirmation": True,
|
634
|
+
},
|
635
|
+
)
|
636
|
+
self.assertRecordValues(
|
637
|
+
self.bin2_lines,
|
638
|
+
[
|
639
|
+
{
|
640
|
+
"result_package_id": False,
|
641
|
+
},
|
642
|
+
{
|
643
|
+
"result_package_id": False,
|
644
|
+
},
|
645
|
+
],
|
646
|
+
)
|
647
|
+
|
648
|
+
def test_unload_scan_destination_one_line_of_picking_only(self):
|
649
|
+
"""Endpoint /unload_scan_destination is called, only one line of picking"""
|
650
|
+
# For this test, we assume the move in bin1 is already done.
|
651
|
+
self.one_line_picking._action_done()
|
652
|
+
# And for the second picking, we put one line bin2 and one line in bin3
|
653
|
+
# so the user would have to go through 2 screens for each pack.
|
654
|
+
# After scanning and setting the destination for bin2, the picking will
|
655
|
+
# still be "assigned" and they'll have to scan bin3 (but this test stops
|
656
|
+
# at bin2)
|
657
|
+
bin3 = self.env["stock.quant.package"].create({})
|
658
|
+
bin2_line = self.bin2_lines[0]
|
659
|
+
bin3_line = self.bin2_lines[1]
|
660
|
+
self._set_dest_package_and_done(bin3_line, bin3)
|
661
|
+
|
662
|
+
dest_location = bin2_line.location_dest_id
|
663
|
+
|
664
|
+
response = self.service.dispatch(
|
665
|
+
"unload_scan_destination",
|
666
|
+
params={
|
667
|
+
"picking_batch_id": self.batch.id,
|
668
|
+
"package_id": self.bin2.id,
|
669
|
+
"barcode": dest_location.barcode,
|
670
|
+
},
|
671
|
+
)
|
672
|
+
|
673
|
+
# The scan of destination set 'unloaded' to True to track the fact
|
674
|
+
# that we set the destination for the line. The picking is still
|
675
|
+
# assigned because the second line still has to be unloaded.
|
676
|
+
self.assertRecordValues(self.two_lines_picking, [{"state": "assigned"}])
|
677
|
+
self.assertRecordValues(
|
678
|
+
bin2_line,
|
679
|
+
[
|
680
|
+
{
|
681
|
+
"shopfloor_unloaded": True,
|
682
|
+
"qty_done": 10,
|
683
|
+
"state": "assigned",
|
684
|
+
"picking_id": self.two_lines_picking.id,
|
685
|
+
"location_dest_id": self.packing_b_location.id,
|
686
|
+
}
|
687
|
+
],
|
688
|
+
)
|
689
|
+
self.assertRecordValues(
|
690
|
+
bin3_line,
|
691
|
+
[
|
692
|
+
{
|
693
|
+
"shopfloor_unloaded": False,
|
694
|
+
"qty_done": 10,
|
695
|
+
"state": "assigned",
|
696
|
+
"picking_id": self.two_lines_picking.id,
|
697
|
+
"location_dest_id": self.packing_b_location.id,
|
698
|
+
}
|
699
|
+
],
|
700
|
+
)
|
701
|
+
self.assertRecordValues(self.batch, [{"state": "in_progress"}])
|
702
|
+
location = bin3_line.location_dest_id
|
703
|
+
data = self._data_for_batch(self.batch, location, pack=bin3)
|
704
|
+
self.assert_response(
|
705
|
+
response,
|
706
|
+
next_state="unload_single",
|
707
|
+
data=data,
|
708
|
+
)
|
709
|
+
|
710
|
+
def test_unload_scan_destination_last_line(self):
|
711
|
+
"""Endpoint /unload_scan_destination is called on last line"""
|
712
|
+
# For this test, we assume the move in bin1 is already done.
|
713
|
+
self.one_line_picking._action_done()
|
714
|
+
# And for the second picking, bin2 was already unloaded,
|
715
|
+
# remains bin3 to unload.
|
716
|
+
bin3 = self.env["stock.quant.package"].create({})
|
717
|
+
bin2_line = self.bin2_lines[0]
|
718
|
+
bin3_line = self.bin2_lines[1]
|
719
|
+
self._set_dest_package_and_done(bin3_line, bin3)
|
720
|
+
bin2_line.shopfloor_unloaded = True
|
721
|
+
|
722
|
+
dest_location = bin3_line.location_dest_id
|
723
|
+
|
724
|
+
response = self.service.dispatch(
|
725
|
+
"unload_scan_destination",
|
726
|
+
params={
|
727
|
+
"picking_batch_id": self.batch.id,
|
728
|
+
"package_id": bin3.id,
|
729
|
+
"barcode": dest_location.barcode,
|
730
|
+
},
|
731
|
+
)
|
732
|
+
|
733
|
+
# The scan of destination set 'unloaded' to True to track the fact
|
734
|
+
# that we set the destination for the line. The picking is done
|
735
|
+
# because all the lines have been unloaded
|
736
|
+
self.assertRecordValues(self.two_lines_picking, [{"state": "done"}])
|
737
|
+
self.assertRecordValues(
|
738
|
+
bin3_line,
|
739
|
+
[
|
740
|
+
{
|
741
|
+
"shopfloor_unloaded": True,
|
742
|
+
"qty_done": 10,
|
743
|
+
"state": "done",
|
744
|
+
"picking_id": self.two_lines_picking.id,
|
745
|
+
"location_dest_id": self.packing_b_location.id,
|
746
|
+
}
|
747
|
+
],
|
748
|
+
)
|
749
|
+
self.assertRecordValues(self.batch, [{"state": "done"}])
|
750
|
+
|
751
|
+
self.assert_response(
|
752
|
+
response,
|
753
|
+
next_state="start",
|
754
|
+
message={"body": "Batch Transfer complete", "message_type": "success"},
|
755
|
+
)
|
756
|
+
|
757
|
+
def test_unload_scan_destination_error_location_not_found(self):
|
758
|
+
"""Endpoint called with a barcode not existing for a location"""
|
759
|
+
response = self.service.dispatch(
|
760
|
+
"unload_scan_destination",
|
761
|
+
params={
|
762
|
+
"picking_batch_id": self.batch.id,
|
763
|
+
"package_id": self.bin1.id,
|
764
|
+
"barcode": "¤",
|
765
|
+
},
|
766
|
+
)
|
767
|
+
location = self.bin1_lines[0].location_dest_id
|
768
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
769
|
+
self.assert_response(
|
770
|
+
response,
|
771
|
+
next_state="unload_set_destination",
|
772
|
+
data=data,
|
773
|
+
message={
|
774
|
+
"message_type": "error",
|
775
|
+
"body": "No location found for this barcode.",
|
776
|
+
},
|
777
|
+
)
|
778
|
+
|
779
|
+
def test_unload_scan_destination_error_location_invalid(self):
|
780
|
+
"""Endpoint called with a barcode for an invalid location
|
781
|
+
|
782
|
+
It is invalid when the location is not the destination location or
|
783
|
+
sublocation of the picking type.
|
784
|
+
"""
|
785
|
+
response = self.service.dispatch(
|
786
|
+
"unload_scan_destination",
|
787
|
+
params={
|
788
|
+
"picking_batch_id": self.batch.id,
|
789
|
+
"package_id": self.bin1.id,
|
790
|
+
"barcode": self.dispatch_location.barcode,
|
791
|
+
},
|
792
|
+
)
|
793
|
+
location = self.bin1_lines[0].location_dest_id
|
794
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
795
|
+
self.assert_response(
|
796
|
+
response,
|
797
|
+
next_state="unload_set_destination",
|
798
|
+
data=data,
|
799
|
+
message={"message_type": "error", "body": "You cannot place it here"},
|
800
|
+
)
|
801
|
+
|
802
|
+
def test_unload_scan_destination_error_location_move_invalid(self):
|
803
|
+
"""Endpoint called with a barcode for an invalid location
|
804
|
+
|
805
|
+
It is invalid when the location is not a sublocation of the picking
|
806
|
+
or move destination
|
807
|
+
"""
|
808
|
+
self.bin1_lines[0].picking_id.location_dest_id = self.packing_a_location
|
809
|
+
response = self.service.dispatch(
|
810
|
+
"unload_scan_destination",
|
811
|
+
params={
|
812
|
+
"picking_batch_id": self.batch.id,
|
813
|
+
"package_id": self.bin1.id,
|
814
|
+
"barcode": self.packing_b_location.barcode,
|
815
|
+
},
|
816
|
+
)
|
817
|
+
location = self.bin1_lines[0].location_dest_id
|
818
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
819
|
+
self.assert_response(
|
820
|
+
response,
|
821
|
+
next_state="unload_set_destination",
|
822
|
+
data=data,
|
823
|
+
message=self.service.msg_store.dest_location_not_allowed(),
|
824
|
+
)
|
825
|
+
|
826
|
+
def test_unload_scan_destination_need_confirmation(self):
|
827
|
+
"""Endpoint called with a barcode for another (valid) location"""
|
828
|
+
response = self.service.dispatch(
|
829
|
+
"unload_scan_destination",
|
830
|
+
params={
|
831
|
+
"picking_batch_id": self.batch.id,
|
832
|
+
"package_id": self.bin1.id,
|
833
|
+
"barcode": self.packing_b_location.barcode,
|
834
|
+
},
|
835
|
+
)
|
836
|
+
location = self.bin1_lines[0].location_dest_id
|
837
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin1)
|
838
|
+
self.assert_response(
|
839
|
+
response,
|
840
|
+
next_state="confirm_unload_set_destination",
|
841
|
+
data=data,
|
842
|
+
)
|
843
|
+
|
844
|
+
def test_unload_scan_destination_with_confirmation(self):
|
845
|
+
"""Endpoint called with a barcode for another (valid) location, confirm"""
|
846
|
+
response = self.service.dispatch(
|
847
|
+
"unload_scan_destination",
|
848
|
+
params={
|
849
|
+
"picking_batch_id": self.batch.id,
|
850
|
+
"package_id": self.bin2.id,
|
851
|
+
"barcode": self.packing_a_location.barcode,
|
852
|
+
"confirmation": True,
|
853
|
+
},
|
854
|
+
)
|
855
|
+
self.assertRecordValues(
|
856
|
+
self.bin2.quant_ids,
|
857
|
+
[
|
858
|
+
{"location_id": self.packing_a_location.id},
|
859
|
+
{"location_id": self.packing_a_location.id},
|
860
|
+
],
|
861
|
+
)
|
862
|
+
self.assertRecordValues(
|
863
|
+
self.two_lines_picking.move_line_ids,
|
864
|
+
[
|
865
|
+
{"location_dest_id": self.packing_a_location.id},
|
866
|
+
{"location_dest_id": self.packing_a_location.id},
|
867
|
+
],
|
868
|
+
)
|
869
|
+
self.assert_response(response, next_state="unload_single", data=self.ANY)
|
870
|
+
|
871
|
+
def test_unload_scan_destination_completion_info(self):
|
872
|
+
"""/unload_scan_destination that make chained picking ready"""
|
873
|
+
picking = self.one_line_picking
|
874
|
+
dest_location = picking.move_line_ids.location_dest_id
|
875
|
+
self.picking_type.sudo().display_completion_info = True
|
876
|
+
|
877
|
+
# create a chained picking after the current one
|
878
|
+
next_picking = picking.copy(
|
879
|
+
{
|
880
|
+
"picking_type_id": self.wh.out_type_id.id,
|
881
|
+
"location_id": dest_location.id,
|
882
|
+
"location_dest_id": self.customer_location.id,
|
883
|
+
}
|
884
|
+
)
|
885
|
+
next_picking.move_ids.write(
|
886
|
+
{
|
887
|
+
"move_orig_ids": [(6, 0, picking.move_ids.ids)],
|
888
|
+
"location_id": dest_location.id,
|
889
|
+
}
|
890
|
+
)
|
891
|
+
next_picking.action_confirm()
|
892
|
+
|
893
|
+
response = self.service.dispatch(
|
894
|
+
"unload_scan_destination",
|
895
|
+
params={
|
896
|
+
"picking_batch_id": self.batch.id,
|
897
|
+
"package_id": self.bin1.id,
|
898
|
+
"barcode": dest_location.barcode,
|
899
|
+
},
|
900
|
+
)
|
901
|
+
location = self.bin2_lines[0].location_dest_id
|
902
|
+
data = self._data_for_batch(self.batch, location, pack=self.bin2)
|
903
|
+
self.assert_response(
|
904
|
+
response,
|
905
|
+
next_state="unload_single",
|
906
|
+
popup={
|
907
|
+
"body": "Last operation of transfer {}. Next operation "
|
908
|
+
"({}) is ready to proceed.".format(picking.name, next_picking.name)
|
909
|
+
},
|
910
|
+
data=data,
|
911
|
+
)
|