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,135 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
3
|
+
|
4
|
+
from .test_delivery_base import DeliveryCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class DeliverySetQtyDonePackCase(DeliveryCommonCase):
|
10
|
+
"""Tests for /set_qty_done_pack"""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassBaseData(cls):
|
14
|
+
super().setUpClassBaseData()
|
15
|
+
cls.picking = picking = cls._create_picking(
|
16
|
+
lines=[
|
17
|
+
# we'll put A and B in a single package
|
18
|
+
(cls.product_a, 10),
|
19
|
+
(cls.product_b, 10),
|
20
|
+
# C alone in a package
|
21
|
+
(cls.product_c, 10),
|
22
|
+
# D in two different packages
|
23
|
+
(cls.product_d, 10),
|
24
|
+
]
|
25
|
+
)
|
26
|
+
cls.pack1_moves = picking.move_ids[:2]
|
27
|
+
cls.pack2_move = picking.move_ids[2]
|
28
|
+
cls.pack3_move = picking.move_ids[3]
|
29
|
+
cls._fill_stock_for_moves(cls.pack1_moves, in_package=True)
|
30
|
+
cls._fill_stock_for_moves(cls.pack2_move, in_package=True)
|
31
|
+
# Fill stock for D moves (two packages)
|
32
|
+
for __ in range(2):
|
33
|
+
product_d_pkg = cls.env["stock.quant.package"].create({})
|
34
|
+
cls._update_qty_in_location(
|
35
|
+
cls.pack3_move.location_id,
|
36
|
+
cls.pack3_move.product_id,
|
37
|
+
5,
|
38
|
+
package=product_d_pkg,
|
39
|
+
)
|
40
|
+
picking.action_assign()
|
41
|
+
|
42
|
+
def _test_set_qty_done_pack_ok(self, move_lines, package, qties=None):
|
43
|
+
response = self.service.dispatch(
|
44
|
+
"set_qty_done_pack",
|
45
|
+
params={"package_id": package.id, "picking_id": self.picking.id},
|
46
|
+
)
|
47
|
+
self.assert_qty_done(move_lines, qties=qties)
|
48
|
+
self.assert_response_deliver(response, picking=self.picking)
|
49
|
+
|
50
|
+
def test_set_qty_done_pack_picking_not_found(self):
|
51
|
+
move_lines = self.pack1_moves.mapped("move_line_ids")
|
52
|
+
package = move_lines.mapped("package_id")
|
53
|
+
response = self.service.dispatch(
|
54
|
+
"set_qty_done_pack", params={"package_id": package.id, "picking_id": -1}
|
55
|
+
)
|
56
|
+
self.assert_response_deliver(
|
57
|
+
response, message=self.service.msg_store.stock_picking_not_found()
|
58
|
+
)
|
59
|
+
|
60
|
+
def test_set_qty_done_pack_picking_canceled(self):
|
61
|
+
move_lines = self.pack1_moves.mapped("move_line_ids")
|
62
|
+
package = move_lines.mapped("package_id")
|
63
|
+
self.picking.action_cancel()
|
64
|
+
response = self.service.dispatch(
|
65
|
+
"set_qty_done_pack",
|
66
|
+
params={"package_id": package.id, "picking_id": self.picking.id},
|
67
|
+
)
|
68
|
+
self.assert_response_deliver(
|
69
|
+
response,
|
70
|
+
message=self.service.msg_store.stock_picking_not_available(self.picking),
|
71
|
+
)
|
72
|
+
|
73
|
+
def test_set_qty_done_pack_package_not_found(self):
|
74
|
+
response = self.service.dispatch(
|
75
|
+
"set_qty_done_pack",
|
76
|
+
params={"package_id": -1, "picking_id": self.picking.id},
|
77
|
+
)
|
78
|
+
self.assert_response_deliver(
|
79
|
+
response,
|
80
|
+
picking=self.picking,
|
81
|
+
message=self.service.msg_store.package_not_found(),
|
82
|
+
)
|
83
|
+
|
84
|
+
def test_set_qty_done_pack_multiple_product_ok(self):
|
85
|
+
move_lines = self.pack1_moves.mapped("move_line_ids")
|
86
|
+
package = move_lines.mapped("package_id")
|
87
|
+
self._test_set_qty_done_pack_ok(move_lines, package)
|
88
|
+
|
89
|
+
def test_set_qty_done_pack_one_product_ok(self):
|
90
|
+
move_lines = self.pack2_move.mapped("move_line_ids")
|
91
|
+
package = move_lines.mapped("package_id")
|
92
|
+
self._test_set_qty_done_pack_ok(move_lines, package)
|
93
|
+
|
94
|
+
def test_set_qty_done_pack_product_in_multiple_packages_ok(self):
|
95
|
+
move_lines = self.pack3_move.mapped("move_line_ids")
|
96
|
+
first_package = move_lines.mapped("package_id")[0]
|
97
|
+
self._test_set_qty_done_pack_ok(
|
98
|
+
move_lines,
|
99
|
+
# first_package done, not the second
|
100
|
+
first_package,
|
101
|
+
qties=[5, 0],
|
102
|
+
)
|
103
|
+
|
104
|
+
def test_set_qty_done_pack_picking_done(self):
|
105
|
+
pack1_move_lines = self.pack1_moves.mapped("move_line_ids")
|
106
|
+
package1 = pack1_move_lines.mapped("package_id")
|
107
|
+
pack2_move_lines = self.pack2_move.mapped("move_line_ids")
|
108
|
+
package2 = pack2_move_lines.mapped("package_id")
|
109
|
+
pack3_move_lines = self.pack3_move.mapped("move_line_ids")
|
110
|
+
packages3 = pack3_move_lines.mapped("package_id")
|
111
|
+
# process first package
|
112
|
+
self.service.dispatch(
|
113
|
+
"set_qty_done_pack",
|
114
|
+
params={"package_id": package1.id, "picking_id": self.picking.id},
|
115
|
+
)
|
116
|
+
self.assertEqual(self.picking.state, "assigned")
|
117
|
+
# process second package
|
118
|
+
self.service.dispatch(
|
119
|
+
"set_qty_done_pack",
|
120
|
+
params={"package_id": package2.id, "picking_id": self.picking.id},
|
121
|
+
)
|
122
|
+
self.assertEqual(self.picking.state, "assigned")
|
123
|
+
# process third package
|
124
|
+
self.service.dispatch(
|
125
|
+
"set_qty_done_pack",
|
126
|
+
params={"package_id": packages3[0].id, "picking_id": self.picking.id},
|
127
|
+
)
|
128
|
+
self.assertEqual(self.picking.state, "assigned")
|
129
|
+
# process last package
|
130
|
+
self.service.dispatch(
|
131
|
+
"set_qty_done_pack",
|
132
|
+
params={"package_id": packages3[1].id, "picking_id": self.picking.id},
|
133
|
+
)
|
134
|
+
# picking is done once all its moves have been processed
|
135
|
+
self.assertEqual(self.picking.state, "done")
|
@@ -0,0 +1,180 @@
|
|
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_delivery_base import DeliveryCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class DeliveryScanSublocationCase(DeliveryCommonCase):
|
10
|
+
"""Tests sublocation with delivery service."""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassBaseData(cls):
|
14
|
+
super().setUpClassBaseData()
|
15
|
+
cls.product_e.tracking = "lot"
|
16
|
+
# Picking for the top location
|
17
|
+
cls.picking = picking = cls._create_picking(
|
18
|
+
lines=[
|
19
|
+
(cls.product_d, 10), # D as raw product
|
20
|
+
(cls.product_e, 10), # E as raw product with a lot
|
21
|
+
]
|
22
|
+
)
|
23
|
+
cls.raw_move = picking.move_ids[0]
|
24
|
+
cls.raw_lot_move = picking.move_ids[1]
|
25
|
+
cls._fill_stock_for_moves(cls.raw_move)
|
26
|
+
cls._fill_stock_for_moves(cls.raw_lot_move, in_lot=True)
|
27
|
+
picking.action_assign()
|
28
|
+
cls.lot = cls.raw_lot_move.move_line_ids.lot_id
|
29
|
+
# Create a sublocation
|
30
|
+
cls.sublocation = (
|
31
|
+
cls.env["stock.location"]
|
32
|
+
.sudo()
|
33
|
+
.create(
|
34
|
+
{
|
35
|
+
"name": "Output 1",
|
36
|
+
"location_id": cls.picking_type.default_location_src_id.id,
|
37
|
+
"usage": "internal",
|
38
|
+
"barcode": "WH-OUTPUT-1",
|
39
|
+
}
|
40
|
+
)
|
41
|
+
)
|
42
|
+
# Picking for the sublocation
|
43
|
+
cls.picking_sublocation = cls._create_picking(
|
44
|
+
lines=[
|
45
|
+
(cls.product_d, 10), # D as raw product
|
46
|
+
(cls.product_e, 10), # E as raw product with a lot
|
47
|
+
],
|
48
|
+
location_id=cls.sublocation,
|
49
|
+
)
|
50
|
+
cls.raw_move_sublocation = cls.picking_sublocation.move_ids[0]
|
51
|
+
cls.raw_lot_move_sublocation = cls.picking_sublocation.move_ids[1]
|
52
|
+
cls._fill_stock_for_moves(cls.raw_move_sublocation, location=cls.sublocation)
|
53
|
+
# Use the same lot on product from both picking
|
54
|
+
cls._fill_stock_for_moves(
|
55
|
+
cls.raw_lot_move_sublocation, in_lot=cls.lot, location=cls.sublocation
|
56
|
+
)
|
57
|
+
cls.picking_sublocation.action_assign()
|
58
|
+
|
59
|
+
def test_scan_sublocation_exists(self):
|
60
|
+
"""Check scanning a sublocation sets it as sublocation."""
|
61
|
+
response = self.service.dispatch(
|
62
|
+
"scan_deliver",
|
63
|
+
params={
|
64
|
+
"barcode": self.sublocation.barcode,
|
65
|
+
"picking_id": None,
|
66
|
+
"location_id": None,
|
67
|
+
},
|
68
|
+
)
|
69
|
+
self.assert_response_deliver(
|
70
|
+
response,
|
71
|
+
picking=None,
|
72
|
+
location=self.sublocation,
|
73
|
+
message=self.service.msg_store.location_src_set_to_sublocation(
|
74
|
+
self.sublocation
|
75
|
+
),
|
76
|
+
)
|
77
|
+
|
78
|
+
def test_scan_invalid_barcode_in_sublocation(self):
|
79
|
+
response = self.service.dispatch(
|
80
|
+
"scan_deliver",
|
81
|
+
params={
|
82
|
+
"barcode": "NO VALID BARCODE",
|
83
|
+
"picking_id": None,
|
84
|
+
"location_id": self.sublocation.id,
|
85
|
+
},
|
86
|
+
)
|
87
|
+
self.assert_response_deliver(
|
88
|
+
response,
|
89
|
+
location=self.sublocation,
|
90
|
+
message=self.service.msg_store.barcode_not_found(),
|
91
|
+
)
|
92
|
+
|
93
|
+
def test_scan_barcode_in_sublocation(self):
|
94
|
+
"""Scan product barcode that exists in sublocation."""
|
95
|
+
|
96
|
+
response = self.service.dispatch(
|
97
|
+
"scan_deliver",
|
98
|
+
params={
|
99
|
+
"barcode": self.product_d.barcode,
|
100
|
+
"location_id": self.sublocation.id,
|
101
|
+
},
|
102
|
+
)
|
103
|
+
self.assert_response_deliver(
|
104
|
+
response,
|
105
|
+
location=self.sublocation,
|
106
|
+
picking=self.picking_sublocation,
|
107
|
+
)
|
108
|
+
|
109
|
+
def test_scan_product_not_in_sublocation(self):
|
110
|
+
"""Scan a product in picking type location but not in sublocation set."""
|
111
|
+
response = self.service.dispatch(
|
112
|
+
"scan_deliver",
|
113
|
+
params={
|
114
|
+
"barcode": self.product_c.barcode,
|
115
|
+
"picking_id": None,
|
116
|
+
"location_id": self.sublocation.id,
|
117
|
+
},
|
118
|
+
)
|
119
|
+
self.assert_response_deliver(
|
120
|
+
response,
|
121
|
+
location=self.sublocation,
|
122
|
+
message=self.service.msg_store.product_not_found_in_pickings(),
|
123
|
+
)
|
124
|
+
|
125
|
+
def test_scan_product_exist_in_multiple_sublocation(self):
|
126
|
+
"""Check scan of product in multiple location will ask to scan a location."""
|
127
|
+
response = self.service.dispatch(
|
128
|
+
"scan_deliver",
|
129
|
+
params={
|
130
|
+
"barcode": self.product_d.barcode,
|
131
|
+
"picking_id": None,
|
132
|
+
},
|
133
|
+
)
|
134
|
+
self.assert_response_deliver(
|
135
|
+
response,
|
136
|
+
message=self.service.msg_store.product_in_multiple_sublocation(
|
137
|
+
self.product_d
|
138
|
+
),
|
139
|
+
)
|
140
|
+
|
141
|
+
def test_list_stock_picking_sublocation(self):
|
142
|
+
"""Check manual selection filter picking in sublocation."""
|
143
|
+
response = self.service.dispatch(
|
144
|
+
"list_stock_picking", params={"location_id": self.sublocation.id}
|
145
|
+
)
|
146
|
+
self.assert_response_manual_selection(
|
147
|
+
response,
|
148
|
+
pickings=self.picking_sublocation,
|
149
|
+
)
|
150
|
+
|
151
|
+
def test_scan_lot_in_sublocation(self):
|
152
|
+
"""Scan a lot that exists in sublocation."""
|
153
|
+
lot = self.raw_lot_move_sublocation.move_line_ids.lot_id
|
154
|
+
response = self.service.dispatch(
|
155
|
+
"scan_deliver",
|
156
|
+
params={
|
157
|
+
"barcode": lot.name,
|
158
|
+
"picking_id": None,
|
159
|
+
"location_id": self.sublocation.id,
|
160
|
+
},
|
161
|
+
)
|
162
|
+
self.assert_response_deliver(
|
163
|
+
response,
|
164
|
+
location=self.sublocation,
|
165
|
+
picking=self.picking_sublocation,
|
166
|
+
)
|
167
|
+
|
168
|
+
def test_scan_lot_exist_in_multiple_sublocation(self):
|
169
|
+
"""Check scanning lot in multiple location, will ask location scan first."""
|
170
|
+
response = self.service.dispatch(
|
171
|
+
"scan_deliver",
|
172
|
+
params={
|
173
|
+
"barcode": self.lot.name,
|
174
|
+
"picking_id": None,
|
175
|
+
},
|
176
|
+
)
|
177
|
+
self.assert_response_deliver(
|
178
|
+
response,
|
179
|
+
message=self.service.msg_store.lot_in_multiple_sublocation(self.lot),
|
180
|
+
)
|
@@ -0,0 +1,136 @@
|
|
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
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class LocationContentTransferCommonCase(CommonCase):
|
10
|
+
@classmethod
|
11
|
+
def setUpClassVars(cls, *args, **kwargs):
|
12
|
+
super().setUpClassVars(*args, **kwargs)
|
13
|
+
cls.menu = cls.env.ref(
|
14
|
+
"shopfloor.shopfloor_menu_demo_location_content_transfer"
|
15
|
+
)
|
16
|
+
cls.profile = cls.env.ref("shopfloor_base.profile_demo_1")
|
17
|
+
cls.picking_type = cls.menu.picking_type_ids
|
18
|
+
cls.wh = cls.picking_type.warehouse_id
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
22
|
+
super().setUpClassBaseData(*args, **kwargs)
|
23
|
+
cls.content_loc = (
|
24
|
+
cls.env["stock.location"]
|
25
|
+
.sudo()
|
26
|
+
.create(
|
27
|
+
{
|
28
|
+
"name": "Content Location",
|
29
|
+
"barcode": "Content",
|
30
|
+
"location_id": cls.picking_type.default_location_src_id.id,
|
31
|
+
}
|
32
|
+
)
|
33
|
+
)
|
34
|
+
|
35
|
+
def setUp(self):
|
36
|
+
super().setUp()
|
37
|
+
self.service = self.get_service(
|
38
|
+
"location_content_transfer", menu=self.menu, profile=self.profile
|
39
|
+
)
|
40
|
+
self.stock_action = self.service._actions_for("stock")
|
41
|
+
|
42
|
+
def _simulate_selected_move_line(self, move_line):
|
43
|
+
"""Mark the move line as picked (as it's done into the scan_location method)"""
|
44
|
+
self.stock_action.mark_move_line_as_picked(move_line)
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def _simulate_pickings_selected(cls, pickings):
|
48
|
+
"""Create a state as if pickings has been selected
|
49
|
+
|
50
|
+
... during a Location content transfer.
|
51
|
+
|
52
|
+
It means a user scanned the location with the pickings. They are:
|
53
|
+
|
54
|
+
* assigned to the user
|
55
|
+
* the qty_done of all their move lines is set to they reserved qty
|
56
|
+
|
57
|
+
"""
|
58
|
+
pickings.user_id = cls.env.uid
|
59
|
+
for line in pickings.mapped("move_line_ids"):
|
60
|
+
line.qty_done = line.reserved_uom_qty
|
61
|
+
|
62
|
+
def assert_response_start(self, response, message=None, popup=None):
|
63
|
+
self.assert_response(
|
64
|
+
response, next_state="scan_location", message=message, popup=popup
|
65
|
+
)
|
66
|
+
|
67
|
+
def _assert_response_scan_destination_all(
|
68
|
+
self, state, response, pickings, message=None, confirmation_required=False
|
69
|
+
):
|
70
|
+
# this code is repeated from the implementation, not great, but we
|
71
|
+
# mostly want to ensure the selection of pickings is right, and the
|
72
|
+
# data methods have their own tests
|
73
|
+
move_lines = pickings.move_line_ids
|
74
|
+
lines = move_lines.filtered(lambda line: not line.package_level_id)
|
75
|
+
package_levels = pickings.package_level_ids
|
76
|
+
location = move_lines.location_id
|
77
|
+
self.assert_response(
|
78
|
+
response,
|
79
|
+
next_state=state,
|
80
|
+
data={
|
81
|
+
"move_lines": self.data.move_lines(lines),
|
82
|
+
"package_levels": self.data.package_levels(package_levels),
|
83
|
+
"location": self.data.location(location),
|
84
|
+
"confirmation_required": confirmation_required,
|
85
|
+
},
|
86
|
+
message=message,
|
87
|
+
)
|
88
|
+
|
89
|
+
def assert_response_scan_destination_all(
|
90
|
+
self, response, pickings, message=None, confirmation_required=False
|
91
|
+
):
|
92
|
+
self._assert_response_scan_destination_all(
|
93
|
+
"scan_destination_all",
|
94
|
+
response,
|
95
|
+
pickings,
|
96
|
+
message=message,
|
97
|
+
confirmation_required=confirmation_required,
|
98
|
+
)
|
99
|
+
|
100
|
+
def assert_response_start_single(
|
101
|
+
self, response, pickings, message=None, popup=None
|
102
|
+
):
|
103
|
+
sorter = self.service._actions_for("location_content_transfer.sorter")
|
104
|
+
sorter.feed_pickings(pickings)
|
105
|
+
location = pickings.mapped("location_id")
|
106
|
+
self.assert_response(
|
107
|
+
response,
|
108
|
+
next_state="start_single",
|
109
|
+
data=self.service._data_content_line_for_location(location, next(sorter)),
|
110
|
+
message=message,
|
111
|
+
popup=popup,
|
112
|
+
)
|
113
|
+
|
114
|
+
def _assert_response_scan_destination(
|
115
|
+
self, state, response, next_content, message=None, confirmation_required=False
|
116
|
+
):
|
117
|
+
location = next_content.location_id
|
118
|
+
data = self.service._data_content_line_for_location(location, next_content)
|
119
|
+
data["confirmation_required"] = confirmation_required
|
120
|
+
self.assert_response(
|
121
|
+
response,
|
122
|
+
next_state=state,
|
123
|
+
data=data,
|
124
|
+
message=message,
|
125
|
+
)
|
126
|
+
|
127
|
+
def assert_response_scan_destination(
|
128
|
+
self, response, next_content, message=None, confirmation_required=False
|
129
|
+
):
|
130
|
+
self._assert_response_scan_destination(
|
131
|
+
"scan_destination",
|
132
|
+
response,
|
133
|
+
next_content,
|
134
|
+
message=message,
|
135
|
+
confirmation_required=confirmation_required,
|
136
|
+
)
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Copyright 2022 Camptocamp SA
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
3
|
+
|
4
|
+
from .test_location_content_transfer_base import LocationContentTransferCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class TestLocationContentTransferGetWork(LocationContentTransferCommonCase):
|
10
|
+
"""Tests for getting work
|
11
|
+
|
12
|
+
Endpoints:
|
13
|
+
|
14
|
+
* /find_work
|
15
|
+
* /cancel_work
|
16
|
+
"""
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def setUpClassBaseData(cls):
|
20
|
+
super().setUpClassBaseData()
|
21
|
+
cls.menu.sudo().allow_get_work = True
|
22
|
+
cls.pickings = cls.env["stock.picking"].search(
|
23
|
+
[("location_id", "=", cls.stock_location.id)]
|
24
|
+
)
|
25
|
+
cls.move_lines = cls.pickings.move_line_ids.filtered(
|
26
|
+
lambda line: line.qty_done == 0
|
27
|
+
and line.state in ("assigned", "partially_available")
|
28
|
+
and not line.shopfloor_user_id
|
29
|
+
)
|
30
|
+
products = cls.product_a + cls.product_b + cls.product_c + cls.product_d
|
31
|
+
for product in products:
|
32
|
+
cls.env["stock.putaway.rule"].sudo().create(
|
33
|
+
{
|
34
|
+
"product_id": product.id,
|
35
|
+
"location_in_id": cls.stock_location.id,
|
36
|
+
"location_out_id": cls.shelf1.id,
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
cls.picking1 = picking1 = cls._create_picking(
|
41
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10)]
|
42
|
+
)
|
43
|
+
cls.picking2 = picking2 = cls._create_picking(
|
44
|
+
lines=[(cls.product_c, 10), (cls.product_d, 10)]
|
45
|
+
)
|
46
|
+
cls.pickings = picking1 | picking2
|
47
|
+
cls.content_loc2 = (
|
48
|
+
cls.env["stock.location"]
|
49
|
+
.sudo()
|
50
|
+
.create(
|
51
|
+
{
|
52
|
+
"name": "Content Location 2",
|
53
|
+
"barcode": "Content2",
|
54
|
+
"location_id": cls.picking_type.default_location_src_id.id,
|
55
|
+
}
|
56
|
+
)
|
57
|
+
)
|
58
|
+
cls._fill_stock_for_moves(
|
59
|
+
picking1.move_ids, in_package=True, location=cls.content_loc
|
60
|
+
)
|
61
|
+
cls._fill_stock_for_moves(picking2.move_ids[0], location=cls.content_loc2)
|
62
|
+
cls._fill_stock_for_moves(picking2.move_ids[1], location=cls.content_loc)
|
63
|
+
cls.pickings.action_assign()
|
64
|
+
|
65
|
+
def _get_location_lines(self, location):
|
66
|
+
return self.env["stock.move.line"].search([("location_id", "=", location.id)])
|
67
|
+
|
68
|
+
def test_get_work(self):
|
69
|
+
"""Check the first state is get_work when the option is enabled."""
|
70
|
+
response = self.service.dispatch("start_or_recover", params={})
|
71
|
+
self.assert_response(
|
72
|
+
response,
|
73
|
+
next_state="get_work",
|
74
|
+
data={},
|
75
|
+
)
|
76
|
+
|
77
|
+
def test_find_work_no_work_found(self):
|
78
|
+
"""Check the user asked for work but none is found."""
|
79
|
+
# Cancel all moves related to the location we work from
|
80
|
+
self.pickings.move_ids.filtered(lambda r: r.state != "done")._action_cancel()
|
81
|
+
response = self.service.dispatch("find_work", params={})
|
82
|
+
self.assert_response(
|
83
|
+
response,
|
84
|
+
next_state="get_work",
|
85
|
+
data={},
|
86
|
+
message=self.service.msg_store.no_work_found(),
|
87
|
+
)
|
88
|
+
|
89
|
+
def test_find_work_work_found(self):
|
90
|
+
"""Check the user is offered a location to work from."""
|
91
|
+
next_location = self.service._find_location_to_work_from()
|
92
|
+
response = self.service.dispatch("find_work", params={})
|
93
|
+
self.assert_response(
|
94
|
+
response,
|
95
|
+
next_state="scan_location",
|
96
|
+
data={
|
97
|
+
"location": self.data.location(next_location),
|
98
|
+
},
|
99
|
+
)
|
100
|
+
lines = self._get_location_lines(next_location)
|
101
|
+
self.assertEqual(lines.shopfloor_user_id, self.env.user)
|
102
|
+
# Confirm the location
|
103
|
+
response = self.service.dispatch(
|
104
|
+
"scan_location", params={"barcode": next_location.name}
|
105
|
+
)
|
106
|
+
self.assertEqual(response["next_state"], "scan_destination_all")
|
107
|
+
|
108
|
+
def test_cancel_work(self):
|
109
|
+
next_location = self.service._find_location_to_work_from()
|
110
|
+
stock = self.service._actions_for("stock")
|
111
|
+
location_lines = self._get_location_lines(next_location)
|
112
|
+
stock.mark_move_line_as_picked(location_lines, quantity=0)
|
113
|
+
location_lines = self._get_location_lines(next_location)
|
114
|
+
self.assertEqual(location_lines.shopfloor_user_id, self.env.user)
|
115
|
+
response = self.service.dispatch(
|
116
|
+
"cancel_work", params={"location_id": next_location.id}
|
117
|
+
)
|
118
|
+
self.assert_response(
|
119
|
+
response,
|
120
|
+
next_state="get_work",
|
121
|
+
data={},
|
122
|
+
message={},
|
123
|
+
)
|
124
|
+
lines = self._get_location_lines(next_location)
|
125
|
+
self.assertFalse(lines.shopfloor_user_id)
|