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,1121 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# Copyright 2020 Akretion (http://www.akretion.com)
|
3
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
4
|
+
|
5
|
+
from odoo.tests.common import Form
|
6
|
+
|
7
|
+
from .test_single_pack_transfer_base import SinglePackTransferCommonBase
|
8
|
+
|
9
|
+
# pylint: disable=missing-return
|
10
|
+
|
11
|
+
|
12
|
+
class TestSinglePackTransfer(SinglePackTransferCommonBase):
|
13
|
+
@classmethod
|
14
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
15
|
+
super().setUpClassBaseData(*args, **kwargs)
|
16
|
+
cls.pack_a = cls.env["stock.quant.package"].create(
|
17
|
+
{"location_id": cls.stock_location.id}
|
18
|
+
)
|
19
|
+
cls.quant_a = (
|
20
|
+
cls.env["stock.quant"]
|
21
|
+
.sudo()
|
22
|
+
.create(
|
23
|
+
{
|
24
|
+
"product_id": cls.product_a.id,
|
25
|
+
"location_id": cls.shelf1.id,
|
26
|
+
"quantity": 1,
|
27
|
+
"package_id": cls.pack_a.id,
|
28
|
+
}
|
29
|
+
)
|
30
|
+
)
|
31
|
+
cls.shelf1_2 = cls.shelf1.sudo().copy({"name": "Shelf 1_2"})
|
32
|
+
cls.pack_b = cls.env["stock.quant.package"].create(
|
33
|
+
{"location_id": cls.stock_location.id}
|
34
|
+
)
|
35
|
+
cls.quant_b = (
|
36
|
+
cls.env["stock.quant"]
|
37
|
+
.sudo()
|
38
|
+
.create(
|
39
|
+
{
|
40
|
+
"product_id": cls.product_b.id,
|
41
|
+
"location_id": cls.shelf1_2.id,
|
42
|
+
"quantity": 1,
|
43
|
+
"package_id": cls.pack_b.id,
|
44
|
+
}
|
45
|
+
)
|
46
|
+
)
|
47
|
+
cls.picking = cls._create_initial_move(
|
48
|
+
lines=[(cls.product_a, 1), (cls.product_b, 1)]
|
49
|
+
)
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def _create_initial_move(cls, lines):
|
53
|
+
"""Create the move to satisfy the pre-condition before /start"""
|
54
|
+
picking_form = Form(cls.env["stock.picking"])
|
55
|
+
picking_form.picking_type_id = cls.picking_type
|
56
|
+
picking_form.location_id = cls.stock_location
|
57
|
+
picking_form.location_dest_id = cls.shelf2
|
58
|
+
for line in lines:
|
59
|
+
with picking_form.move_ids_without_package.new() as move:
|
60
|
+
move.product_id = line[0]
|
61
|
+
move.product_uom_qty = line[1]
|
62
|
+
picking = picking_form.save()
|
63
|
+
picking.action_confirm()
|
64
|
+
picking.action_assign()
|
65
|
+
return picking
|
66
|
+
|
67
|
+
def _simulate_started(self, package):
|
68
|
+
"""Replicate what the /start endpoint would do on the given package.
|
69
|
+
|
70
|
+
Used to test the next endpoints (/validate and /cancel)
|
71
|
+
"""
|
72
|
+
package_level = self.picking.move_line_ids.package_level_id.filtered(
|
73
|
+
lambda pl: pl.package_id == package
|
74
|
+
)
|
75
|
+
package_level.is_done = True
|
76
|
+
return package_level
|
77
|
+
|
78
|
+
def _response_package_level_data(self, package_level):
|
79
|
+
return {
|
80
|
+
"id": package_level.id,
|
81
|
+
"name": package_level.package_id.name,
|
82
|
+
"location_src": self.data.location(package_level.location_id),
|
83
|
+
"location_dest": self.data.location(package_level.location_dest_id),
|
84
|
+
"picking": self.data.picking(self.picking),
|
85
|
+
"products": self.data.products(self.product_a),
|
86
|
+
}
|
87
|
+
|
88
|
+
def test_start(self):
|
89
|
+
"""Test the happy path for single pack transfer /start endpoint
|
90
|
+
|
91
|
+
We scan the barcode of the pack (simplest use case).
|
92
|
+
|
93
|
+
The pre-conditions:
|
94
|
+
|
95
|
+
* A Pack exists in Stock/Shelf1.
|
96
|
+
* A stock picking exists to move the Pack from Stock/Shelf1 to
|
97
|
+
Stock/Shelf2. The move is "assigned".
|
98
|
+
|
99
|
+
Expected result:
|
100
|
+
|
101
|
+
* The package level of the move is set to "is_done".
|
102
|
+
|
103
|
+
The next step in the workflow is to call /validate with the
|
104
|
+
package level that will set the move and picking to done.
|
105
|
+
"""
|
106
|
+
barcode = self.pack_a.name
|
107
|
+
params = {"barcode": barcode}
|
108
|
+
|
109
|
+
package_level = self.picking.move_line_ids.package_level_id.filtered(
|
110
|
+
lambda pl: pl.package_id == self.pack_a
|
111
|
+
)
|
112
|
+
self.assertFalse(package_level.is_done)
|
113
|
+
|
114
|
+
# Simulate the client scanning a package's barcode, which
|
115
|
+
# in turns should start the operation in odoo
|
116
|
+
response = self.service.dispatch("start", params=params)
|
117
|
+
|
118
|
+
self.assertTrue(package_level.is_done)
|
119
|
+
self.assert_response(
|
120
|
+
response,
|
121
|
+
next_state="scan_location",
|
122
|
+
data=dict(
|
123
|
+
self._response_package_level_data(package_level),
|
124
|
+
confirmation_required=False,
|
125
|
+
),
|
126
|
+
)
|
127
|
+
|
128
|
+
def test_start_no_operation(self):
|
129
|
+
"""Test /start when there is no operation to move the pack
|
130
|
+
|
131
|
+
The pre-conditions:
|
132
|
+
|
133
|
+
* A Pack exists in Stock/Shelf1.
|
134
|
+
* No stock picking exists to move the Pack from Stock/Shelf1 to
|
135
|
+
Stock/Shelf2, or the state is not assigned.
|
136
|
+
|
137
|
+
Expected result:
|
138
|
+
|
139
|
+
* Return a message
|
140
|
+
"""
|
141
|
+
barcode = self.pack_a.name
|
142
|
+
params = {"barcode": barcode}
|
143
|
+
self.picking.do_unreserve()
|
144
|
+
|
145
|
+
# Simulate the client scanning a package's barcode, which
|
146
|
+
# in turns should start the operation in odoo
|
147
|
+
response = self.service.dispatch("start", params=params)
|
148
|
+
|
149
|
+
self.assert_response(
|
150
|
+
response,
|
151
|
+
next_state="start",
|
152
|
+
message={
|
153
|
+
"message_type": "error",
|
154
|
+
"body": "No pending operation for package {}.".format(self.pack_a.name),
|
155
|
+
},
|
156
|
+
)
|
157
|
+
|
158
|
+
def test_start_no_operation_create(self):
|
159
|
+
"""Test /start when there is no operation to move the pack, it is created
|
160
|
+
|
161
|
+
The pre-conditions:
|
162
|
+
|
163
|
+
* The option "Allow Move Creation" is turned on on the menu
|
164
|
+
* A Pack exists in Stock/Shelf1.
|
165
|
+
* No stock picking exists to move the Pack from Stock/Shelf1 to
|
166
|
+
Stock/Shelf2, or the state is not assigned.
|
167
|
+
|
168
|
+
Expected result:
|
169
|
+
|
170
|
+
* Create a stock.picking, move, package level and continue with the
|
171
|
+
workflow
|
172
|
+
"""
|
173
|
+
self.menu.sudo().allow_move_create = True
|
174
|
+
barcode = self.pack_a.name
|
175
|
+
params = {"barcode": barcode}
|
176
|
+
self.picking.do_unreserve()
|
177
|
+
|
178
|
+
# Simulate the client scanning a package's barcode, which
|
179
|
+
# in turns should start the operation in odoo
|
180
|
+
response = self.service.dispatch("start", params=params)
|
181
|
+
|
182
|
+
move_line = self.env["stock.move.line"].search(
|
183
|
+
[("package_id", "=", self.pack_a.id)]
|
184
|
+
)
|
185
|
+
package_level = move_line.package_level_id
|
186
|
+
|
187
|
+
self.assertTrue(package_level.is_done)
|
188
|
+
|
189
|
+
expected_data = {
|
190
|
+
"id": package_level.id,
|
191
|
+
"name": package_level.package_id.name,
|
192
|
+
"location_src": self.data.location(self.shelf1),
|
193
|
+
"location_dest": self.data.location(
|
194
|
+
self.picking_type.default_location_dest_id
|
195
|
+
),
|
196
|
+
"picking": self.data.picking(package_level.picking_id),
|
197
|
+
"products": self.data.products(self.product_a),
|
198
|
+
"confirmation_required": False,
|
199
|
+
}
|
200
|
+
|
201
|
+
self.assert_response(response, next_state="scan_location", data=expected_data)
|
202
|
+
|
203
|
+
def test_start_barcode_not_known(self):
|
204
|
+
"""Test /start when the barcode is unknown
|
205
|
+
|
206
|
+
The pre-conditions:
|
207
|
+
|
208
|
+
* Nothing
|
209
|
+
|
210
|
+
Expected result:
|
211
|
+
|
212
|
+
* Return a message
|
213
|
+
"""
|
214
|
+
params = {"barcode": "THIS_BARCODE_DOES_NOT_EXIST"}
|
215
|
+
response = self.service.dispatch("start", params=params)
|
216
|
+
self.assert_response(
|
217
|
+
response,
|
218
|
+
next_state="start",
|
219
|
+
message={
|
220
|
+
"message_type": "error",
|
221
|
+
"body": "The package THIS_BARCODE_DOES_NOT_EXIST" " doesn't exist",
|
222
|
+
},
|
223
|
+
)
|
224
|
+
|
225
|
+
def test_start_pack_empty(self):
|
226
|
+
"""Test /start when the scanned pack is empty
|
227
|
+
|
228
|
+
The pre-conditions:
|
229
|
+
|
230
|
+
* Nothing
|
231
|
+
|
232
|
+
Expected result:
|
233
|
+
|
234
|
+
* Return a message
|
235
|
+
"""
|
236
|
+
pack_empty = self.env["stock.quant.package"].create({})
|
237
|
+
pack_code = pack_empty.name
|
238
|
+
params = {"barcode": pack_code}
|
239
|
+
response = self.service.dispatch("start", params=params)
|
240
|
+
self.assert_response(
|
241
|
+
response,
|
242
|
+
next_state="start",
|
243
|
+
message=self.service.msg_store.package_has_no_product_to_take(pack_code),
|
244
|
+
)
|
245
|
+
|
246
|
+
def test_start_pack_from_location(self):
|
247
|
+
"""Test /start, finding the pack from location's barcode
|
248
|
+
|
249
|
+
When we scan a location which contains only one pack,
|
250
|
+
we want to move this pack.
|
251
|
+
|
252
|
+
The pre-conditions:
|
253
|
+
|
254
|
+
* A Pack exists in Stock/Shelf1.
|
255
|
+
* A stock picking exists to move the Pack from Stock/Shelf1 to
|
256
|
+
Stock/Shelf2. The move is "assigned".
|
257
|
+
|
258
|
+
Expected result:
|
259
|
+
|
260
|
+
* The package level of the move is set to "is_done".
|
261
|
+
|
262
|
+
The next step in the workflow is to call /validate with the
|
263
|
+
package level that will set the move and picking to done.
|
264
|
+
"""
|
265
|
+
barcode = self.shelf1.barcode
|
266
|
+
params = {"barcode": barcode}
|
267
|
+
response = self.service.dispatch("start", params=params)
|
268
|
+
self.assert_response(
|
269
|
+
# We only care about the fact that we jump to the next
|
270
|
+
# screen, so it found the pack. The details are already
|
271
|
+
# checked in the test_start test.
|
272
|
+
response,
|
273
|
+
next_state="scan_location",
|
274
|
+
data=self.ANY,
|
275
|
+
)
|
276
|
+
|
277
|
+
def test_start_pack_from_location_empty(self):
|
278
|
+
"""Test /start, scan location's barcode without pack
|
279
|
+
|
280
|
+
When we scan a location which contains no packs,
|
281
|
+
we ask the user to scan a pack.
|
282
|
+
|
283
|
+
The pre-conditions:
|
284
|
+
|
285
|
+
* No packs exists in Stock/Shelf2
|
286
|
+
|
287
|
+
Expected result:
|
288
|
+
|
289
|
+
* Return a message
|
290
|
+
"""
|
291
|
+
barcode = self.shelf2.barcode
|
292
|
+
params = {"barcode": barcode}
|
293
|
+
response = self.service.dispatch("start", params=params)
|
294
|
+
self.assert_response(
|
295
|
+
response,
|
296
|
+
next_state="start",
|
297
|
+
message={
|
298
|
+
"message_type": "error",
|
299
|
+
"body": "Location %s doesn't contain any package."
|
300
|
+
% (self.shelf2.name,),
|
301
|
+
},
|
302
|
+
)
|
303
|
+
|
304
|
+
def test_start_pack_from_location_several_packs(self):
|
305
|
+
"""Test /start, scan location's barcode with several packs
|
306
|
+
|
307
|
+
When we scan a location which contains several packs,
|
308
|
+
we ask the user to scan a pack.
|
309
|
+
|
310
|
+
The pre-conditions:
|
311
|
+
|
312
|
+
* 2 packs exists in Stock/Shelf1.
|
313
|
+
|
314
|
+
Expected result:
|
315
|
+
|
316
|
+
* Return a message
|
317
|
+
"""
|
318
|
+
pack_b = self.env["stock.quant.package"].create(
|
319
|
+
{"location_id": self.stock_location.id}
|
320
|
+
)
|
321
|
+
self.env["stock.quant"].sudo().create(
|
322
|
+
{
|
323
|
+
"product_id": self.product_a.id,
|
324
|
+
"location_id": self.shelf1.id,
|
325
|
+
"quantity": 1,
|
326
|
+
"package_id": pack_b.id,
|
327
|
+
}
|
328
|
+
)
|
329
|
+
barcode = self.shelf1.barcode
|
330
|
+
params = {"barcode": barcode}
|
331
|
+
response = self.service.dispatch("start", params=params)
|
332
|
+
self.assert_response(
|
333
|
+
response,
|
334
|
+
next_state="start",
|
335
|
+
message={
|
336
|
+
"message_type": "warning",
|
337
|
+
"body": "Several packages found in %s, please scan a package."
|
338
|
+
% (self.shelf1.name,),
|
339
|
+
},
|
340
|
+
)
|
341
|
+
|
342
|
+
def test_start_pack_outside_of_location(self):
|
343
|
+
"""Test /start, scan a pack outside of the picking type location
|
344
|
+
|
345
|
+
The pre-conditions:
|
346
|
+
|
347
|
+
* A pack exists in a location outside of Stock (the default source
|
348
|
+
location of the picking type associated with the process)
|
349
|
+
|
350
|
+
Expected result:
|
351
|
+
|
352
|
+
* Return a message
|
353
|
+
"""
|
354
|
+
self.pack_a.location_id = self.dispatch_location
|
355
|
+
barcode = self.pack_a.name
|
356
|
+
params = {"barcode": barcode}
|
357
|
+
response = self.service.dispatch("start", params=params)
|
358
|
+
self.assert_response(
|
359
|
+
response,
|
360
|
+
next_state="start",
|
361
|
+
message={
|
362
|
+
"message_type": "error",
|
363
|
+
"body": "You cannot work on a package (%s) outside of locations: %s"
|
364
|
+
% (self.pack_a.name, self.picking_type.default_location_src_id.name),
|
365
|
+
},
|
366
|
+
)
|
367
|
+
|
368
|
+
def test_start_already_started(self):
|
369
|
+
"""Test /start when it was already started
|
370
|
+
|
371
|
+
We scan the barcode of the pack (simplest use case).
|
372
|
+
|
373
|
+
The pre-conditions:
|
374
|
+
|
375
|
+
* A Pack exists in Stock/Shelf1.
|
376
|
+
* A stock picking exists to move the Pack from Stock/Shelf1 to
|
377
|
+
Stock/Shelf2. The move is "assigned".
|
378
|
+
* Start is already called once
|
379
|
+
|
380
|
+
Expected result:
|
381
|
+
|
382
|
+
* Transition for confirmation with such message
|
383
|
+
|
384
|
+
The next step in the workflow is to call /validate with the
|
385
|
+
package level that will set the move and picking to done.
|
386
|
+
"""
|
387
|
+
barcode = self.pack_a.name
|
388
|
+
params = {"barcode": barcode}
|
389
|
+
|
390
|
+
package_level = self._simulate_started(self.pack_a)
|
391
|
+
self.assertTrue(package_level.is_done)
|
392
|
+
|
393
|
+
# Simulate the client scanning a package's barcode, which
|
394
|
+
# in turns should start the operation in odoo
|
395
|
+
response = self.service.dispatch("start", params=params)
|
396
|
+
|
397
|
+
self.assert_response(
|
398
|
+
response,
|
399
|
+
next_state="start",
|
400
|
+
message={
|
401
|
+
"message_type": "warning",
|
402
|
+
"body": "Operation's already running."
|
403
|
+
" Would you like to take it over?",
|
404
|
+
},
|
405
|
+
data=dict(
|
406
|
+
self._response_package_level_data(package_level),
|
407
|
+
confirmation_required=True,
|
408
|
+
),
|
409
|
+
)
|
410
|
+
|
411
|
+
def test_validate(self):
|
412
|
+
"""Test the happy path for single pack transfer /validate endpoint
|
413
|
+
|
414
|
+
The pre-conditions:
|
415
|
+
|
416
|
+
* /start has been called
|
417
|
+
* "completion info" is not active on the picking type
|
418
|
+
|
419
|
+
Expected result:
|
420
|
+
|
421
|
+
* The move associated to the package level is 'done'
|
422
|
+
"""
|
423
|
+
# setup the picking as we need, like if the move line
|
424
|
+
# was already started by the first step (start operation)
|
425
|
+
package_level = self._simulate_started(self.pack_a)
|
426
|
+
|
427
|
+
# now, call the service to proceed with validation of the
|
428
|
+
# movement
|
429
|
+
response = self.service.dispatch(
|
430
|
+
"validate",
|
431
|
+
params={
|
432
|
+
"package_level_id": package_level.id,
|
433
|
+
"location_barcode": self.shelf2.barcode,
|
434
|
+
},
|
435
|
+
)
|
436
|
+
|
437
|
+
self.assert_response(
|
438
|
+
response,
|
439
|
+
next_state="start",
|
440
|
+
message={
|
441
|
+
"message_type": "success",
|
442
|
+
"body": "The pack has been moved, you can scan a new pack.",
|
443
|
+
},
|
444
|
+
)
|
445
|
+
|
446
|
+
self.assertRecordValues(
|
447
|
+
package_level.move_line_ids,
|
448
|
+
[{"qty_done": 1.0, "location_dest_id": self.shelf2.id, "state": "done"}],
|
449
|
+
)
|
450
|
+
self.assertRecordValues(
|
451
|
+
package_level.move_line_ids.move_id,
|
452
|
+
[{"location_dest_id": self.shelf2.id, "state": "done"}],
|
453
|
+
)
|
454
|
+
|
455
|
+
def test_validate_completion_info(self):
|
456
|
+
"""Test /validate when the picking is the last (show completion info)
|
457
|
+
|
458
|
+
When the picking is the last, we display an information screen on the
|
459
|
+
js application.
|
460
|
+
|
461
|
+
The pre-conditions:
|
462
|
+
|
463
|
+
* /start has been called
|
464
|
+
* "completion info" is active on the picking type
|
465
|
+
* the picking must be the last (it must not have destination moves with
|
466
|
+
unprocessed origin moves)
|
467
|
+
|
468
|
+
Expected result:
|
469
|
+
|
470
|
+
* The move associated to the package level is 'done'
|
471
|
+
* The transition goes to the completion info screen instead of starting
|
472
|
+
over
|
473
|
+
"""
|
474
|
+
# activate the computation of this field, so we have a chance to
|
475
|
+
# transition to the 'show completion info' screen.
|
476
|
+
self.picking_type.sudo().display_completion_info = True
|
477
|
+
|
478
|
+
# create a chained picking after the current one
|
479
|
+
next_picking = self.picking.copy(
|
480
|
+
{
|
481
|
+
"picking_type_id": self.wh.out_type_id.id,
|
482
|
+
"location_id": self.picking.location_dest_id.id,
|
483
|
+
"location_dest_id": self.customer_location.id,
|
484
|
+
}
|
485
|
+
)
|
486
|
+
next_picking.move_ids.write(
|
487
|
+
{"move_orig_ids": [(6, 0, self.picking.move_ids.ids)]}
|
488
|
+
)
|
489
|
+
next_picking.action_confirm()
|
490
|
+
|
491
|
+
# process the first package
|
492
|
+
package_level_a = self._simulate_started(self.pack_a)
|
493
|
+
# validate the first package
|
494
|
+
response = self.service.dispatch(
|
495
|
+
"validate",
|
496
|
+
params={
|
497
|
+
"package_level_id": package_level_a.id,
|
498
|
+
"location_barcode": self.shelf2.barcode,
|
499
|
+
},
|
500
|
+
)
|
501
|
+
self.assertEqual(package_level_a.picking_id.state, "done")
|
502
|
+
# check the response: still no completion info message as we still have
|
503
|
+
# the second package to process
|
504
|
+
self.assert_response(
|
505
|
+
response,
|
506
|
+
next_state="start",
|
507
|
+
message={
|
508
|
+
"message_type": "success",
|
509
|
+
"body": "The pack has been moved, you can scan a new pack.",
|
510
|
+
},
|
511
|
+
)
|
512
|
+
# process the second package
|
513
|
+
package_level_b = self._simulate_started(self.pack_b)
|
514
|
+
# validate the second package
|
515
|
+
response = self.service.dispatch(
|
516
|
+
"validate",
|
517
|
+
params={
|
518
|
+
"package_level_id": package_level_b.id,
|
519
|
+
"location_barcode": self.shelf2.barcode,
|
520
|
+
},
|
521
|
+
)
|
522
|
+
self.assertEqual(package_level_b.picking_id.state, "done")
|
523
|
+
self.assertNotEqual(package_level_a.picking_id, package_level_b.picking_id)
|
524
|
+
# check the response: the chained transfer is ready to be processed now
|
525
|
+
# that all the packages have been processed
|
526
|
+
self.assert_response(
|
527
|
+
response,
|
528
|
+
next_state="start",
|
529
|
+
popup={
|
530
|
+
"body": "Last operation of transfer {}. Next operation "
|
531
|
+
"({}) is ready to proceed.".format(self.picking.name, next_picking.name)
|
532
|
+
},
|
533
|
+
message={
|
534
|
+
"message_type": "success",
|
535
|
+
"body": "The pack has been moved, you can scan a new pack.",
|
536
|
+
},
|
537
|
+
)
|
538
|
+
|
539
|
+
def test_validate_not_found(self):
|
540
|
+
"""Test a call on /validate on package level not found
|
541
|
+
|
542
|
+
Expected result:
|
543
|
+
|
544
|
+
* No change in odoo, Transition with a message
|
545
|
+
"""
|
546
|
+
response = self.service.dispatch(
|
547
|
+
"validate",
|
548
|
+
params={"package_level_id": -1, "location_barcode": self.shelf1.barcode},
|
549
|
+
)
|
550
|
+
|
551
|
+
self.assert_response(
|
552
|
+
response,
|
553
|
+
next_state="start",
|
554
|
+
message={
|
555
|
+
"message_type": "error",
|
556
|
+
"body": "This operation does not exist anymore.",
|
557
|
+
},
|
558
|
+
)
|
559
|
+
|
560
|
+
def test_validate_location_not_found(self):
|
561
|
+
"""Test a call on /validate on location not found
|
562
|
+
|
563
|
+
The pre-conditions:
|
564
|
+
|
565
|
+
* /start has been called
|
566
|
+
|
567
|
+
Expected result:
|
568
|
+
|
569
|
+
* No change in odoo, Transition with a message
|
570
|
+
"""
|
571
|
+
# setup the picking as we need, like if the move line
|
572
|
+
# was already started by the first step (start operation)
|
573
|
+
package_level = self._simulate_started(self.pack_a)
|
574
|
+
|
575
|
+
response = self.service.dispatch(
|
576
|
+
"validate",
|
577
|
+
params={
|
578
|
+
"package_level_id": package_level.id,
|
579
|
+
"location_barcode": "THIS_BARCODE_DOES_NOT_EXISTS",
|
580
|
+
},
|
581
|
+
)
|
582
|
+
|
583
|
+
self.assert_response(
|
584
|
+
response,
|
585
|
+
next_state="scan_location",
|
586
|
+
data=self.ANY,
|
587
|
+
message={
|
588
|
+
"message_type": "error",
|
589
|
+
"body": "No location found for this barcode.",
|
590
|
+
},
|
591
|
+
)
|
592
|
+
|
593
|
+
def test_validate_location_forbidden(self):
|
594
|
+
"""Test a call on /validate on a forbidden location (not child of
|
595
|
+
picking or move)
|
596
|
+
|
597
|
+
The pre-conditions:
|
598
|
+
|
599
|
+
* /start has been called
|
600
|
+
|
601
|
+
Expected result:
|
602
|
+
|
603
|
+
* No change in odoo, Transition with a message
|
604
|
+
|
605
|
+
Note: the location is forbidden when a location is not a child
|
606
|
+
of the destination location of the picking used for the process
|
607
|
+
"""
|
608
|
+
# setup the picking as we need, like if the move line
|
609
|
+
# was already started by the first step (start operation)
|
610
|
+
package_level = self._simulate_started(self.pack_a)
|
611
|
+
|
612
|
+
response = self.service.dispatch(
|
613
|
+
"validate",
|
614
|
+
params={
|
615
|
+
"package_level_id": package_level.id,
|
616
|
+
# this location is outside of the expected destination
|
617
|
+
"location_barcode": self.dispatch_location.barcode,
|
618
|
+
},
|
619
|
+
)
|
620
|
+
|
621
|
+
self.assert_response(
|
622
|
+
response,
|
623
|
+
next_state="scan_location",
|
624
|
+
data=self.ANY,
|
625
|
+
message={"message_type": "error", "body": "You cannot place it here"},
|
626
|
+
)
|
627
|
+
|
628
|
+
def test_validate_location_move_not_child_of_picking_allowed(self):
|
629
|
+
"""Test a call on /validate on a location not child of picking but child of move
|
630
|
+
|
631
|
+
The pre-conditions:
|
632
|
+
|
633
|
+
* /start has been called
|
634
|
+
|
635
|
+
Expected result:
|
636
|
+
|
637
|
+
* No change in odoo, Transition with a message
|
638
|
+
|
639
|
+
Note: the location is allowed when the move location has changed and
|
640
|
+
that location is a child of the destination location of the move
|
641
|
+
"""
|
642
|
+
# setup the picking as we need, like if the move line
|
643
|
+
# was already started by the first step (start operation)
|
644
|
+
package_level = self._simulate_started(self.pack_a)
|
645
|
+
|
646
|
+
location = package_level.location_dest_id.location_id
|
647
|
+
package_level.location_dest_id = location
|
648
|
+
package_level.move_line_ids.move_id.location_dest_id = location
|
649
|
+
|
650
|
+
response = self.service.dispatch(
|
651
|
+
"validate",
|
652
|
+
params={
|
653
|
+
"package_level_id": package_level.id,
|
654
|
+
"location_barcode": location.barcode,
|
655
|
+
},
|
656
|
+
)
|
657
|
+
|
658
|
+
self.assert_response(
|
659
|
+
response,
|
660
|
+
next_state="start",
|
661
|
+
message={
|
662
|
+
"message_type": "success",
|
663
|
+
"body": "The pack has been moved, you can scan a new pack.",
|
664
|
+
},
|
665
|
+
)
|
666
|
+
|
667
|
+
def test_validate_location_to_confirm(self):
|
668
|
+
"""Test a call on /validate on a location to confirm
|
669
|
+
|
670
|
+
The pre-conditions:
|
671
|
+
|
672
|
+
* /start has been called
|
673
|
+
|
674
|
+
Expected result:
|
675
|
+
|
676
|
+
* No change in odoo, transition with a message
|
677
|
+
|
678
|
+
Note: a location to confirm is when a location is a child
|
679
|
+
of the destination location of the picking type used for the process
|
680
|
+
but not a child or the expected destination
|
681
|
+
"""
|
682
|
+
# setup the picking as we need, like if the move line
|
683
|
+
# was already started by the first step (start operation)
|
684
|
+
package_level = self._simulate_started(self.pack_a)
|
685
|
+
|
686
|
+
sub_shelf1 = (
|
687
|
+
self.env["stock.location"]
|
688
|
+
.sudo()
|
689
|
+
.create(
|
690
|
+
{
|
691
|
+
"name": "subshelf1",
|
692
|
+
"barcode": "subshelf1",
|
693
|
+
"location_id": self.shelf2.id,
|
694
|
+
}
|
695
|
+
)
|
696
|
+
)
|
697
|
+
sub_shelf2 = (
|
698
|
+
self.env["stock.location"]
|
699
|
+
.sudo()
|
700
|
+
.create(
|
701
|
+
{
|
702
|
+
"name": "subshelf2",
|
703
|
+
"barcode": "subshelf2",
|
704
|
+
"location_id": self.shelf2.id,
|
705
|
+
}
|
706
|
+
)
|
707
|
+
)
|
708
|
+
|
709
|
+
# expected destination is 'shelf2', we'll scan shelf1 which must
|
710
|
+
# ask a confirmation to the user (it's still in the same picking type)
|
711
|
+
package_level.location_dest_id = sub_shelf1
|
712
|
+
response = self.service.dispatch(
|
713
|
+
"validate",
|
714
|
+
params={
|
715
|
+
"package_level_id": package_level.id,
|
716
|
+
"location_barcode": sub_shelf2.barcode,
|
717
|
+
},
|
718
|
+
)
|
719
|
+
|
720
|
+
message = self.service._actions_for("message").confirm_location_changed(
|
721
|
+
sub_shelf1, sub_shelf2
|
722
|
+
)
|
723
|
+
self.assert_response(
|
724
|
+
response,
|
725
|
+
next_state="scan_location",
|
726
|
+
message=message,
|
727
|
+
data=dict(
|
728
|
+
self._response_package_level_data(package_level),
|
729
|
+
confirmation_required=True,
|
730
|
+
),
|
731
|
+
)
|
732
|
+
|
733
|
+
def test_validate_location_with_confirm(self):
|
734
|
+
"""Test a call on /validate on a different location with confirmation
|
735
|
+
|
736
|
+
The pre-conditions:
|
737
|
+
|
738
|
+
* /start has been called
|
739
|
+
|
740
|
+
Expected result:
|
741
|
+
|
742
|
+
* Ignore the fact that the scanned location is not the expected
|
743
|
+
* Change the destination of the move line to the scanned one
|
744
|
+
* The move associated to the package level is 'done'
|
745
|
+
|
746
|
+
Note: a location to confirm is when a location is a child
|
747
|
+
of the destination location of the picking type used for the process
|
748
|
+
but not a child or the expected destination.
|
749
|
+
In such situation, the js application has to call /validate with
|
750
|
+
a ``confirmation`` flag.
|
751
|
+
"""
|
752
|
+
# setup the picking as we need, like if the move line
|
753
|
+
# was already started by the first step (start operation)
|
754
|
+
package_level = self._simulate_started(self.pack_a)
|
755
|
+
|
756
|
+
# expected destination is 'shelf1', we'll scan shelf2 which must
|
757
|
+
# ask a confirmation to the user (it's still in the same picking type)
|
758
|
+
response = self.service.dispatch(
|
759
|
+
"validate",
|
760
|
+
params={
|
761
|
+
"package_level_id": package_level.id,
|
762
|
+
"location_barcode": self.shelf2.barcode,
|
763
|
+
# acknowledge the change of destination
|
764
|
+
"confirmation": True,
|
765
|
+
},
|
766
|
+
)
|
767
|
+
|
768
|
+
self.assert_response(
|
769
|
+
response,
|
770
|
+
next_state="start",
|
771
|
+
message={
|
772
|
+
"message_type": "success",
|
773
|
+
"body": "The pack has been moved, you can scan a new pack.",
|
774
|
+
},
|
775
|
+
)
|
776
|
+
|
777
|
+
self.assertRecordValues(
|
778
|
+
package_level.move_line_ids,
|
779
|
+
[{"qty_done": 1.0, "location_dest_id": self.shelf2.id, "state": "done"}],
|
780
|
+
)
|
781
|
+
self.assertRecordValues(
|
782
|
+
package_level.move_line_ids.move_id,
|
783
|
+
[{"location_dest_id": self.shelf2.id, "state": "done"}],
|
784
|
+
)
|
785
|
+
|
786
|
+
def test_cancel(self):
|
787
|
+
"""Test the happy path for single pack transfer /cancel endpoint
|
788
|
+
|
789
|
+
The pre-conditions:
|
790
|
+
|
791
|
+
* /start has been called
|
792
|
+
|
793
|
+
Expected result:
|
794
|
+
|
795
|
+
* The package level has is_done to False
|
796
|
+
"""
|
797
|
+
# setup the picking as we need, like if the move line
|
798
|
+
# was already started by the first step (start operation)
|
799
|
+
package_level = self._simulate_started(self.pack_a)
|
800
|
+
self.assertTrue(package_level.is_done)
|
801
|
+
|
802
|
+
# keep references for later checks
|
803
|
+
move = package_level.move_line_ids.move_id
|
804
|
+
picking = move.picking_id
|
805
|
+
|
806
|
+
# now, call the service to cancel
|
807
|
+
response = self.service.dispatch(
|
808
|
+
"cancel", params={"package_level_id": package_level.id}
|
809
|
+
)
|
810
|
+
self.assertRecordValues(move, [{"state": "assigned"}])
|
811
|
+
self.assertRecordValues(picking, [{"state": "assigned"}])
|
812
|
+
self.assertRecordValues(package_level, [{"is_done": False}])
|
813
|
+
|
814
|
+
self.assert_response(
|
815
|
+
response,
|
816
|
+
next_state="start",
|
817
|
+
message={
|
818
|
+
"message_type": "success",
|
819
|
+
"body": "Canceled, you can scan a new pack.",
|
820
|
+
},
|
821
|
+
)
|
822
|
+
|
823
|
+
def test_cancel_already_canceled(self):
|
824
|
+
"""Test a call on /cancel for already canceled package level
|
825
|
+
|
826
|
+
The pre-conditions:
|
827
|
+
|
828
|
+
* /start has been called
|
829
|
+
* /cancel has been called elsewhere or 'is_done' removed in odoo
|
830
|
+
|
831
|
+
Expected result:
|
832
|
+
|
833
|
+
* Nothing happens, transition with a message
|
834
|
+
"""
|
835
|
+
# setup the picking as we need, like if the move line
|
836
|
+
# was already started by the first step (start operation)
|
837
|
+
package_level_a = self._simulate_started(self.pack_a)
|
838
|
+
# keep references for later checks
|
839
|
+
move_a = package_level_a.move_line_ids.move_id
|
840
|
+
move_lines_a = package_level_a.move_line_ids
|
841
|
+
picking = move_a.picking_id
|
842
|
+
# someone cancel the work started by our operator
|
843
|
+
move_lines_a.write({"qty_done": 0})
|
844
|
+
move_a._action_cancel()
|
845
|
+
|
846
|
+
# now, call the service to cancel the first package
|
847
|
+
response = self.service.dispatch(
|
848
|
+
"cancel", params={"package_level_id": package_level_a.id}
|
849
|
+
)
|
850
|
+
self.assertRecordValues(move_a, [{"state": "cancel"}])
|
851
|
+
self.assertRecordValues(picking, [{"state": "assigned"}])
|
852
|
+
self.assertFalse(package_level_a.move_line_ids)
|
853
|
+
self.assertFalse(move_lines_a.exists())
|
854
|
+
|
855
|
+
self.assert_response(
|
856
|
+
response,
|
857
|
+
next_state="start",
|
858
|
+
message={
|
859
|
+
"message_type": "error",
|
860
|
+
"body": "This operation does not exist anymore.",
|
861
|
+
},
|
862
|
+
)
|
863
|
+
package_level_b = self._simulate_started(self.pack_b)
|
864
|
+
# keep references for later checks
|
865
|
+
move_b = package_level_b.move_line_ids.move_id
|
866
|
+
# someone cancel the work started by our operator
|
867
|
+
move_lines_b = package_level_b.move_line_ids
|
868
|
+
move_lines_b.write({"qty_done": 0})
|
869
|
+
move_b._action_cancel()
|
870
|
+
# then cancel the second package
|
871
|
+
response = self.service.dispatch(
|
872
|
+
"cancel", params={"package_level_id": package_level_b.id}
|
873
|
+
)
|
874
|
+
self.assertRecordValues(move_b, [{"state": "cancel"}])
|
875
|
+
picking.invalidate_recordset(["state"])
|
876
|
+
self.assertRecordValues(picking, [{"state": "cancel"}])
|
877
|
+
|
878
|
+
def test_cancel_already_done(self):
|
879
|
+
"""Test a call on /cancel on move already done
|
880
|
+
|
881
|
+
The pre-conditions:
|
882
|
+
|
883
|
+
* /start has been called
|
884
|
+
* /validate has been called or move set to done in odoo
|
885
|
+
|
886
|
+
Expected result:
|
887
|
+
|
888
|
+
* No change in odoo, Transition with a message
|
889
|
+
"""
|
890
|
+
# setup the picking as we need, like if the move line
|
891
|
+
# was already started by the first step (start operation)
|
892
|
+
package_levels = self._simulate_started(self.pack_a) | self._simulate_started(
|
893
|
+
self.pack_b
|
894
|
+
)
|
895
|
+
|
896
|
+
# keep references for later checks
|
897
|
+
moves = package_levels.move_line_ids.move_id
|
898
|
+
picking = moves.picking_id
|
899
|
+
|
900
|
+
# someone cancel the work started by our operator
|
901
|
+
moves.extract_and_action_done()
|
902
|
+
|
903
|
+
# now, call the service to cancel
|
904
|
+
response = self.service.dispatch(
|
905
|
+
"cancel", params={"package_level_id": package_levels[0].id}
|
906
|
+
)
|
907
|
+
response = self.service.dispatch(
|
908
|
+
"cancel", params={"package_level_id": package_levels[1].id}
|
909
|
+
)
|
910
|
+
self.assertRecordValues(moves, [{"state": "done"}, {"state": "done"}])
|
911
|
+
self.assertRecordValues(picking, [{"state": "done"}])
|
912
|
+
|
913
|
+
self.assert_response(
|
914
|
+
response,
|
915
|
+
next_state="start",
|
916
|
+
message={"message_type": "info", "body": "Operation already processed."},
|
917
|
+
)
|
918
|
+
|
919
|
+
def test_cancel_not_found(self):
|
920
|
+
"""Test a call on /cancel on package level not found
|
921
|
+
|
922
|
+
Expected result:
|
923
|
+
|
924
|
+
* No change in odoo, Transition with a message
|
925
|
+
"""
|
926
|
+
response = self.service.dispatch("cancel", params={"package_level_id": -1})
|
927
|
+
self.assert_response(
|
928
|
+
response,
|
929
|
+
next_state="start",
|
930
|
+
message={
|
931
|
+
"message_type": "error",
|
932
|
+
"body": "This operation does not exist anymore.",
|
933
|
+
},
|
934
|
+
)
|
935
|
+
|
936
|
+
|
937
|
+
class SinglePackTransferSpecialCase(SinglePackTransferCommonBase):
|
938
|
+
def test_start_package_unreserve_ok(self):
|
939
|
+
"""Test /start when the package was already reserved...
|
940
|
+
|
941
|
+
...for another picking type and unreserving is allowed.
|
942
|
+
|
943
|
+
When we scan a location which contains only one package,
|
944
|
+
we want to move this package.
|
945
|
+
|
946
|
+
The pre-conditions:
|
947
|
+
|
948
|
+
* A package exists in Stock/Shelf1.
|
949
|
+
* A stock picking exists to move the package from Stock/Shelf1 to
|
950
|
+
Out with a different picking type. The move is "assigned".
|
951
|
+
* Another package exists in Stock
|
952
|
+
|
953
|
+
Expected result:
|
954
|
+
|
955
|
+
* the original transfer is reserved to move the other package from Stock
|
956
|
+
* a new transfer is created to move the package from Shelf1
|
957
|
+
|
958
|
+
The next step in the workflow is to call /validate with the
|
959
|
+
package level that will set the move and picking to done.
|
960
|
+
"""
|
961
|
+
self.menu.sudo().allow_move_create = True
|
962
|
+
self.menu.sudo().allow_unreserve_other_moves = True
|
963
|
+
|
964
|
+
package = self.env["stock.quant.package"].create({})
|
965
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 10, package=package)
|
966
|
+
# create a picking of another picking type
|
967
|
+
picking = self._create_picking(
|
968
|
+
picking_type=self.wh.out_type_id, lines=[(self.product_a, 10)]
|
969
|
+
)
|
970
|
+
picking.action_assign()
|
971
|
+
|
972
|
+
# create another package that should be used when the picking will
|
973
|
+
# get re-assigned
|
974
|
+
package2 = self.env["stock.quant.package"].create({})
|
975
|
+
self._update_qty_in_location(
|
976
|
+
self.stock_location, self.product_a, 10, package=package2
|
977
|
+
)
|
978
|
+
|
979
|
+
barcode = self.shelf1.barcode
|
980
|
+
params = {"barcode": barcode}
|
981
|
+
response = self.service.dispatch("start", params=params)
|
982
|
+
|
983
|
+
new_picking = self.env["stock.picking"].search(
|
984
|
+
[("picking_type_id", "=", self.picking_type.id)]
|
985
|
+
)
|
986
|
+
self.assertEqual(len(new_picking), 1)
|
987
|
+
new_package_level = new_picking.package_level_ids
|
988
|
+
|
989
|
+
self.assert_response(
|
990
|
+
# We only care about the fact that we jump to the next
|
991
|
+
# screen, so it found the pack. The details are already
|
992
|
+
# checked in the test_start test.
|
993
|
+
response,
|
994
|
+
next_state="scan_location",
|
995
|
+
data=dict(
|
996
|
+
self.service._data_after_package_scanned(new_package_level),
|
997
|
+
confirmation_required=False,
|
998
|
+
),
|
999
|
+
)
|
1000
|
+
self.assertRecordValues(
|
1001
|
+
picking.package_level_ids, [{"package_id": package2.id}]
|
1002
|
+
)
|
1003
|
+
|
1004
|
+
self.assertRecordValues(new_package_level, [{"package_id": package.id}])
|
1005
|
+
|
1006
|
+
def test_start_package_unreserve_picked_error(self):
|
1007
|
+
"""Test /start when the package was already reserved...
|
1008
|
+
|
1009
|
+
...for another picking type and the other move is already picked.
|
1010
|
+
|
1011
|
+
When we scan a location which contains only one package,
|
1012
|
+
we want to move this package.
|
1013
|
+
|
1014
|
+
The pre-conditions:
|
1015
|
+
|
1016
|
+
* A package exists in Stock/Shelf1.
|
1017
|
+
* A stock picking exists to move the package from Stock/Shelf1 to
|
1018
|
+
Out with a different picking type. The move is "assigned".
|
1019
|
+
|
1020
|
+
Expected result:
|
1021
|
+
|
1022
|
+
* receive an error that we cannot pick the package
|
1023
|
+
"""
|
1024
|
+
self.menu.sudo().allow_move_create = True
|
1025
|
+
self.menu.sudo().allow_unreserve_other_moves = True
|
1026
|
+
|
1027
|
+
package = self.env["stock.quant.package"].create({})
|
1028
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 10, package=package)
|
1029
|
+
# create a picking of another picking type
|
1030
|
+
picking = self._create_picking(
|
1031
|
+
picking_type=self.wh.out_type_id, lines=[(self.product_a, 10)]
|
1032
|
+
)
|
1033
|
+
picking.action_assign()
|
1034
|
+
|
1035
|
+
# pick the goods
|
1036
|
+
picking.move_line_ids.qty_done = 10
|
1037
|
+
|
1038
|
+
barcode = self.shelf1.barcode
|
1039
|
+
params = {"barcode": barcode}
|
1040
|
+
response = self.service.dispatch("start", params=params)
|
1041
|
+
self.assert_response(
|
1042
|
+
response,
|
1043
|
+
next_state="start",
|
1044
|
+
message=self.service.msg_store.package_already_picked_by(package, picking),
|
1045
|
+
)
|
1046
|
+
# no change in the picking
|
1047
|
+
self.assertEqual(picking.state, "assigned")
|
1048
|
+
self.assertRecordValues(picking.package_level_ids, [{"package_id": package.id}])
|
1049
|
+
|
1050
|
+
def test_start_package_unreserve_disabled_error(self):
|
1051
|
+
"""Test /start when the package was already reserved...
|
1052
|
+
|
1053
|
+
...for another picking type and unreserving is disallowed.
|
1054
|
+
|
1055
|
+
When we scan a location which contains only one package,
|
1056
|
+
we want to move this package.
|
1057
|
+
|
1058
|
+
The pre-conditions:
|
1059
|
+
|
1060
|
+
* A package exists in Stock/Shelf1.
|
1061
|
+
* A stock picking exists to move the package from Stock/Shelf1 to
|
1062
|
+
Out with a different picking type. The move is "assigned".
|
1063
|
+
|
1064
|
+
Expected result:
|
1065
|
+
|
1066
|
+
* receive an error that we cannot pick the package
|
1067
|
+
"""
|
1068
|
+
self.menu.sudo().allow_move_create = True
|
1069
|
+
self.menu.sudo().allow_unreserve_other_moves = False
|
1070
|
+
|
1071
|
+
package = self.env["stock.quant.package"].create({})
|
1072
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 10, package=package)
|
1073
|
+
# create a picking of another picking type
|
1074
|
+
picking = self._create_picking(
|
1075
|
+
picking_type=self.wh.out_type_id, lines=[(self.product_a, 10)]
|
1076
|
+
)
|
1077
|
+
picking.action_assign()
|
1078
|
+
barcode = self.shelf1.barcode
|
1079
|
+
params = {"barcode": barcode}
|
1080
|
+
response = self.service.dispatch("start", params=params)
|
1081
|
+
self.assert_response(
|
1082
|
+
response,
|
1083
|
+
next_state="start",
|
1084
|
+
message=self.service.msg_store.package_already_picked_by(package, picking),
|
1085
|
+
)
|
1086
|
+
# no change in the picking
|
1087
|
+
self.assertEqual(picking.state, "assigned")
|
1088
|
+
self.assertRecordValues(picking.package_level_ids, [{"package_id": package.id}])
|
1089
|
+
|
1090
|
+
def test_start_package_unreserve_no_create_error(self):
|
1091
|
+
"""Test /start when the package was already reserved...
|
1092
|
+
|
1093
|
+
...for another picking type and unreserving is allowed
|
1094
|
+
and the option to create a move is not allowed.
|
1095
|
+
|
1096
|
+
This test ensure that the unreservation of the first package
|
1097
|
+
is rollbacked.
|
1098
|
+
|
1099
|
+
"""
|
1100
|
+
self.menu.sudo().allow_move_create = False
|
1101
|
+
self.menu.sudo().allow_unreserve_other_moves = True
|
1102
|
+
|
1103
|
+
package = self.env["stock.quant.package"].create({})
|
1104
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 10, package=package)
|
1105
|
+
# create a picking of another picking type
|
1106
|
+
picking = self._create_picking(
|
1107
|
+
picking_type=self.wh.out_type_id, lines=[(self.product_a, 10)]
|
1108
|
+
)
|
1109
|
+
picking.action_assign()
|
1110
|
+
self.assertEqual(picking.state, "assigned")
|
1111
|
+
barcode = self.shelf1.barcode
|
1112
|
+
params = {"barcode": barcode}
|
1113
|
+
response = self.service.dispatch("start", params=params)
|
1114
|
+
self.assert_response(
|
1115
|
+
response,
|
1116
|
+
next_state="start",
|
1117
|
+
message=self.service.msg_store.no_pending_operation_for_pack(package),
|
1118
|
+
)
|
1119
|
+
# no change in the picking
|
1120
|
+
self.assertEqual(picking.state, "assigned")
|
1121
|
+
self.assertRecordValues(picking.package_level_ids, [{"package_id": package.id}])
|