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,451 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from itertools import product
|
4
|
+
from unittest import mock
|
5
|
+
|
6
|
+
from .test_checkout_base import CheckoutCommonCase
|
7
|
+
from .test_checkout_select_package_base import CheckoutSelectPackageMixin
|
8
|
+
|
9
|
+
|
10
|
+
class CheckoutScanPackageActionCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
|
11
|
+
def _test_select_product(
|
12
|
+
self, barcode_func, origin_qty_func, expected_qty_func, in_lot=False
|
13
|
+
):
|
14
|
+
picking = self._create_picking(
|
15
|
+
lines=[(self.product_a, 10), (self.product_b, 10), (self.product_c, 10)]
|
16
|
+
)
|
17
|
+
for move_line in picking.move_ids:
|
18
|
+
# put in 3 different packages
|
19
|
+
self._fill_stock_for_moves(move_line, in_package=True, in_lot=in_lot)
|
20
|
+
picking.action_assign()
|
21
|
+
|
22
|
+
# we have selected the pack that contains product a
|
23
|
+
line_a = picking.move_line_ids[0]
|
24
|
+
line_a.qty_done = origin_qty_func(line_a)
|
25
|
+
|
26
|
+
response = self.service.dispatch(
|
27
|
+
"scan_package_action",
|
28
|
+
params={
|
29
|
+
"picking_id": picking.id,
|
30
|
+
"selected_line_ids": line_a.ids,
|
31
|
+
"barcode": barcode_func(line_a),
|
32
|
+
},
|
33
|
+
)
|
34
|
+
|
35
|
+
# since we scanned the barcode of the product and we had a
|
36
|
+
# qty_done, the qty_done should flip to 0
|
37
|
+
self._assert_selected_qties(
|
38
|
+
response, line_a, {line_a: expected_qty_func(line_a)}
|
39
|
+
)
|
40
|
+
|
41
|
+
def test_scan_package_action_select_product(self):
|
42
|
+
self._test_select_product(
|
43
|
+
lambda l: l.product_id.barcode, lambda l: l.reserved_uom_qty, lambda __: 0
|
44
|
+
)
|
45
|
+
|
46
|
+
def test_scan_package_action_deselect_product(self):
|
47
|
+
self._test_select_product(
|
48
|
+
lambda l: l.product_id.barcode, lambda __: 0, lambda l: l.reserved_uom_qty
|
49
|
+
)
|
50
|
+
|
51
|
+
def test_scan_package_action_select_product_packaging(self):
|
52
|
+
self._test_select_product(
|
53
|
+
lambda l: l.product_id.packaging_ids.barcode,
|
54
|
+
lambda l: l.reserved_uom_qty,
|
55
|
+
lambda __: 0,
|
56
|
+
)
|
57
|
+
|
58
|
+
def test_scan_package_action_deselect_product_packaging(self):
|
59
|
+
self._test_select_product(
|
60
|
+
lambda l: l.product_id.packaging_ids.barcode,
|
61
|
+
lambda __: 0,
|
62
|
+
lambda l: l.reserved_uom_qty,
|
63
|
+
)
|
64
|
+
|
65
|
+
def test_scan_package_action_select_product_lot(self):
|
66
|
+
self._test_select_product(
|
67
|
+
lambda l: l.lot_id.name,
|
68
|
+
lambda __: 0,
|
69
|
+
lambda l: l.reserved_uom_qty,
|
70
|
+
in_lot=True,
|
71
|
+
)
|
72
|
+
|
73
|
+
def test_scan_package_action_deselect_product_lot(self):
|
74
|
+
self._test_select_product(
|
75
|
+
lambda l: l.lot_id.name,
|
76
|
+
lambda l: l.reserved_uom_qty,
|
77
|
+
lambda __: 0,
|
78
|
+
in_lot=True,
|
79
|
+
)
|
80
|
+
|
81
|
+
def _test_scan_package_action_scan_product_error_tracked_by(
|
82
|
+
self, tracked_by, barcode
|
83
|
+
):
|
84
|
+
self.product_a.tracking = tracked_by
|
85
|
+
picking = self._create_picking(lines=[(self.product_a, 1)])
|
86
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
87
|
+
picking.action_assign()
|
88
|
+
move_line = picking.move_line_ids
|
89
|
+
origin_qty_done = move_line.qty_done
|
90
|
+
response = self.service.dispatch(
|
91
|
+
"scan_package_action",
|
92
|
+
params={
|
93
|
+
"picking_id": picking.id,
|
94
|
+
"selected_line_ids": move_line.ids,
|
95
|
+
"barcode": barcode,
|
96
|
+
},
|
97
|
+
)
|
98
|
+
self._assert_selected_qties(
|
99
|
+
response,
|
100
|
+
move_line,
|
101
|
+
# no change as the scan was not valid
|
102
|
+
{move_line: origin_qty_done},
|
103
|
+
message={
|
104
|
+
"message_type": "warning",
|
105
|
+
"body": "Product tracked by lot, please scan one.",
|
106
|
+
},
|
107
|
+
)
|
108
|
+
|
109
|
+
def test_scan_package_action_scan_product_error_tracking(self):
|
110
|
+
trackings = ("lot", "serial")
|
111
|
+
barcodes = (self.product_a.barcode, self.product_a.packaging_ids.barcode)
|
112
|
+
for tracking, barcode in product(trackings, barcodes):
|
113
|
+
self._test_scan_package_action_scan_product_error_tracked_by(
|
114
|
+
tracking, barcode
|
115
|
+
)
|
116
|
+
|
117
|
+
def test_scan_package_action_scan_package_keep_source_package_error(self):
|
118
|
+
picking = self._create_picking(
|
119
|
+
lines=[
|
120
|
+
(self.product_a, 10),
|
121
|
+
(self.product_b, 10),
|
122
|
+
(self.product_c, 10),
|
123
|
+
(self.product_d, 10),
|
124
|
+
]
|
125
|
+
)
|
126
|
+
pack1_moves = picking.move_ids[:3]
|
127
|
+
pack2_moves = picking.move_ids[3:]
|
128
|
+
# put in 2 packs, for this test, we'll work on pack1
|
129
|
+
self._fill_stock_for_moves(pack1_moves, in_package=True)
|
130
|
+
self._fill_stock_for_moves(pack2_moves, in_package=True)
|
131
|
+
picking.action_assign()
|
132
|
+
|
133
|
+
selected_lines = pack1_moves.move_line_ids
|
134
|
+
pack1 = pack1_moves.move_line_ids.package_id
|
135
|
+
|
136
|
+
move_line1, move_line2, move_line3 = selected_lines
|
137
|
+
# We'll put only product A and B in the package
|
138
|
+
move_line1.qty_done = move_line1.reserved_uom_qty
|
139
|
+
move_line2.qty_done = move_line2.reserved_uom_qty
|
140
|
+
move_line3.qty_done = 0
|
141
|
+
|
142
|
+
response = self.service.dispatch(
|
143
|
+
"scan_package_action",
|
144
|
+
params={
|
145
|
+
"picking_id": picking.id,
|
146
|
+
"selected_line_ids": selected_lines.ids,
|
147
|
+
# we try to keep the goods in the same package, so we scan the
|
148
|
+
# source package but this isn't allowed as it is not a delivery
|
149
|
+
# package (i.e. having a delivery packaging set)
|
150
|
+
"barcode": pack1.name,
|
151
|
+
},
|
152
|
+
)
|
153
|
+
|
154
|
+
self.assertRecordValues(
|
155
|
+
move_line1,
|
156
|
+
[{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
|
157
|
+
)
|
158
|
+
self.assertRecordValues(
|
159
|
+
move_line2,
|
160
|
+
[{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
|
161
|
+
)
|
162
|
+
self.assertRecordValues(
|
163
|
+
move_line3,
|
164
|
+
# qty_done was zero so it hasn't been done anyway
|
165
|
+
[{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
|
166
|
+
)
|
167
|
+
self.assert_response(
|
168
|
+
response,
|
169
|
+
# go pack to the screen to select lines to put in packages
|
170
|
+
next_state="select_package",
|
171
|
+
data={
|
172
|
+
"picking": self.data.picking(picking),
|
173
|
+
"selected_move_lines": self.data.move_lines(selected_lines),
|
174
|
+
"packing_info": self.service._data_for_packing_info(picking),
|
175
|
+
"no_package_enabled": not self.service.options.get(
|
176
|
+
"checkout__disable_no_package"
|
177
|
+
),
|
178
|
+
},
|
179
|
+
message=self.service.msg_store.dest_package_not_valid(pack1),
|
180
|
+
)
|
181
|
+
|
182
|
+
def test_scan_package_action_scan_package_error_invalid(self):
|
183
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
184
|
+
move = picking.move_ids
|
185
|
+
self._fill_stock_for_moves(move, in_package=True)
|
186
|
+
picking.action_assign()
|
187
|
+
|
188
|
+
selected_line = move.move_line_ids
|
189
|
+
other_package = self.env["stock.quant.package"].create({})
|
190
|
+
|
191
|
+
response = self.service.dispatch(
|
192
|
+
"scan_package_action",
|
193
|
+
params={
|
194
|
+
"picking_id": picking.id,
|
195
|
+
"selected_line_ids": selected_line.ids,
|
196
|
+
"barcode": other_package.name,
|
197
|
+
},
|
198
|
+
)
|
199
|
+
|
200
|
+
self.assertRecordValues(
|
201
|
+
selected_line,
|
202
|
+
[
|
203
|
+
{
|
204
|
+
# the result package must remain identical, so equal to the
|
205
|
+
# source package
|
206
|
+
"result_package_id": selected_line.package_id.id,
|
207
|
+
"shopfloor_checkout_done": False,
|
208
|
+
}
|
209
|
+
],
|
210
|
+
)
|
211
|
+
self._assert_selected_response(
|
212
|
+
response,
|
213
|
+
selected_line,
|
214
|
+
message=self.service.msg_store.dest_package_not_valid(other_package),
|
215
|
+
)
|
216
|
+
|
217
|
+
def test_scan_package_action_scan_package_use_existing_package_ok(self):
|
218
|
+
picking = self._create_picking(
|
219
|
+
lines=[
|
220
|
+
(self.product_a, 10),
|
221
|
+
(self.product_b, 10),
|
222
|
+
(self.product_c, 10),
|
223
|
+
(self.product_d, 10),
|
224
|
+
]
|
225
|
+
)
|
226
|
+
pack1_moves = picking.move_ids[:3]
|
227
|
+
pack2_moves = picking.move_ids[3:]
|
228
|
+
# put in 2 packs, for this test, we'll work on pack1
|
229
|
+
self._fill_stock_for_moves(pack1_moves, in_package=True)
|
230
|
+
self._fill_stock_for_moves(pack2_moves, in_package=True)
|
231
|
+
picking.action_assign()
|
232
|
+
|
233
|
+
package = self.env["stock.quant.package"].create(
|
234
|
+
{"package_type_id": self.delivery_packaging.id}
|
235
|
+
)
|
236
|
+
|
237
|
+
# assume that product d was already put in a package,
|
238
|
+
# we must be able to put the lines of pack1 inside the same
|
239
|
+
pack2_moves.move_line_ids.write(
|
240
|
+
{"result_package_id": package.id, "shopfloor_checkout_done": True}
|
241
|
+
)
|
242
|
+
|
243
|
+
selected_lines = pack1_moves.move_line_ids
|
244
|
+
# they are all selected
|
245
|
+
selected_lines.write({"qty_done": 10.0})
|
246
|
+
|
247
|
+
response = self.service.dispatch(
|
248
|
+
"scan_package_action",
|
249
|
+
params={
|
250
|
+
"picking_id": picking.id,
|
251
|
+
"selected_line_ids": selected_lines.ids,
|
252
|
+
# use the package that was used for product D
|
253
|
+
"barcode": package.name,
|
254
|
+
},
|
255
|
+
)
|
256
|
+
|
257
|
+
self.assertRecordValues(
|
258
|
+
selected_lines,
|
259
|
+
[
|
260
|
+
{"result_package_id": package.id, "shopfloor_checkout_done": True},
|
261
|
+
{"result_package_id": package.id, "shopfloor_checkout_done": True},
|
262
|
+
{"result_package_id": package.id, "shopfloor_checkout_done": True},
|
263
|
+
],
|
264
|
+
)
|
265
|
+
|
266
|
+
self.assert_response(
|
267
|
+
response,
|
268
|
+
# all the lines are packed, so we expect to go the summary screen
|
269
|
+
next_state="summary",
|
270
|
+
data={
|
271
|
+
"picking": self._stock_picking_data(picking, done=True),
|
272
|
+
"all_processed": True,
|
273
|
+
},
|
274
|
+
message=self.msg_store.goods_packed_in(package),
|
275
|
+
)
|
276
|
+
|
277
|
+
def test_scan_package_action_scan_packaging_ok(self):
|
278
|
+
picking = self._create_picking(
|
279
|
+
lines=[
|
280
|
+
(self.product_a, 10),
|
281
|
+
(self.product_b, 10),
|
282
|
+
(self.product_c, 10),
|
283
|
+
(self.product_d, 10),
|
284
|
+
]
|
285
|
+
)
|
286
|
+
pack1_moves = picking.move_ids[:3]
|
287
|
+
pack2_moves = picking.move_ids[3:]
|
288
|
+
# put in 2 packs, for this test, we'll work on pack1
|
289
|
+
self._fill_stock_for_moves(pack1_moves, in_package=True)
|
290
|
+
self._fill_stock_for_moves(pack2_moves, in_package=True)
|
291
|
+
picking.action_assign()
|
292
|
+
|
293
|
+
selected_lines = pack1_moves.move_line_ids
|
294
|
+
pack1 = pack1_moves.move_line_ids.package_id
|
295
|
+
|
296
|
+
move_line1, move_line2, move_line3 = selected_lines
|
297
|
+
# we'll put only the first 2 lines (product A and B) in the new package
|
298
|
+
move_line1.qty_done = move_line1.reserved_uom_qty
|
299
|
+
move_line2.qty_done = move_line2.reserved_uom_qty
|
300
|
+
move_line3.qty_done = 0
|
301
|
+
|
302
|
+
packaging = (
|
303
|
+
self.env["stock.package.type"]
|
304
|
+
.sudo()
|
305
|
+
.create(
|
306
|
+
{
|
307
|
+
"name": "Pallet",
|
308
|
+
"barcode": "PPP",
|
309
|
+
"height": 12,
|
310
|
+
"width": 13,
|
311
|
+
"packaging_length": 14,
|
312
|
+
}
|
313
|
+
)
|
314
|
+
)
|
315
|
+
|
316
|
+
response = self.service.dispatch(
|
317
|
+
"scan_package_action",
|
318
|
+
params={
|
319
|
+
"picking_id": picking.id,
|
320
|
+
"selected_line_ids": selected_lines.ids,
|
321
|
+
# create a new package using this packaging
|
322
|
+
"barcode": packaging.barcode,
|
323
|
+
},
|
324
|
+
)
|
325
|
+
|
326
|
+
new_package = move_line1.result_package_id
|
327
|
+
self.assertNotEqual(pack1, new_package)
|
328
|
+
|
329
|
+
self.assertRecordValues(
|
330
|
+
new_package,
|
331
|
+
[
|
332
|
+
{
|
333
|
+
"package_type_id": packaging.id,
|
334
|
+
"pack_length": packaging.packaging_length,
|
335
|
+
"width": packaging.width,
|
336
|
+
"height": packaging.height,
|
337
|
+
}
|
338
|
+
],
|
339
|
+
)
|
340
|
+
|
341
|
+
self.assertRecordValues(
|
342
|
+
move_line1,
|
343
|
+
[{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
|
344
|
+
)
|
345
|
+
self.assertRecordValues(
|
346
|
+
move_line2,
|
347
|
+
[{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
|
348
|
+
)
|
349
|
+
self.assertRecordValues(
|
350
|
+
move_line3,
|
351
|
+
# qty_done was zero so we don't set it as packed and it remains in
|
352
|
+
# the same package
|
353
|
+
[{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
|
354
|
+
)
|
355
|
+
self.assert_response(
|
356
|
+
response,
|
357
|
+
next_state="select_line",
|
358
|
+
data=self._data_for_select_line(picking),
|
359
|
+
message=self.msg_store.goods_packed_in(new_package),
|
360
|
+
)
|
361
|
+
|
362
|
+
def test_scan_package_action_scan_packaging_bad_carrier(self):
|
363
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
364
|
+
picking.carrier_id = picking.carrier_id.search([], limit=1)
|
365
|
+
pack1_moves = picking.move_ids
|
366
|
+
# put in 2 packs, for this test, we'll work on pack1
|
367
|
+
self._fill_stock_for_moves(pack1_moves, in_package=True)
|
368
|
+
picking.action_assign()
|
369
|
+
selected_lines = pack1_moves.move_line_ids
|
370
|
+
selected_lines.qty_done = selected_lines.reserved_uom_qty
|
371
|
+
|
372
|
+
packaging = (
|
373
|
+
self.env["stock.package.type"]
|
374
|
+
.sudo()
|
375
|
+
.create(
|
376
|
+
{
|
377
|
+
"name": "DeliverX",
|
378
|
+
"package_carrier_type": "none",
|
379
|
+
"barcode": "XXX",
|
380
|
+
}
|
381
|
+
)
|
382
|
+
)
|
383
|
+
# Delivery type and package_carrier_type values
|
384
|
+
# depend on specific implementations that we don't have as dependency.
|
385
|
+
# What is important here is to simulate their value when mismatching.
|
386
|
+
mock1 = mock.patch.object(
|
387
|
+
type(packaging),
|
388
|
+
"package_carrier_type",
|
389
|
+
new_callable=mock.PropertyMock,
|
390
|
+
)
|
391
|
+
mock2 = mock.patch.object(
|
392
|
+
type(picking.carrier_id),
|
393
|
+
"delivery_type",
|
394
|
+
new_callable=mock.PropertyMock,
|
395
|
+
)
|
396
|
+
with mock1 as mocked_package_carrier_type, mock2 as mocked_delivery_type:
|
397
|
+
# Not matching at all -> bad
|
398
|
+
mocked_package_carrier_type.return_value = "DHL"
|
399
|
+
mocked_delivery_type.return_value = "UPS"
|
400
|
+
response = self.service.dispatch(
|
401
|
+
"scan_package_action",
|
402
|
+
params={
|
403
|
+
"picking_id": picking.id,
|
404
|
+
"selected_line_ids": selected_lines.ids,
|
405
|
+
# create a new package using this packaging
|
406
|
+
"barcode": packaging.barcode,
|
407
|
+
},
|
408
|
+
)
|
409
|
+
self._assert_selected_response(
|
410
|
+
response,
|
411
|
+
selected_lines,
|
412
|
+
message=self.msg_store.packaging_invalid_for_carrier(
|
413
|
+
packaging, picking.carrier_id
|
414
|
+
),
|
415
|
+
)
|
416
|
+
# No carrier type set on the packaging -> good
|
417
|
+
mocked_package_carrier_type.return_value = "none"
|
418
|
+
response = self.service.dispatch(
|
419
|
+
"scan_package_action",
|
420
|
+
params={
|
421
|
+
"picking_id": picking.id,
|
422
|
+
"selected_line_ids": selected_lines.ids,
|
423
|
+
# create a new package using this packaging
|
424
|
+
"barcode": packaging.barcode,
|
425
|
+
},
|
426
|
+
)
|
427
|
+
self.assertEqual(
|
428
|
+
response["message"],
|
429
|
+
self.msg_store.goods_packed_in(selected_lines.result_package_id),
|
430
|
+
)
|
431
|
+
|
432
|
+
def test_scan_package_action_scan_not_found(self):
|
433
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
434
|
+
move = picking.move_ids
|
435
|
+
self._fill_stock_for_moves(move, in_package=True)
|
436
|
+
picking.action_assign()
|
437
|
+
selected_line = move.move_line_ids
|
438
|
+
response = self.service.dispatch(
|
439
|
+
"scan_package_action",
|
440
|
+
params={
|
441
|
+
"picking_id": picking.id,
|
442
|
+
"selected_line_ids": selected_line.ids,
|
443
|
+
# create a new package using this packaging
|
444
|
+
"barcode": "BARCODE NOT FOUND",
|
445
|
+
},
|
446
|
+
)
|
447
|
+
self._assert_selected_response(
|
448
|
+
response,
|
449
|
+
selected_line,
|
450
|
+
message={"message_type": "error", "body": "Barcode not found"},
|
451
|
+
)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .test_checkout_base import CheckoutCommonCase
|
5
|
+
from .test_checkout_select_package_base import CheckoutSelectPackageMixin
|
6
|
+
|
7
|
+
# pylint: disable=missing-return
|
8
|
+
|
9
|
+
|
10
|
+
class CheckoutScanPackageActionCaseNoPrefillQty(
|
11
|
+
CheckoutCommonCase, CheckoutSelectPackageMixin
|
12
|
+
):
|
13
|
+
@classmethod
|
14
|
+
def setUpClass(cls):
|
15
|
+
super().setUpClass()
|
16
|
+
cls.menu.sudo().no_prefill_qty = True
|
17
|
+
|
18
|
+
def test_scan_package_action_scan_product_to_increment_qty(self):
|
19
|
+
""" """
|
20
|
+
picking = self._create_picking(lines=[(self.product_a, 3)])
|
21
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=False)
|
22
|
+
picking.action_assign()
|
23
|
+
move_line = picking.move_line_ids
|
24
|
+
origin_qty_done = move_line.qty_done = 2
|
25
|
+
response = self.service.dispatch(
|
26
|
+
"scan_package_action",
|
27
|
+
params={
|
28
|
+
"picking_id": picking.id,
|
29
|
+
"selected_line_ids": move_line.ids,
|
30
|
+
"barcode": move_line.product_id.barcode,
|
31
|
+
},
|
32
|
+
)
|
33
|
+
self._assert_selected_qties(
|
34
|
+
response,
|
35
|
+
move_line,
|
36
|
+
{move_line: origin_qty_done + 1},
|
37
|
+
)
|
38
|
+
|
39
|
+
def test_scan_package_action_scan_product2_to_increment_qty(self):
|
40
|
+
"""Scan a product which is present in two lines.
|
41
|
+
|
42
|
+
Only one line should have its quantity incremented.
|
43
|
+
|
44
|
+
"""
|
45
|
+
picking = self._create_picking(
|
46
|
+
lines=[(self.product_a, 3), (self.product_a, 1)], confirm=False
|
47
|
+
)
|
48
|
+
picking.move_ids._action_confirm(merge=False)
|
49
|
+
picking.action_confirm()
|
50
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=False)
|
51
|
+
picking.action_assign()
|
52
|
+
move_lines = picking.move_line_ids
|
53
|
+
response = self.service.dispatch(
|
54
|
+
"scan_package_action",
|
55
|
+
params={
|
56
|
+
"picking_id": picking.id,
|
57
|
+
"selected_line_ids": move_lines.ids,
|
58
|
+
"barcode": self.product_a.barcode,
|
59
|
+
},
|
60
|
+
)
|
61
|
+
self._assert_selected_qties(
|
62
|
+
response,
|
63
|
+
move_lines,
|
64
|
+
{move_lines[0]: 1, move_lines[1]: 0},
|
65
|
+
)
|
66
|
+
|
67
|
+
def test_scan_package_action_scan_lot_to_increment_qty(self):
|
68
|
+
""" """
|
69
|
+
picking = self._create_picking(lines=[(self.product_a, 3)])
|
70
|
+
self._fill_stock_for_moves(picking.move_ids, in_lot=True)
|
71
|
+
picking.action_assign()
|
72
|
+
move_line = picking.move_line_ids
|
73
|
+
origin_qty_done = move_line.qty_done = 2
|
74
|
+
response = self.service.dispatch(
|
75
|
+
"scan_package_action",
|
76
|
+
params={
|
77
|
+
"picking_id": picking.id,
|
78
|
+
"selected_line_ids": move_line.ids,
|
79
|
+
"barcode": move_line.lot_id.name,
|
80
|
+
},
|
81
|
+
)
|
82
|
+
self._assert_selected_qties(
|
83
|
+
response,
|
84
|
+
move_line,
|
85
|
+
{move_line: origin_qty_done + 1},
|
86
|
+
)
|
87
|
+
|
88
|
+
def test_scan_package_action_scan_packaging_to_increment_qty(self):
|
89
|
+
""" """
|
90
|
+
picking = self._create_picking(lines=[(self.product_a, 3)])
|
91
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True, in_lot=False)
|
92
|
+
picking.action_assign()
|
93
|
+
move_line = picking.move_line_ids
|
94
|
+
origin_qty_done = move_line.qty_done = 0
|
95
|
+
response = self.service.dispatch(
|
96
|
+
"scan_package_action",
|
97
|
+
params={
|
98
|
+
"picking_id": picking.id,
|
99
|
+
"selected_line_ids": move_line.ids,
|
100
|
+
"barcode": self.product_a_packaging.barcode,
|
101
|
+
},
|
102
|
+
)
|
103
|
+
self._assert_selected_qties(
|
104
|
+
response,
|
105
|
+
move_line,
|
106
|
+
{move_line: origin_qty_done + self.product_a_packaging.qty},
|
107
|
+
)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from .test_checkout_base import CheckoutCommonCase
|
4
|
+
|
5
|
+
# pylint: disable=missing-return
|
6
|
+
|
7
|
+
|
8
|
+
class CheckoutListStockPickingCase(CheckoutCommonCase):
|
9
|
+
def test_list_stock_picking(self):
|
10
|
+
picking1 = self._create_picking()
|
11
|
+
picking2 = self._create_picking()
|
12
|
+
# should not be in the list because another type:
|
13
|
+
picking3 = self._create_picking(picking_type=self.wh.pick_type_id)
|
14
|
+
# should not be in list because not assigned:
|
15
|
+
self._create_picking()
|
16
|
+
to_assign = picking1 | picking2 | picking3
|
17
|
+
self._fill_stock_for_moves(to_assign.move_ids, in_package=True)
|
18
|
+
to_assign.action_assign()
|
19
|
+
response = self.service.dispatch("list_stock_picking", params={})
|
20
|
+
expected = {
|
21
|
+
"pickings": [
|
22
|
+
self._picking_summary_data(picking1),
|
23
|
+
self._picking_summary_data(picking2),
|
24
|
+
]
|
25
|
+
}
|
26
|
+
|
27
|
+
self.assert_response(response, next_state="manual_selection", data=expected)
|
28
|
+
|
29
|
+
|
30
|
+
class CheckoutSelectCase(CheckoutCommonCase):
|
31
|
+
@classmethod
|
32
|
+
def setUpClassBaseData(cls):
|
33
|
+
super().setUpClassBaseData()
|
34
|
+
cls.picking = cls._create_picking()
|
35
|
+
cls._fill_stock_for_moves(cls.picking.move_ids, in_package=True)
|
36
|
+
cls.picking.action_assign()
|
37
|
+
|
38
|
+
def test_select_ok(self):
|
39
|
+
response = self.service.dispatch(
|
40
|
+
"select", params={"picking_id": self.picking.id}
|
41
|
+
)
|
42
|
+
self.assert_response(
|
43
|
+
response,
|
44
|
+
next_state="select_line",
|
45
|
+
data=self._data_for_select_line(self.picking),
|
46
|
+
)
|
47
|
+
|
48
|
+
def _test_error(self, picking, msg):
|
49
|
+
response = self.service.dispatch("select", params={"picking_id": picking.id})
|
50
|
+
self.assert_response(
|
51
|
+
response,
|
52
|
+
next_state="manual_selection",
|
53
|
+
message={"message_type": "error", "body": msg},
|
54
|
+
data={"pickings": [self._picking_summary_data(self.picking)]},
|
55
|
+
)
|
56
|
+
|
57
|
+
def test_select_error_not_found(self):
|
58
|
+
picking = self._create_picking()
|
59
|
+
picking.sudo().unlink()
|
60
|
+
self._test_error(
|
61
|
+
picking, self.service.msg_store.stock_picking_not_found()["body"]
|
62
|
+
)
|
63
|
+
|
64
|
+
def test_select_error_not_available(self):
|
65
|
+
picking = self._create_picking()
|
66
|
+
self._test_error(
|
67
|
+
picking, self.service.msg_store.stock_picking_not_available(picking)["body"]
|
68
|
+
)
|
69
|
+
|
70
|
+
def test_select_error_not_allowed(self):
|
71
|
+
picking = self._create_picking(picking_type=self.wh.pick_type_id)
|
72
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
73
|
+
picking.action_assign()
|
74
|
+
self._test_error(picking, "You cannot move this using this menu.")
|