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,130 @@
|
|
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
|
+
from .test_checkout_select_package_base import CheckoutSelectPackageMixin
|
5
|
+
|
6
|
+
|
7
|
+
# pylint: disable=missing-return
|
8
|
+
class CheckoutSelectLineCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
|
9
|
+
@classmethod
|
10
|
+
def setUpClassBaseData(cls):
|
11
|
+
super().setUpClassBaseData()
|
12
|
+
picking = cls._create_picking(
|
13
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10), (cls.product_c, 10)]
|
14
|
+
)
|
15
|
+
cls.moves_pack = picking.move_ids[:2]
|
16
|
+
cls.move_single = picking.move_ids[2:]
|
17
|
+
cls._fill_stock_for_moves(cls.moves_pack, in_package=True)
|
18
|
+
cls._fill_stock_for_moves(cls.move_single)
|
19
|
+
picking.action_assign()
|
20
|
+
cls.picking = picking
|
21
|
+
|
22
|
+
def test_select_line_package_ok(self):
|
23
|
+
selected_lines = self.moves_pack.move_line_ids
|
24
|
+
# we want to check that when we give the package id, we get
|
25
|
+
# all its move lines
|
26
|
+
response = self.service.dispatch(
|
27
|
+
"select_line",
|
28
|
+
params={
|
29
|
+
"picking_id": self.picking.id,
|
30
|
+
"package_id": selected_lines.package_id.id,
|
31
|
+
},
|
32
|
+
)
|
33
|
+
self._assert_selected(response, selected_lines)
|
34
|
+
|
35
|
+
def test_select_line_no_package_disabled(self):
|
36
|
+
selected_lines = self.moves_pack.move_line_ids
|
37
|
+
self.service.work.options = {"checkout__disable_no_package": True}
|
38
|
+
response = self.service.dispatch(
|
39
|
+
"select_line",
|
40
|
+
params={
|
41
|
+
"picking_id": self.picking.id,
|
42
|
+
"package_id": selected_lines.package_id.id,
|
43
|
+
},
|
44
|
+
)
|
45
|
+
self._assert_selected(response, selected_lines, no_package_enabled=False)
|
46
|
+
|
47
|
+
def test_select_line_move_line_package_ok(self):
|
48
|
+
selected_lines = self.moves_pack.move_line_ids
|
49
|
+
# When we select a single line but the line is part of a package,
|
50
|
+
# we select all the lines. Note: not really supposed to happen as
|
51
|
+
# the client application should use send a package id when there is
|
52
|
+
# a package and use the move line id only for lines without package
|
53
|
+
response = self.service.dispatch(
|
54
|
+
"select_line",
|
55
|
+
params={
|
56
|
+
"picking_id": self.picking.id,
|
57
|
+
"move_line_id": selected_lines[0].id,
|
58
|
+
},
|
59
|
+
)
|
60
|
+
self._assert_selected(response, selected_lines)
|
61
|
+
|
62
|
+
def test_select_line_move_line_ok(self):
|
63
|
+
selected_lines = self.move_single.move_line_ids
|
64
|
+
response = self.service.dispatch(
|
65
|
+
"select_line",
|
66
|
+
params={
|
67
|
+
"picking_id": self.picking.id,
|
68
|
+
"move_line_id": selected_lines[0].id,
|
69
|
+
},
|
70
|
+
)
|
71
|
+
self._assert_selected(response, selected_lines)
|
72
|
+
|
73
|
+
def _test_select_line_error(self, params, message):
|
74
|
+
"""Test errors for /select_line
|
75
|
+
|
76
|
+
:param params: params sent to /select_line
|
77
|
+
:param message: the dict of expected error message
|
78
|
+
"""
|
79
|
+
response = self.service.dispatch("select_line", params=params)
|
80
|
+
self.assert_response(
|
81
|
+
response,
|
82
|
+
next_state="select_line",
|
83
|
+
data=self._data_for_select_line(self.picking),
|
84
|
+
message=message,
|
85
|
+
)
|
86
|
+
|
87
|
+
def test_select_line_package_error_not_found(self):
|
88
|
+
selected_lines = self.move_single.move_line_ids
|
89
|
+
selected_lines.unlink()
|
90
|
+
self._test_select_line_error(
|
91
|
+
{"picking_id": self.picking.id, "package_id": selected_lines[0].id},
|
92
|
+
{
|
93
|
+
"message_type": "error",
|
94
|
+
"body": "The record you were working on does not exist anymore.",
|
95
|
+
},
|
96
|
+
)
|
97
|
+
|
98
|
+
def test_select_line_move_line_error_not_found(self):
|
99
|
+
selected_lines = self.move_single.move_line_ids
|
100
|
+
selected_lines.unlink()
|
101
|
+
self._test_select_line_error(
|
102
|
+
{"picking_id": self.picking.id, "move_line_id": selected_lines[0].id},
|
103
|
+
{
|
104
|
+
"message_type": "error",
|
105
|
+
"body": "The record you were working on does not exist anymore.",
|
106
|
+
},
|
107
|
+
)
|
108
|
+
|
109
|
+
def test_select_line_all_lines_done(self):
|
110
|
+
# set all lines as done
|
111
|
+
self.picking.move_line_ids.write(
|
112
|
+
{"qty_done": 10.0, "shopfloor_checkout_done": True}
|
113
|
+
)
|
114
|
+
response = self.service.dispatch(
|
115
|
+
"select_line",
|
116
|
+
params={
|
117
|
+
"picking_id": self.picking.id,
|
118
|
+
# doesn't matter as all lines are done, we should be
|
119
|
+
# redirected to the summary
|
120
|
+
"package_id": self.picking.move_line_ids[0].package_id.id,
|
121
|
+
},
|
122
|
+
)
|
123
|
+
self.assert_response(
|
124
|
+
response,
|
125
|
+
next_state="summary",
|
126
|
+
data={
|
127
|
+
"picking": self._stock_picking_data(self.picking, done=True),
|
128
|
+
"all_processed": True,
|
129
|
+
},
|
130
|
+
)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
|
5
|
+
class CheckoutSelectPackageMixin:
|
6
|
+
def _assert_selected_response(
|
7
|
+
self,
|
8
|
+
response,
|
9
|
+
selected_lines,
|
10
|
+
message=None,
|
11
|
+
packing_info="",
|
12
|
+
no_package_enabled=True,
|
13
|
+
):
|
14
|
+
picking = selected_lines.mapped("picking_id")
|
15
|
+
self.assert_response(
|
16
|
+
response,
|
17
|
+
next_state="select_package",
|
18
|
+
data={
|
19
|
+
"selected_move_lines": [
|
20
|
+
self._move_line_data(ml) for ml in selected_lines.sorted()
|
21
|
+
],
|
22
|
+
"picking": self._picking_summary_data(picking),
|
23
|
+
"packing_info": packing_info,
|
24
|
+
"no_package_enabled": no_package_enabled,
|
25
|
+
},
|
26
|
+
message=message,
|
27
|
+
)
|
28
|
+
|
29
|
+
def _assert_selected_qties(
|
30
|
+
self,
|
31
|
+
response,
|
32
|
+
selected_lines,
|
33
|
+
lines_quantities,
|
34
|
+
message=None,
|
35
|
+
packing_info="",
|
36
|
+
):
|
37
|
+
picking = selected_lines.mapped("picking_id")
|
38
|
+
deselected_lines = picking.move_line_ids - selected_lines
|
39
|
+
self.assertEqual(
|
40
|
+
sorted(selected_lines.ids), sorted([line.id for line in lines_quantities])
|
41
|
+
)
|
42
|
+
for line, quantity in lines_quantities.items():
|
43
|
+
self.assertEqual(line.qty_done, quantity)
|
44
|
+
for line in deselected_lines:
|
45
|
+
self.assertEqual(line.qty_done, 0, "Lines deselected must have no qty done")
|
46
|
+
self._assert_selected_response(
|
47
|
+
response, selected_lines, message=message, packing_info=packing_info
|
48
|
+
)
|
49
|
+
|
50
|
+
def _assert_selected(
|
51
|
+
self, response, selected_lines, related_lines=None, message=None, **kw
|
52
|
+
):
|
53
|
+
related_lines = related_lines or self.env["stock.move.line"]
|
54
|
+
picking = selected_lines.mapped("picking_id")
|
55
|
+
unselected_lines = picking.move_line_ids - selected_lines
|
56
|
+
for line in selected_lines - related_lines:
|
57
|
+
self.assertEqual(
|
58
|
+
line.qty_done,
|
59
|
+
line.reserved_uom_qty,
|
60
|
+
"Scanned lines must have their qty done set to the reserved quantity",
|
61
|
+
)
|
62
|
+
for line in unselected_lines + related_lines:
|
63
|
+
self.assertEqual(line.qty_done, 0)
|
64
|
+
self._assert_selected_response(response, selected_lines, message=message, **kw)
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .test_checkout_base import CheckoutCommonCase
|
5
|
+
from .test_checkout_select_package_base import CheckoutSelectPackageMixin
|
6
|
+
|
7
|
+
# pylint: disable=missing-return
|
8
|
+
|
9
|
+
|
10
|
+
class CheckoutSetQtyCommonCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
|
11
|
+
@classmethod
|
12
|
+
def setUpClassBaseData(cls):
|
13
|
+
super().setUpClassBaseData()
|
14
|
+
picking = cls._create_picking(
|
15
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10), (cls.product_c, 10)]
|
16
|
+
)
|
17
|
+
cls.moves_pack1 = picking.move_ids[:2]
|
18
|
+
cls.moves_pack2 = picking.move_ids[2:]
|
19
|
+
cls._fill_stock_for_moves(cls.moves_pack1, in_package=True)
|
20
|
+
cls._fill_stock_for_moves(cls.moves_pack2, in_package=True)
|
21
|
+
picking.action_assign()
|
22
|
+
cls.picking = picking
|
23
|
+
|
24
|
+
def setUp(self):
|
25
|
+
super().setUp()
|
26
|
+
# we assume we have called /select_line on pack one, so by default, we
|
27
|
+
# expect the lines for product a and b to have their qty_done set to
|
28
|
+
# their reserved_uom_qty at the start of the tests
|
29
|
+
self.selected_lines = self.moves_pack1.move_line_ids
|
30
|
+
self.deselected_lines = self.moves_pack2.move_line_ids
|
31
|
+
self.service._select_lines(self.selected_lines)
|
32
|
+
self.assertTrue(
|
33
|
+
all(line.qty_done == line.reserved_uom_qty for line in self.selected_lines)
|
34
|
+
)
|
35
|
+
self.assertTrue(all(line.qty_done == 0 for line in self.deselected_lines))
|
36
|
+
|
37
|
+
|
38
|
+
class CheckoutResetLineQtyCase(CheckoutSetQtyCommonCase):
|
39
|
+
def test_reset_line_qty_ok(self):
|
40
|
+
selected_lines = self.moves_pack1.move_line_ids
|
41
|
+
line_to_reset = selected_lines[0]
|
42
|
+
line_with_qty = selected_lines[1]
|
43
|
+
# we want to check that when we give the package id, we get
|
44
|
+
# all its move lines
|
45
|
+
response = self.service.dispatch(
|
46
|
+
"reset_line_qty",
|
47
|
+
params={
|
48
|
+
"picking_id": self.picking.id,
|
49
|
+
"selected_line_ids": selected_lines.ids,
|
50
|
+
"move_line_id": line_to_reset.id,
|
51
|
+
},
|
52
|
+
)
|
53
|
+
self._assert_selected_qties(
|
54
|
+
response,
|
55
|
+
selected_lines,
|
56
|
+
{line_to_reset: 0, line_with_qty: line_with_qty.reserved_uom_qty},
|
57
|
+
)
|
58
|
+
|
59
|
+
def test_reset_line_qty_not_found(self):
|
60
|
+
selected_lines = self.moves_pack1.move_line_ids
|
61
|
+
response = self.service.dispatch(
|
62
|
+
"reset_line_qty",
|
63
|
+
params={
|
64
|
+
"picking_id": self.picking.id,
|
65
|
+
"selected_line_ids": selected_lines.ids,
|
66
|
+
"move_line_id": 0,
|
67
|
+
},
|
68
|
+
)
|
69
|
+
# if the move line is not found, ignore and return to the
|
70
|
+
# screen
|
71
|
+
self._assert_selected_qties(
|
72
|
+
response,
|
73
|
+
selected_lines,
|
74
|
+
{line: line.reserved_uom_qty for line in selected_lines},
|
75
|
+
message={
|
76
|
+
"body": "The record you were working on does not exist anymore.",
|
77
|
+
"message_type": "error",
|
78
|
+
},
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
class CheckoutSetLineQtyCase(CheckoutSetQtyCommonCase):
|
83
|
+
def test_set_line_qty_ok(self):
|
84
|
+
selected_lines = self.moves_pack1.move_line_ids
|
85
|
+
# do as if the user removed the qties of the 2 selected lines
|
86
|
+
selected_lines.qty_done = 0
|
87
|
+
line_to_set = selected_lines[0]
|
88
|
+
line_no_qty = selected_lines[1]
|
89
|
+
# we want to check that when we give the package id, we get
|
90
|
+
# all its move lines
|
91
|
+
response = self.service.dispatch(
|
92
|
+
"set_line_qty",
|
93
|
+
params={
|
94
|
+
"picking_id": self.picking.id,
|
95
|
+
"selected_line_ids": selected_lines.ids,
|
96
|
+
"move_line_id": line_to_set.id,
|
97
|
+
},
|
98
|
+
)
|
99
|
+
self.assertEqual(line_to_set.qty_done, line_to_set.reserved_uom_qty)
|
100
|
+
self.assertEqual(line_no_qty.qty_done, 0)
|
101
|
+
self._assert_selected_qties(
|
102
|
+
response,
|
103
|
+
selected_lines,
|
104
|
+
{line_to_set: line_to_set.reserved_uom_qty, line_no_qty: 0},
|
105
|
+
)
|
106
|
+
|
107
|
+
def test_set_line_qty_not_found(self):
|
108
|
+
selected_lines = self.moves_pack1.move_line_ids
|
109
|
+
response = self.service.dispatch(
|
110
|
+
"set_line_qty",
|
111
|
+
params={
|
112
|
+
"picking_id": self.picking.id,
|
113
|
+
"selected_line_ids": selected_lines.ids,
|
114
|
+
"move_line_id": 0,
|
115
|
+
},
|
116
|
+
)
|
117
|
+
# if the move line is not found, ignore and return to the
|
118
|
+
# screen
|
119
|
+
self._assert_selected_qties(
|
120
|
+
response,
|
121
|
+
selected_lines,
|
122
|
+
{line: line.reserved_uom_qty for line in selected_lines},
|
123
|
+
message={
|
124
|
+
"body": "The record you were working on does not exist anymore.",
|
125
|
+
"message_type": "error",
|
126
|
+
},
|
127
|
+
)
|
128
|
+
|
129
|
+
|
130
|
+
class CheckoutSetCustomQtyCase(CheckoutSetQtyCommonCase):
|
131
|
+
def test_set_custom_qty_ok(self):
|
132
|
+
selected_lines = self.moves_pack1.move_line_ids
|
133
|
+
line_to_change = selected_lines[0]
|
134
|
+
line_keep_qty = selected_lines[1]
|
135
|
+
# Process full qty
|
136
|
+
new_qty = line_to_change.reserved_uom_qty
|
137
|
+
# we want to check that when we give the package id, we get
|
138
|
+
# all its move lines
|
139
|
+
response = self.service.dispatch(
|
140
|
+
"set_custom_qty",
|
141
|
+
params={
|
142
|
+
"picking_id": self.picking.id,
|
143
|
+
"selected_line_ids": selected_lines.ids,
|
144
|
+
"move_line_id": line_to_change.id,
|
145
|
+
"qty_done": new_qty,
|
146
|
+
},
|
147
|
+
)
|
148
|
+
self.assertEqual(line_to_change.qty_done, new_qty)
|
149
|
+
self.assertEqual(line_keep_qty.qty_done, line_keep_qty.reserved_uom_qty)
|
150
|
+
self._assert_selected_qties(
|
151
|
+
response,
|
152
|
+
selected_lines,
|
153
|
+
{line_to_change: new_qty, line_keep_qty: line_keep_qty.reserved_uom_qty},
|
154
|
+
)
|
155
|
+
|
156
|
+
def test_set_custom_qty_not_found(self):
|
157
|
+
selected_lines = self.moves_pack1.move_line_ids
|
158
|
+
response = self.service.dispatch(
|
159
|
+
"set_custom_qty",
|
160
|
+
params={
|
161
|
+
"picking_id": self.picking.id,
|
162
|
+
"selected_line_ids": selected_lines.ids,
|
163
|
+
"move_line_id": 0,
|
164
|
+
"qty_done": 3,
|
165
|
+
},
|
166
|
+
)
|
167
|
+
# if the move line is not found, ignore and return to the
|
168
|
+
# screen
|
169
|
+
self._assert_selected_qties(
|
170
|
+
response,
|
171
|
+
selected_lines,
|
172
|
+
{line: line.reserved_uom_qty for line in selected_lines},
|
173
|
+
message={
|
174
|
+
"body": "The record you were working on does not exist anymore.",
|
175
|
+
"message_type": "error",
|
176
|
+
},
|
177
|
+
)
|
178
|
+
|
179
|
+
def test_set_custom_qty_above(self):
|
180
|
+
selected_lines = self.moves_pack1.move_line_ids
|
181
|
+
line1 = selected_lines[0]
|
182
|
+
# modify so we can check that a too high quantity set the max
|
183
|
+
line1.qty_done = 1
|
184
|
+
line2 = selected_lines[1]
|
185
|
+
response = self.service.dispatch(
|
186
|
+
"set_custom_qty",
|
187
|
+
params={
|
188
|
+
"picking_id": self.picking.id,
|
189
|
+
"selected_line_ids": selected_lines.ids,
|
190
|
+
"move_line_id": line1.id,
|
191
|
+
"qty_done": line1.reserved_uom_qty + 1,
|
192
|
+
},
|
193
|
+
)
|
194
|
+
self._assert_selected_qties(
|
195
|
+
response,
|
196
|
+
selected_lines,
|
197
|
+
{line1: line1.reserved_uom_qty + 1, line2: line2.reserved_uom_qty},
|
198
|
+
message={
|
199
|
+
"body": "Please note that the scanned quantity "
|
200
|
+
"is higher than the maximum allowed.",
|
201
|
+
"message_type": "warning",
|
202
|
+
},
|
203
|
+
)
|
204
|
+
|
205
|
+
def test_set_custom_qty_negative(self):
|
206
|
+
selected_lines = self.moves_pack1.move_line_ids
|
207
|
+
line1 = selected_lines[0]
|
208
|
+
line2 = selected_lines[1]
|
209
|
+
response = self.service.dispatch(
|
210
|
+
"set_custom_qty",
|
211
|
+
params={
|
212
|
+
"picking_id": self.picking.id,
|
213
|
+
"selected_line_ids": selected_lines.ids,
|
214
|
+
"move_line_id": line1.id,
|
215
|
+
"qty_done": -1,
|
216
|
+
},
|
217
|
+
)
|
218
|
+
self._assert_selected_qties(
|
219
|
+
response,
|
220
|
+
selected_lines,
|
221
|
+
{line1: line1.reserved_uom_qty, line2: line2.reserved_uom_qty},
|
222
|
+
message={
|
223
|
+
"body": "Negative quantity not allowed.",
|
224
|
+
"message_type": "error",
|
225
|
+
},
|
226
|
+
)
|
227
|
+
|
228
|
+
def test_set_custom_qty_partial(self):
|
229
|
+
selected_lines = self.moves_pack1.move_line_ids
|
230
|
+
line_to_change = selected_lines[0]
|
231
|
+
line_keep_qty = selected_lines[1]
|
232
|
+
# split 1 qty
|
233
|
+
new_qty = line_to_change.reserved_uom_qty - 1
|
234
|
+
response = self.service.dispatch(
|
235
|
+
"set_custom_qty",
|
236
|
+
params={
|
237
|
+
"picking_id": self.picking.id,
|
238
|
+
"selected_line_ids": selected_lines.ids,
|
239
|
+
"move_line_id": line_to_change.id,
|
240
|
+
"qty_done": new_qty,
|
241
|
+
},
|
242
|
+
)
|
243
|
+
self.assertEqual(line_to_change.qty_done, new_qty)
|
244
|
+
self.assertEqual(line_keep_qty.qty_done, line_keep_qty.reserved_uom_qty)
|
245
|
+
new_lines = [
|
246
|
+
x for x in self.moves_pack1.move_line_ids if x not in selected_lines
|
247
|
+
]
|
248
|
+
# Lines are not being split anymore
|
249
|
+
self.assertFalse(new_lines)
|
250
|
+
self._assert_selected_qties(
|
251
|
+
response,
|
252
|
+
self.moves_pack1.move_line_ids,
|
253
|
+
{
|
254
|
+
line_to_change: new_qty,
|
255
|
+
line_keep_qty: line_keep_qty.reserved_uom_qty,
|
256
|
+
},
|
257
|
+
)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .test_checkout_base import CheckoutCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class CheckoutSummaryCase(CheckoutCommonCase):
|
10
|
+
@classmethod
|
11
|
+
def setUpClassBaseData(cls):
|
12
|
+
super().setUpClassBaseData()
|
13
|
+
cls.picking = cls._create_picking(
|
14
|
+
lines=[
|
15
|
+
(cls.product_a, 10),
|
16
|
+
(cls.product_b, 10),
|
17
|
+
(cls.product_c, 10),
|
18
|
+
(cls.product_d, 10),
|
19
|
+
]
|
20
|
+
)
|
21
|
+
|
22
|
+
def test_summary_picking_not_ready(self):
|
23
|
+
response = self.service.dispatch(
|
24
|
+
"summary", params={"picking_id": self.picking.id}
|
25
|
+
)
|
26
|
+
self.assert_response(
|
27
|
+
response,
|
28
|
+
next_state="select_document",
|
29
|
+
data={},
|
30
|
+
message=self.service.msg_store.stock_picking_not_available(self.picking),
|
31
|
+
)
|
32
|
+
|
33
|
+
def test_summary_not_fully_processed(self):
|
34
|
+
self._fill_stock_for_moves(self.picking.move_ids, in_package=True)
|
35
|
+
self.picking.action_assign()
|
36
|
+
# satisfy only few lines
|
37
|
+
for ml in self.picking.move_line_ids[:2]:
|
38
|
+
ml.qty_done = ml.reserved_uom_qty
|
39
|
+
ml.shopfloor_checkout_done = True
|
40
|
+
response = self.service.dispatch(
|
41
|
+
"summary", params={"picking_id": self.picking.id}
|
42
|
+
)
|
43
|
+
self.assert_response(
|
44
|
+
response,
|
45
|
+
next_state="summary",
|
46
|
+
data={
|
47
|
+
"picking": self._stock_picking_data(self.picking, done=True),
|
48
|
+
"all_processed": False,
|
49
|
+
},
|
50
|
+
)
|
51
|
+
|
52
|
+
def test_summary_fully_processed(self):
|
53
|
+
self._fill_stock_for_moves(self.picking.move_ids, in_package=True)
|
54
|
+
self.picking.action_assign()
|
55
|
+
# satisfy only all lines
|
56
|
+
for ml in self.picking.move_line_ids:
|
57
|
+
ml.qty_done = ml.reserved_uom_qty
|
58
|
+
ml.shopfloor_checkout_done = True
|
59
|
+
response = self.service.dispatch(
|
60
|
+
"summary", params={"picking_id": self.picking.id}
|
61
|
+
)
|
62
|
+
self.assert_response(
|
63
|
+
response,
|
64
|
+
next_state="summary",
|
65
|
+
data={
|
66
|
+
"picking": self._stock_picking_data(self.picking, done=True),
|
67
|
+
"all_processed": True,
|
68
|
+
},
|
69
|
+
)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .common import CommonCase, PickingBatchMixin
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class ClusterPickingCommonCase(CommonCase, PickingBatchMixin):
|
10
|
+
@classmethod
|
11
|
+
def setUpClassVars(cls, *args, **kwargs):
|
12
|
+
super().setUpClassVars(*args, **kwargs)
|
13
|
+
cls.menu = cls.env.ref("shopfloor.shopfloor_menu_demo_cluster_picking")
|
14
|
+
cls.profile = cls.env.ref("shopfloor_base.profile_demo_1")
|
15
|
+
cls.picking_type = cls.menu.picking_type_ids
|
16
|
+
cls.wh = cls.picking_type.warehouse_id
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
20
|
+
super().setUpClassBaseData(*args, **kwargs)
|
21
|
+
cls.wh.sudo().delivery_steps = "pick_pack_ship"
|
22
|
+
|
23
|
+
def setUp(self):
|
24
|
+
super().setUp()
|
25
|
+
self.service = self.get_service(
|
26
|
+
"cluster_picking", menu=self.menu, profile=self.profile
|
27
|
+
)
|
28
|
+
|
29
|
+
def _line_data(self, move_line, qty=None, package_dest=False, qty_done=None, **kw):
|
30
|
+
picking = move_line.picking_id
|
31
|
+
# A package exists on the move line, because the quant created
|
32
|
+
# by ``_simulate_batch_selected`` has a package.
|
33
|
+
data = self.data.move_line(move_line)
|
34
|
+
if not package_dest:
|
35
|
+
data["package_dest"] = None
|
36
|
+
if qty:
|
37
|
+
data["quantity"] = qty
|
38
|
+
if qty_done:
|
39
|
+
data["qty_done"] = qty_done
|
40
|
+
data.update(
|
41
|
+
{
|
42
|
+
"batch": self.data.picking_batch(picking.batch_id),
|
43
|
+
"picking": self.data.picking(picking),
|
44
|
+
"scan_location_or_pack_first": False,
|
45
|
+
}
|
46
|
+
)
|
47
|
+
data.update(kw)
|
48
|
+
return data
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def _set_dest_package_and_done(cls, move_lines, dest_package):
|
52
|
+
"""Simulate what would have been done in the previous steps"""
|
53
|
+
for line in move_lines:
|
54
|
+
line.write(
|
55
|
+
{
|
56
|
+
"qty_done": line.reserved_uom_qty,
|
57
|
+
"result_package_id": dest_package.id,
|
58
|
+
}
|
59
|
+
)
|
60
|
+
|
61
|
+
def _data_for_batch(self, batch, location, pack=None):
|
62
|
+
data = self.data.picking_batch(batch)
|
63
|
+
data["location_dest"] = self.data.location(location)
|
64
|
+
if pack:
|
65
|
+
data["package"] = self.data.package(pack)
|
66
|
+
return data
|
67
|
+
|
68
|
+
|
69
|
+
class ClusterPickingLineCommonCase(ClusterPickingCommonCase):
|
70
|
+
@classmethod
|
71
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
72
|
+
super().setUpClassBaseData(*args, **kwargs)
|
73
|
+
# quants already existing are from demo data
|
74
|
+
cls.env["stock.quant"].sudo().search(
|
75
|
+
[("location_id", "=", cls.stock_location.id)]
|
76
|
+
).unlink()
|
77
|
+
cls.batch = cls._create_picking_batch(
|
78
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=1)]]
|
79
|
+
)
|
80
|
+
|
81
|
+
def _line_data(self, move_line, qty=1.0, **kw):
|
82
|
+
# just force qty to 1.0
|
83
|
+
return super()._line_data(move_line, qty=qty, **kw)
|