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,377 @@
|
|
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_scan_line_base import CheckoutScanLineCaseBase
|
4
|
+
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
class CheckoutScanLineCase(CheckoutScanLineCaseBase):
|
8
|
+
@classmethod
|
9
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
10
|
+
super().setUpClassBaseData(*args, **kwargs)
|
11
|
+
cls.delivery_packaging = (
|
12
|
+
cls.env["stock.package.type"]
|
13
|
+
.sudo()
|
14
|
+
.create(
|
15
|
+
{
|
16
|
+
"name": "DelivBox",
|
17
|
+
"barcode": "DelivBox",
|
18
|
+
}
|
19
|
+
)
|
20
|
+
)
|
21
|
+
|
22
|
+
def test_scan_line_package_ok(self):
|
23
|
+
picking = self._create_picking(
|
24
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
25
|
+
)
|
26
|
+
move1 = picking.move_ids[0]
|
27
|
+
move2 = picking.move_ids[1]
|
28
|
+
# put the lines in 2 separate packages (only the first line should be selected
|
29
|
+
# by the package barcode)
|
30
|
+
self._fill_stock_for_moves(move1, in_package=True)
|
31
|
+
self._fill_stock_for_moves(move2, in_package=True)
|
32
|
+
picking.action_assign()
|
33
|
+
move_line = move1.move_line_ids
|
34
|
+
self._test_scan_line_ok(move_line.package_id.name, move_line)
|
35
|
+
|
36
|
+
def test_scan_line_package_ok_packing_info_empty_info(self):
|
37
|
+
picking = self._create_picking(
|
38
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
39
|
+
)
|
40
|
+
move1 = picking.move_ids[0]
|
41
|
+
move2 = picking.move_ids[1]
|
42
|
+
# put the lines in 2 separate packages (only the first line should be selected
|
43
|
+
# by the package barcode)
|
44
|
+
self._fill_stock_for_moves(move1, in_package=True)
|
45
|
+
self._fill_stock_for_moves(move2, in_package=True)
|
46
|
+
picking.action_assign()
|
47
|
+
move_line = move1.move_line_ids
|
48
|
+
self._test_scan_line_ok(move_line.package_id.name, move_line)
|
49
|
+
|
50
|
+
def test_scan_line_package_several_lines_ok(self):
|
51
|
+
picking = self._create_picking(
|
52
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
53
|
+
)
|
54
|
+
# put all the lines in the same source package
|
55
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
56
|
+
picking.action_assign()
|
57
|
+
package = picking.move_line_ids.mapped("package_id")
|
58
|
+
self._test_scan_line_ok(package.name, picking.move_line_ids)
|
59
|
+
|
60
|
+
def test_scan_line_product_ok(self):
|
61
|
+
picking = self._create_picking(
|
62
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
63
|
+
)
|
64
|
+
# do not put them in a package, we'll pack units here
|
65
|
+
self._fill_stock_for_moves(picking.move_ids)
|
66
|
+
picking.action_assign()
|
67
|
+
# The product a is scanned, so selected and quantity updated
|
68
|
+
line_a = picking.move_line_ids.filtered(
|
69
|
+
lambda l: l.product_id == self.product_a
|
70
|
+
)
|
71
|
+
# Because not part of a package other lines are selected also
|
72
|
+
related_lines = picking.move_line_ids - line_a
|
73
|
+
selected_lines = picking.move_line_ids
|
74
|
+
self._test_scan_line_ok(
|
75
|
+
self.product_a.barcode, selected_lines, related_lines=related_lines
|
76
|
+
)
|
77
|
+
|
78
|
+
def test_scan_line_product_several_lines_ok(self):
|
79
|
+
picking = self._create_picking(
|
80
|
+
lines=[(self.product_a, 10), (self.product_a, 10), (self.product_b, 10)]
|
81
|
+
)
|
82
|
+
self._fill_stock_for_moves(picking.move_ids)
|
83
|
+
picking.action_assign()
|
84
|
+
# The product a is scanned, so selected and quantity updated
|
85
|
+
lines_a = picking.move_line_ids.filtered(
|
86
|
+
lambda l: l.product_id == self.product_a
|
87
|
+
)
|
88
|
+
# Because not part of a package other lines are selected also
|
89
|
+
related_lines = picking.move_line_ids - lines_a
|
90
|
+
selected_lines = picking.move_line_ids
|
91
|
+
self._test_scan_line_ok(
|
92
|
+
self.product_a.barcode, selected_lines, related_lines=related_lines
|
93
|
+
)
|
94
|
+
|
95
|
+
def test_scan_line_product_packaging_ok(self):
|
96
|
+
picking = self._create_picking(
|
97
|
+
lines=[(self.product_a, 10), (self.product_a, 10), (self.product_b, 10)]
|
98
|
+
)
|
99
|
+
self._fill_stock_for_moves(picking.move_ids)
|
100
|
+
picking.action_assign()
|
101
|
+
lines_a = picking.move_line_ids.filtered(
|
102
|
+
lambda l: l.product_id == self.product_a
|
103
|
+
)
|
104
|
+
# when we scan the packaging of the product, we should select the
|
105
|
+
# lines as if the product was scanned
|
106
|
+
# Because not part of a package other lines are selected also
|
107
|
+
related_lines = picking.move_line_ids - lines_a
|
108
|
+
selected_lines = picking.move_line_ids
|
109
|
+
self._test_scan_line_ok(
|
110
|
+
self.product_a_packaging.barcode, selected_lines, related_lines
|
111
|
+
)
|
112
|
+
|
113
|
+
def test_scan_line_product_lot_ok(self):
|
114
|
+
picking = self._create_picking(
|
115
|
+
lines=[(self.product_a, 1), (self.product_a, 1), (self.product_b, 1)]
|
116
|
+
)
|
117
|
+
for move in picking.move_ids:
|
118
|
+
self._fill_stock_for_moves(move, in_lot=True)
|
119
|
+
picking.action_assign()
|
120
|
+
first_line = picking.move_line_ids[0]
|
121
|
+
lot = first_line.lot_id
|
122
|
+
self._test_scan_line_ok(lot.name, first_line)
|
123
|
+
|
124
|
+
def test_scan_line_product_in_one_package_all_package_lines_ok(self):
|
125
|
+
picking = self._create_picking(
|
126
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
127
|
+
)
|
128
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
129
|
+
picking.action_assign()
|
130
|
+
# Product_a and product_b are in the same package, when we scan product_a,
|
131
|
+
# we expect to work on all the lines of the package. If product_a was in
|
132
|
+
# more than one package, it would be an error.
|
133
|
+
self._test_scan_line_ok(self.product_a.barcode, picking.move_line_ids)
|
134
|
+
|
135
|
+
def _test_scan_line_error(self, picking, barcode, message):
|
136
|
+
"""Test errors for /scan_line
|
137
|
+
|
138
|
+
:param picking: the picking we are currently working with (selected)
|
139
|
+
:param barcode: the barcode we scan
|
140
|
+
:param message: the dict of expected error message
|
141
|
+
"""
|
142
|
+
response = self.service.dispatch(
|
143
|
+
"scan_line", params={"picking_id": picking.id, "barcode": barcode}
|
144
|
+
)
|
145
|
+
self.assert_response(
|
146
|
+
response,
|
147
|
+
next_state="select_line",
|
148
|
+
data=self._data_for_select_line(picking),
|
149
|
+
message=message,
|
150
|
+
)
|
151
|
+
|
152
|
+
def test_scan_line_error_barcode_not_found(self):
|
153
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
154
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
155
|
+
picking.action_assign()
|
156
|
+
self._test_scan_line_error(
|
157
|
+
picking,
|
158
|
+
"NOT A BARCODE",
|
159
|
+
{"message_type": "error", "body": "Barcode not found"},
|
160
|
+
)
|
161
|
+
|
162
|
+
def test_scan_line_error_package_not_in_picking(self):
|
163
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
164
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
165
|
+
picking2 = self._create_picking(lines=[(self.product_a, 10)])
|
166
|
+
self._fill_stock_for_moves(picking2.move_ids, in_package=True)
|
167
|
+
(picking | picking2).action_assign()
|
168
|
+
package = picking2.move_line_ids.package_id
|
169
|
+
# we work with picking, but we scan the package of picking2
|
170
|
+
self._test_scan_line_error(
|
171
|
+
picking,
|
172
|
+
package.name,
|
173
|
+
{
|
174
|
+
"message_type": "error",
|
175
|
+
"body": "Package {} is not in the current transfer.".format(
|
176
|
+
package.name
|
177
|
+
),
|
178
|
+
},
|
179
|
+
)
|
180
|
+
|
181
|
+
def test_scan_line_error_product_tracked_by_lot(self):
|
182
|
+
self.product_a.tracking = "lot"
|
183
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
184
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
185
|
+
picking.action_assign()
|
186
|
+
# product tracked by lot, but we scan the product barcode, user
|
187
|
+
# has to scan the lot
|
188
|
+
self._test_scan_line_error(
|
189
|
+
picking,
|
190
|
+
self.product_a.barcode,
|
191
|
+
{
|
192
|
+
"message_type": "warning",
|
193
|
+
"body": "Product tracked by lot, please scan one.",
|
194
|
+
},
|
195
|
+
)
|
196
|
+
|
197
|
+
def test_scan_line_error_product_in_two_packages(self):
|
198
|
+
picking = self._create_picking(
|
199
|
+
lines=[(self.product_a, 10), (self.product_a, 10)],
|
200
|
+
# when action_confirm is called, it would merge the moves
|
201
|
+
confirm=False,
|
202
|
+
)
|
203
|
+
self._fill_stock_for_moves(picking.move_ids[0], in_package=True)
|
204
|
+
self._fill_stock_for_moves(picking.move_ids[1], in_package=True)
|
205
|
+
picking.action_assign()
|
206
|
+
self._test_scan_line_error(
|
207
|
+
picking,
|
208
|
+
self.product_a.barcode,
|
209
|
+
{
|
210
|
+
"message_type": "warning",
|
211
|
+
"body": "This product is part of multiple"
|
212
|
+
" packages, please scan a package.",
|
213
|
+
},
|
214
|
+
)
|
215
|
+
|
216
|
+
def test_scan_line_error_product_in_one_package_and_unit(self):
|
217
|
+
picking = self._create_picking(
|
218
|
+
lines=[(self.product_a, 10), (self.product_a, 10)],
|
219
|
+
# when action_confirm is called, it would merge the moves
|
220
|
+
# we want to keep them separated to put a part in a package
|
221
|
+
confirm=False,
|
222
|
+
)
|
223
|
+
# put the product in one package and the other as unit
|
224
|
+
self._fill_stock_for_moves(picking.move_ids[0], in_package=True)
|
225
|
+
self._fill_stock_for_moves(picking.move_ids[1])
|
226
|
+
picking.action_assign()
|
227
|
+
self._test_scan_line_error(
|
228
|
+
picking,
|
229
|
+
self.product_a.barcode,
|
230
|
+
{
|
231
|
+
"message_type": "warning",
|
232
|
+
"body": "This product is part of multiple"
|
233
|
+
" packages, please scan a package.",
|
234
|
+
},
|
235
|
+
)
|
236
|
+
|
237
|
+
def test_scan_line_error_product_not_in_picking(self):
|
238
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
239
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
240
|
+
picking.action_assign()
|
241
|
+
self._test_scan_line_error(
|
242
|
+
picking,
|
243
|
+
self.product_b.barcode,
|
244
|
+
{
|
245
|
+
"message_type": "error",
|
246
|
+
"body": "Product is not in the current transfer.",
|
247
|
+
},
|
248
|
+
)
|
249
|
+
|
250
|
+
def test_scan_line_error_lot_not_in_picking(self):
|
251
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
252
|
+
self._fill_stock_for_moves(picking.move_ids, in_lot=True)
|
253
|
+
picking.action_assign()
|
254
|
+
lot = self.env["stock.lot"].create(
|
255
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
256
|
+
)
|
257
|
+
self._test_scan_line_error(
|
258
|
+
picking,
|
259
|
+
lot.name,
|
260
|
+
{"message_type": "error", "body": "Lot is not in the current transfer."},
|
261
|
+
)
|
262
|
+
|
263
|
+
def test_scan_line_error_lot_in_two_packages(self):
|
264
|
+
picking = self._create_picking(
|
265
|
+
lines=[(self.product_a, 10), (self.product_a, 10)],
|
266
|
+
# when action_confirm is called, it would merge the moves
|
267
|
+
confirm=False,
|
268
|
+
)
|
269
|
+
# we want the same lot to be used in 2 lines with different packages
|
270
|
+
lot = self.env["stock.lot"].create(
|
271
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
272
|
+
)
|
273
|
+
self._fill_stock_for_moves(picking.move_ids[0], in_package=True, in_lot=lot)
|
274
|
+
self._fill_stock_for_moves(picking.move_ids[1], in_package=True, in_lot=lot)
|
275
|
+
picking.action_assign()
|
276
|
+
self._test_scan_line_error(
|
277
|
+
picking,
|
278
|
+
lot.name,
|
279
|
+
{
|
280
|
+
"message_type": "warning",
|
281
|
+
"body": "This lot is part of multiple"
|
282
|
+
" packages, please scan a package.",
|
283
|
+
},
|
284
|
+
)
|
285
|
+
|
286
|
+
def test_scan_line_error_lot_in_one_package_and_unit(self):
|
287
|
+
picking = self._create_picking(
|
288
|
+
lines=[(self.product_a, 10), (self.product_a, 10)],
|
289
|
+
# when action_confirm is called, it would merge the moves
|
290
|
+
confirm=False,
|
291
|
+
)
|
292
|
+
# we want the same lot to be used in 2 lines with different packages
|
293
|
+
lot = self.env["stock.lot"].create(
|
294
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
295
|
+
)
|
296
|
+
self._fill_stock_for_moves(picking.move_ids[0], in_package=True, in_lot=lot)
|
297
|
+
self._fill_stock_for_moves(picking.move_ids[1], in_lot=lot)
|
298
|
+
picking.action_assign()
|
299
|
+
self._test_scan_line_error(
|
300
|
+
picking,
|
301
|
+
lot.name,
|
302
|
+
{
|
303
|
+
"message_type": "warning",
|
304
|
+
"body": "This lot is part of multiple"
|
305
|
+
" packages, please scan a package.",
|
306
|
+
},
|
307
|
+
)
|
308
|
+
|
309
|
+
def test_scan_line_all_lines_done(self):
|
310
|
+
picking = self._create_picking(
|
311
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
312
|
+
)
|
313
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
314
|
+
picking.action_assign()
|
315
|
+
# set all lines as done
|
316
|
+
picking.move_line_ids.write({"qty_done": 10.0, "shopfloor_checkout_done": True})
|
317
|
+
response = self.service.dispatch(
|
318
|
+
"scan_line",
|
319
|
+
params={
|
320
|
+
"picking_id": picking.id,
|
321
|
+
# the barcode doesn't matter as we have no
|
322
|
+
# lines to pack anymore
|
323
|
+
"barcode": self.product_a.barcode,
|
324
|
+
},
|
325
|
+
)
|
326
|
+
self.assert_response(
|
327
|
+
response,
|
328
|
+
next_state="summary",
|
329
|
+
data={
|
330
|
+
"picking": self._stock_picking_data(picking, done=True),
|
331
|
+
"all_processed": True,
|
332
|
+
},
|
333
|
+
)
|
334
|
+
|
335
|
+
def test_scan_line_delivery_package_ok(self):
|
336
|
+
picking = self._create_picking(
|
337
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
338
|
+
)
|
339
|
+
move1 = picking.move_ids[0]
|
340
|
+
move2 = picking.move_ids[1]
|
341
|
+
# put the lines in 2 separate packages (only the first line should be selected
|
342
|
+
# by the package barcode)
|
343
|
+
self._fill_stock_for_moves(move1, in_package=True)
|
344
|
+
self._fill_stock_for_moves(move2, in_package=True)
|
345
|
+
picking.action_assign()
|
346
|
+
result_pkgs = picking.move_line_ids.result_package_id
|
347
|
+
response = self.service.dispatch(
|
348
|
+
"scan_line",
|
349
|
+
params={
|
350
|
+
"picking_id": picking.id,
|
351
|
+
"barcode": self.delivery_packaging.barcode,
|
352
|
+
},
|
353
|
+
)
|
354
|
+
# back to same state
|
355
|
+
self.assertEqual(response["next_state"], "select_line")
|
356
|
+
self.assertEqual(
|
357
|
+
response["message"],
|
358
|
+
self.msg_store.confirm_put_all_goods_in_delivery_package(
|
359
|
+
self.delivery_packaging
|
360
|
+
),
|
361
|
+
)
|
362
|
+
self.assertTrue(response["data"]["select_line"]["need_confirm_pack_all"])
|
363
|
+
response = self.service.dispatch(
|
364
|
+
"scan_line",
|
365
|
+
params={
|
366
|
+
"picking_id": picking.id,
|
367
|
+
"barcode": self.delivery_packaging.barcode,
|
368
|
+
"confirm_pack_all": True,
|
369
|
+
},
|
370
|
+
)
|
371
|
+
# move to summary as all lines are done
|
372
|
+
self.assertEqual(response["next_state"], "summary")
|
373
|
+
self.assertTrue(response["message"]["body"].startswith("Goods packed into "))
|
374
|
+
self.assertNotEqual(
|
375
|
+
result_pkgs.sorted("id"),
|
376
|
+
picking.move_line_ids.result_package_id.sorted("id"),
|
377
|
+
)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright 2021 Camptocamp SA (https://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
|
3
|
+
from .test_checkout_base import CheckoutCommonCase
|
4
|
+
from .test_checkout_select_package_base import CheckoutSelectPackageMixin
|
5
|
+
|
6
|
+
|
7
|
+
class CheckoutScanLineCaseBase(CheckoutCommonCase, CheckoutSelectPackageMixin):
|
8
|
+
def _test_scan_line_ok(
|
9
|
+
self, barcode, selected_lines, related_lines=None, packing_info=""
|
10
|
+
):
|
11
|
+
"""Test /scan_line with a valid return
|
12
|
+
|
13
|
+
:param barcode: the barcode we scan
|
14
|
+
:selected_lines: expected move lines returned by the endpoint
|
15
|
+
"""
|
16
|
+
picking = selected_lines.mapped("picking_id")
|
17
|
+
response = self.service.dispatch(
|
18
|
+
"scan_line", params={"picking_id": picking.id, "barcode": barcode}
|
19
|
+
)
|
20
|
+
self._assert_selected(
|
21
|
+
response,
|
22
|
+
selected_lines,
|
23
|
+
related_lines=related_lines,
|
24
|
+
packing_info=packing_info,
|
25
|
+
)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from .test_checkout_scan_line_base import CheckoutScanLineCaseBase
|
4
|
+
|
5
|
+
# pylint: disable=missing-return
|
6
|
+
|
7
|
+
|
8
|
+
class CheckoutScanLineNoPrefillQtyCase(CheckoutScanLineCaseBase):
|
9
|
+
@classmethod
|
10
|
+
def setUpClass(cls):
|
11
|
+
super().setUpClass()
|
12
|
+
cls.menu.sudo().no_prefill_qty = True
|
13
|
+
cls.picking = cls._create_picking(
|
14
|
+
lines=[(cls.product_a, 3), (cls.product_a, 1), (cls.product_b, 10)],
|
15
|
+
confirm=False,
|
16
|
+
)
|
17
|
+
cls.picking.move_ids._action_confirm(merge=False)
|
18
|
+
cls.picking.action_confirm()
|
19
|
+
for move in cls.picking.move_ids:
|
20
|
+
cls._fill_stock_for_moves(move, in_lot=True)
|
21
|
+
cls.picking.action_assign()
|
22
|
+
cls.move_lines = cls.picking.move_line_ids
|
23
|
+
|
24
|
+
def _assert_quantity_done(self, barcode, selected_lines, qties):
|
25
|
+
picking = selected_lines.mapped("picking_id")
|
26
|
+
response = self.service.dispatch(
|
27
|
+
"scan_line", params={"picking_id": picking.id, "barcode": barcode}
|
28
|
+
)
|
29
|
+
response_lines = response["data"]["select_package"]["selected_move_lines"]
|
30
|
+
for response_line, qty in zip(response_lines, qties):
|
31
|
+
self.assertEqual(response_line["qty_done"], qty)
|
32
|
+
|
33
|
+
def test_scan_line_product_exist_in_two_lines(self):
|
34
|
+
"""Check scanning a product only increment the quantity done on one line."""
|
35
|
+
# All lines are selected because not in a package
|
36
|
+
selected_lines = self.picking.move_line_ids
|
37
|
+
response = self.service.dispatch(
|
38
|
+
"scan_line",
|
39
|
+
params={
|
40
|
+
"picking_id": self.picking.id,
|
41
|
+
"barcode": self.product_a.barcode,
|
42
|
+
},
|
43
|
+
)
|
44
|
+
self._assert_selected_qties(
|
45
|
+
response,
|
46
|
+
selected_lines,
|
47
|
+
{selected_lines[0]: 1, selected_lines[1]: 0, selected_lines[2]: 0},
|
48
|
+
)
|
49
|
+
|
50
|
+
def test_scan_line_product_no_prefill_ok(self):
|
51
|
+
picking = self._create_picking(
|
52
|
+
lines=[(self.product_a, 10), (self.product_b, 10)]
|
53
|
+
)
|
54
|
+
# do not put them in a package, we'll pack units here
|
55
|
+
self._fill_stock_for_moves(picking.move_ids)
|
56
|
+
picking.action_assign()
|
57
|
+
line_a = picking.move_line_ids.filtered(
|
58
|
+
lambda l: l.product_id == self.product_a
|
59
|
+
)
|
60
|
+
# When no_prefill_qty is enabled in the checkout menu, prefilled qty
|
61
|
+
# should be 1.0 if a product is scanned
|
62
|
+
qties = [1.0] * len(line_a)
|
63
|
+
self._assert_quantity_done(self.product_a.barcode, line_a, qties)
|
64
|
+
|
65
|
+
def test_scan_line_product_packaging_no_prefill_ok(self):
|
66
|
+
picking = self._create_picking(
|
67
|
+
lines=[(self.product_a, 10), (self.product_a, 10), (self.product_b, 10)]
|
68
|
+
)
|
69
|
+
self._fill_stock_for_moves(picking.move_ids)
|
70
|
+
picking.action_assign()
|
71
|
+
lines_a = picking.move_line_ids.filtered(
|
72
|
+
lambda l: l.product_id == self.product_a
|
73
|
+
)
|
74
|
+
# When no_prefill_qty is enabled in the checkout menu, prefilled qty
|
75
|
+
# should be the packaging qty, if a packaging is scanned
|
76
|
+
qties = [3.0] * len(lines_a)
|
77
|
+
self._assert_quantity_done(self.product_a_packaging.barcode, lines_a, qties)
|
78
|
+
|
79
|
+
def test_scan_line_product_lot_no_prefill_ok(self):
|
80
|
+
picking = self._create_picking(
|
81
|
+
lines=[(self.product_a, 1), (self.product_a, 1), (self.product_b, 1)]
|
82
|
+
)
|
83
|
+
for move in picking.move_ids:
|
84
|
+
self._fill_stock_for_moves(move, in_lot=True)
|
85
|
+
picking.action_assign()
|
86
|
+
first_line = picking.move_line_ids[0]
|
87
|
+
lot = first_line.lot_id
|
88
|
+
# When no_prefill_qty is enabled in the checkout menu, prefilled qty
|
89
|
+
# should be the packaging qty, if a packaging is scanned
|
90
|
+
qties = [1.0] * len(first_line)
|
91
|
+
self._assert_quantity_done(lot.name, first_line, qties)
|