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,70 @@
|
|
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_cluster_picking_base import ClusterPickingLineCommonCase
|
5
|
+
|
6
|
+
|
7
|
+
class ClusterPickingScanLineNoPrefillQtyCase(ClusterPickingLineCommonCase):
|
8
|
+
"""Tests covering the /scan_line endpoint
|
9
|
+
|
10
|
+
With the no prefill quantity option set
|
11
|
+
|
12
|
+
"""
|
13
|
+
|
14
|
+
@classmethod
|
15
|
+
def _enable_no_prefill(cls):
|
16
|
+
cls.menu.sudo().no_prefill_qty = True
|
17
|
+
cls.picking = cls.batch.picking_ids
|
18
|
+
cls.line = cls.picking.move_line_ids
|
19
|
+
cls.line.reserved_uom_qty = 3
|
20
|
+
|
21
|
+
def _assert_qty_done(self, line, scanned, expected_qty_done):
|
22
|
+
batch = line.picking_id.batch_id
|
23
|
+
response = self.service.dispatch(
|
24
|
+
"scan_line",
|
25
|
+
params={
|
26
|
+
"picking_batch_id": batch.id,
|
27
|
+
"move_line_id": line.id,
|
28
|
+
"barcode": scanned,
|
29
|
+
},
|
30
|
+
)
|
31
|
+
qty_done = response["data"]["scan_destination"]["qty_done"]
|
32
|
+
self.assertEqual(qty_done, expected_qty_done)
|
33
|
+
|
34
|
+
def test_scan_line_package_no_prefill_set(self):
|
35
|
+
"""Check scanning a package when no_prefill_qty is enabled."""
|
36
|
+
self._simulate_batch_selected(self.batch, in_package=True)
|
37
|
+
self._enable_no_prefill()
|
38
|
+
self._assert_qty_done(self.line, self.line.package_id.name, 0.0)
|
39
|
+
|
40
|
+
def test_scan_line_packaging_no_prefill_set(self):
|
41
|
+
"""Check scanning a packaging when no_prefill_qty is enabled."""
|
42
|
+
self._simulate_batch_selected(self.batch, in_package=False)
|
43
|
+
self._enable_no_prefill()
|
44
|
+
packaging = self.env.ref(
|
45
|
+
"stock_storage_type.product_product_9_packaging_4_cardbox"
|
46
|
+
)
|
47
|
+
packaging.sudo().write(
|
48
|
+
{"product_id": self.line.product_id.id, "barcode": "cute-pack"}
|
49
|
+
)
|
50
|
+
# The quantity of the packaging is incremented
|
51
|
+
self._assert_qty_done(self.line, packaging.barcode, packaging.qty)
|
52
|
+
|
53
|
+
def test_scan_line_product_no_prefill_set(self):
|
54
|
+
"""Check qty done when product is scanned and no_prefill_qty is enabled"""
|
55
|
+
self._simulate_batch_selected(self.batch)
|
56
|
+
self._enable_no_prefill()
|
57
|
+
self._assert_qty_done(self.line, self.line.product_id.barcode, 1.0)
|
58
|
+
|
59
|
+
def test_scan_line_lot_no_prefill_set(self):
|
60
|
+
"""Check qty done when lot is scanned and no_prefill_qty is enabled"""
|
61
|
+
self.product_a.tracking = "lot"
|
62
|
+
self._simulate_batch_selected(self.batch, in_lot=True)
|
63
|
+
self._enable_no_prefill()
|
64
|
+
self._assert_qty_done(self.line, self.line.lot_id.name, 1.0)
|
65
|
+
|
66
|
+
def test_scan_line_location_no_prefill_set(self):
|
67
|
+
"""Check qty done when location is scanned and no_prefill_qty is enabled"""
|
68
|
+
self._simulate_batch_selected(self.batch, in_package=True)
|
69
|
+
self._enable_no_prefill()
|
70
|
+
self._assert_qty_done(self.line, self.line.location_id.barcode, 0)
|
@@ -0,0 +1,387 @@
|
|
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 odoo import fields
|
5
|
+
|
6
|
+
from .test_cluster_picking_base import ClusterPickingCommonCase
|
7
|
+
|
8
|
+
# pylint: disable=missing-return
|
9
|
+
|
10
|
+
|
11
|
+
class ClusterPickingSelectionCase(ClusterPickingCommonCase):
|
12
|
+
"""Tests covering the selection of picking batches
|
13
|
+
|
14
|
+
Endpoints:
|
15
|
+
|
16
|
+
* /cluster_picking/find_batch
|
17
|
+
* /cluster_picking/list_batch
|
18
|
+
* /cluster_picking/select
|
19
|
+
* /cluster_picking/unassign
|
20
|
+
|
21
|
+
These endpoints interact with a list of picking batches.
|
22
|
+
The other endpoints that interact with a single batch (after selection)
|
23
|
+
are handled in other classes.
|
24
|
+
"""
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
28
|
+
super().setUpClassBaseData(*args, **kwargs)
|
29
|
+
# drop base demo data and create our own batches to work with
|
30
|
+
old_batchs = cls.env["stock.picking.batch"].search([])
|
31
|
+
old_batchs.write({"state": "draft"})
|
32
|
+
old_batchs.unlink()
|
33
|
+
cls.batch1 = cls._create_picking_batch(
|
34
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=3)]]
|
35
|
+
)
|
36
|
+
cls.batch2 = cls._create_picking_batch(
|
37
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=3)]]
|
38
|
+
)
|
39
|
+
cls.batch3 = cls._create_picking_batch(
|
40
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=3)]]
|
41
|
+
)
|
42
|
+
cls.batch4 = cls._create_picking_batch(
|
43
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=3)]]
|
44
|
+
)
|
45
|
+
|
46
|
+
def _add_stock_and_assign_pickings_for_batches(self, batches):
|
47
|
+
pickings = batches.mapped("picking_ids")
|
48
|
+
self._fill_stock_for_moves(pickings.mapped("move_ids"))
|
49
|
+
pickings.action_assign()
|
50
|
+
|
51
|
+
def test_find_batch_in_progress_current_user(self):
|
52
|
+
"""Find an in-progress batch assigned to the current user"""
|
53
|
+
# Simulate the client asking a batch by clicking on "get work"
|
54
|
+
self._add_stock_and_assign_pickings_for_batches(
|
55
|
+
self.batch1 | self.batch2 | self.batch3
|
56
|
+
)
|
57
|
+
self.batch3.user_id = self.env.uid
|
58
|
+
self.batch3.action_confirm() # set to in progress
|
59
|
+
response = self.service.dispatch("find_batch")
|
60
|
+
|
61
|
+
# we expect to find batch 3 as it's assigned to the current
|
62
|
+
# user and in progress (first priority)
|
63
|
+
data = self.data.picking_batch(self.batch3, with_pickings=True)
|
64
|
+
self.assert_response(
|
65
|
+
response,
|
66
|
+
next_state="confirm_start",
|
67
|
+
data=data,
|
68
|
+
)
|
69
|
+
|
70
|
+
def test_find_batch_assigned(self):
|
71
|
+
"""Find a draft batch assigned to the current user"""
|
72
|
+
# batches must have all their pickings available to be selected
|
73
|
+
self._add_stock_and_assign_pickings_for_batches(
|
74
|
+
self.batch1 | self.batch2 | self.batch3
|
75
|
+
)
|
76
|
+
# batch2 in draft but assigned to the current user should be
|
77
|
+
# selected before the others
|
78
|
+
self.batch2.user_id = self.env.uid
|
79
|
+
response = self.service.dispatch("find_batch")
|
80
|
+
|
81
|
+
# The endpoint starts the batch
|
82
|
+
self.assertEqual(self.batch2.state, "in_progress")
|
83
|
+
|
84
|
+
# we expect to find batch 2 as it's assigned to the current user
|
85
|
+
data = self.data.picking_batch(self.batch2, with_pickings=True)
|
86
|
+
self.assert_response(
|
87
|
+
response,
|
88
|
+
next_state="confirm_start",
|
89
|
+
data=data,
|
90
|
+
)
|
91
|
+
|
92
|
+
def test_find_batch_unassigned_draft(self):
|
93
|
+
"""Find a draft batch"""
|
94
|
+
# batches must have all their pickings available to be selected
|
95
|
+
self._add_stock_and_assign_pickings_for_batches(self.batch2 | self.batch3)
|
96
|
+
# batch1 has not all pickings available, so the first draft
|
97
|
+
# is batch2, should be selected
|
98
|
+
response = self.service.dispatch("find_batch")
|
99
|
+
|
100
|
+
# The endpoint starts the batch and assign it to self
|
101
|
+
self.assertEqual(self.batch2.user_id, self.env.user)
|
102
|
+
self.assertEqual(self.batch2.state, "in_progress")
|
103
|
+
|
104
|
+
# we expect to find batch 2 as it's the first one with all pickings
|
105
|
+
# available
|
106
|
+
data = self.data.picking_batch(self.batch2, with_pickings=True)
|
107
|
+
self.assert_response(
|
108
|
+
response,
|
109
|
+
next_state="confirm_start",
|
110
|
+
data=data,
|
111
|
+
)
|
112
|
+
|
113
|
+
def test_find_batch_not_found(self):
|
114
|
+
"""No batch to work on"""
|
115
|
+
# No batch match the rules to work on them, because
|
116
|
+
# their pickings are not available
|
117
|
+
response = self.service.dispatch("find_batch")
|
118
|
+
self.assert_response(
|
119
|
+
response,
|
120
|
+
next_state="start",
|
121
|
+
message={
|
122
|
+
"message_type": "info",
|
123
|
+
"body": "No more work to do, please create a new batch transfer",
|
124
|
+
},
|
125
|
+
)
|
126
|
+
|
127
|
+
def test_list_batch(self):
|
128
|
+
"""List all available batches"""
|
129
|
+
# batches must have all their pickings available to be selected
|
130
|
+
self._add_stock_and_assign_pickings_for_batches(
|
131
|
+
self.batch1 | self.batch2 | self.batch3
|
132
|
+
)
|
133
|
+
self.batch1.write({"state": "in_progress", "user_id": self.env.uid})
|
134
|
+
self.batch2.write(
|
135
|
+
{"state": "in_progress", "user_id": self.env.ref("base.user_demo").id}
|
136
|
+
)
|
137
|
+
self.batch3.write({"state": "draft", "user_id": False})
|
138
|
+
|
139
|
+
self.assertEqual(
|
140
|
+
self.env["stock.picking.batch"].search([]),
|
141
|
+
self.batch1 + self.batch2 + self.batch3 + self.batch4,
|
142
|
+
)
|
143
|
+
# Simulate the client asking the list of batches
|
144
|
+
response = self.service.dispatch("list_batch")
|
145
|
+
self.assert_response(
|
146
|
+
response,
|
147
|
+
next_state="manual_selection",
|
148
|
+
data={
|
149
|
+
"size": 2,
|
150
|
+
"records": self.data.picking_batches(self.batch1 + self.batch3),
|
151
|
+
},
|
152
|
+
)
|
153
|
+
|
154
|
+
def test_select_in_progress_assigned(self):
|
155
|
+
"""Select an in-progress batch assigned to the current user"""
|
156
|
+
self._add_stock_and_assign_pickings_for_batches(self.batch1)
|
157
|
+
self.batch1.write({"state": "in_progress", "user_id": self.env.uid})
|
158
|
+
# Simulate the client selecting the batch in a list
|
159
|
+
response = self.service.dispatch(
|
160
|
+
"select", params={"picking_batch_id": self.batch1.id}
|
161
|
+
)
|
162
|
+
data = self.data.picking_batch(self.batch1)
|
163
|
+
# we don't care in these tests, 'find_batch' tests them already
|
164
|
+
data["pickings"] = self.ANY
|
165
|
+
self.assert_response(
|
166
|
+
response,
|
167
|
+
next_state="confirm_start",
|
168
|
+
data=data,
|
169
|
+
)
|
170
|
+
|
171
|
+
def test_select_draft_assigned(self):
|
172
|
+
"""Select a draft batch assigned to the current user"""
|
173
|
+
self._add_stock_and_assign_pickings_for_batches(self.batch1)
|
174
|
+
self.batch1.write({"user_id": self.env.uid})
|
175
|
+
# Simulate the client selecting the batch in a list
|
176
|
+
response = self.service.dispatch(
|
177
|
+
"select", params={"picking_batch_id": self.batch1.id}
|
178
|
+
)
|
179
|
+
# The endpoint starts the batch and assign it to self
|
180
|
+
self.assertEqual(self.batch1.user_id, self.env.user)
|
181
|
+
self.assertEqual(self.batch1.state, "in_progress")
|
182
|
+
data = self.data.picking_batch(self.batch1)
|
183
|
+
# we don't care in these tests, 'find_batch' tests them already
|
184
|
+
data["pickings"] = self.ANY
|
185
|
+
self.assert_response(
|
186
|
+
response,
|
187
|
+
next_state="confirm_start",
|
188
|
+
data=data,
|
189
|
+
)
|
190
|
+
|
191
|
+
def test_select_draft_unassigned(self):
|
192
|
+
"""Select a draft batch not assigned to a user"""
|
193
|
+
self._add_stock_and_assign_pickings_for_batches(self.batch1)
|
194
|
+
# Simulate the client selecting the batch in a list
|
195
|
+
response = self.service.dispatch(
|
196
|
+
"select", params={"picking_batch_id": self.batch1.id}
|
197
|
+
)
|
198
|
+
# The endpoint starts the batch and assign it to self
|
199
|
+
self.assertEqual(self.batch1.user_id, self.env.user)
|
200
|
+
self.assertEqual(self.batch1.state, "in_progress")
|
201
|
+
data = self.data.picking_batch(self.batch1)
|
202
|
+
# we don't care in these tests, 'find_batch' tests them already
|
203
|
+
data["pickings"] = self.ANY
|
204
|
+
self.assert_response(
|
205
|
+
response,
|
206
|
+
next_state="confirm_start",
|
207
|
+
data=data,
|
208
|
+
)
|
209
|
+
|
210
|
+
def test_select_not_exists(self):
|
211
|
+
"""Select a draft that does not exist"""
|
212
|
+
batch_id = self.batch1.id
|
213
|
+
self.batch1.state = "draft"
|
214
|
+
self.batch1.unlink()
|
215
|
+
# Simulate the client selecting the batch in a list
|
216
|
+
response = self.service.dispatch(
|
217
|
+
"select", params={"picking_batch_id": batch_id}
|
218
|
+
)
|
219
|
+
self.assert_response(
|
220
|
+
response,
|
221
|
+
next_state="manual_selection",
|
222
|
+
message={
|
223
|
+
"message_type": "warning",
|
224
|
+
"body": "This batch cannot be selected.",
|
225
|
+
},
|
226
|
+
data={"size": 0, "records": []},
|
227
|
+
)
|
228
|
+
|
229
|
+
def test_select_already_assigned(self):
|
230
|
+
"""Select a draft that does not exist"""
|
231
|
+
self._add_stock_and_assign_pickings_for_batches(self.batch1)
|
232
|
+
self.batch1.write(
|
233
|
+
{"state": "in_progress", "user_id": self.env.ref("base.user_demo").id}
|
234
|
+
)
|
235
|
+
# Simulate the client selecting the batch in a list
|
236
|
+
response = self.service.dispatch(
|
237
|
+
"select", params={"picking_batch_id": self.batch1.id}
|
238
|
+
)
|
239
|
+
self.assert_response(
|
240
|
+
response,
|
241
|
+
next_state="manual_selection",
|
242
|
+
message={
|
243
|
+
"message_type": "warning",
|
244
|
+
"body": "This batch cannot be selected.",
|
245
|
+
},
|
246
|
+
data={"size": 0, "records": []},
|
247
|
+
)
|
248
|
+
|
249
|
+
def test_unassign_batch(self):
|
250
|
+
"""User cancels after selecting a batch, unassign it"""
|
251
|
+
self._simulate_batch_selected(self.batch1)
|
252
|
+
# Simulate the client selecting the batch in a list
|
253
|
+
response = self.service.dispatch(
|
254
|
+
"unassign", params={"picking_batch_id": self.batch1.id}
|
255
|
+
)
|
256
|
+
self.assertEqual(self.batch1.state, "draft")
|
257
|
+
self.assertFalse(self.batch1.user_id)
|
258
|
+
self.assert_response(response, next_state="start")
|
259
|
+
|
260
|
+
def test_unassign_batch_not_exists(self):
|
261
|
+
"""User cancels after selecting a batch deleted meanwhile"""
|
262
|
+
batch_id = self.batch1.id
|
263
|
+
self.batch1.state = "draft"
|
264
|
+
self.batch1.unlink()
|
265
|
+
# Simulate the client selecting the batch in a list
|
266
|
+
response = self.service.dispatch(
|
267
|
+
"unassign", params={"picking_batch_id": batch_id}
|
268
|
+
)
|
269
|
+
self.assert_response(response, next_state="start")
|
270
|
+
|
271
|
+
|
272
|
+
class ClusterPickingSelectedCase(ClusterPickingCommonCase):
|
273
|
+
"""Tests covering endpoints working on a single picking batch
|
274
|
+
|
275
|
+
After a batch has been selected, by the tests covered in
|
276
|
+
``ClusterPickingSelectionCase``.
|
277
|
+
"""
|
278
|
+
|
279
|
+
@classmethod
|
280
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
281
|
+
super().setUpClassBaseData(*args, **kwargs)
|
282
|
+
cls.batch = cls._create_picking_batch(
|
283
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=1)]]
|
284
|
+
)
|
285
|
+
cls._simulate_batch_selected(cls.batch, in_package=True)
|
286
|
+
cls.batch2 = cls._create_picking_batch(
|
287
|
+
[
|
288
|
+
[cls.BatchProduct(product=cls.product_a, quantity=1)],
|
289
|
+
[cls.BatchProduct(product=cls.product_a, quantity=1)],
|
290
|
+
[cls.BatchProduct(product=cls.product_b, quantity=1)],
|
291
|
+
[cls.BatchProduct(product=cls.product_b, quantity=1)],
|
292
|
+
]
|
293
|
+
)
|
294
|
+
cls._simulate_batch_selected(cls.batch2, in_package=True)
|
295
|
+
|
296
|
+
def test_lines_order(self):
|
297
|
+
batch = self.batch2
|
298
|
+
picking1 = batch.picking_ids[0]
|
299
|
+
today = fields.Datetime.today()
|
300
|
+
future = fields.Datetime.add(
|
301
|
+
fields.Datetime.end_of(fields.Datetime.today(), "day"), days=2
|
302
|
+
)
|
303
|
+
# Change dates
|
304
|
+
move1 = picking1.move_ids[0]
|
305
|
+
move1_line = move1.move_line_ids[0]
|
306
|
+
move1.write({"date": today})
|
307
|
+
(batch.picking_ids.move_ids - move1).write({"date": future})
|
308
|
+
|
309
|
+
move_lines = self.service._lines_for_picking_batch(batch)
|
310
|
+
order_mapping = {line: i for i, line in enumerate(move_lines)}
|
311
|
+
|
312
|
+
# Today line comes first
|
313
|
+
self.assertEqual(order_mapping[move1_line], 0)
|
314
|
+
# swap dates
|
315
|
+
move1.write({"date": future})
|
316
|
+
(batch.picking_ids.move_ids - move1).write({"date": today})
|
317
|
+
|
318
|
+
move_lines = self.service._lines_for_picking_batch(batch)
|
319
|
+
order_mapping = {line: i for i, line in enumerate(move_lines)}
|
320
|
+
self.assertEqual(order_mapping[move1_line], len(move_lines) - 1)
|
321
|
+
# TODO: we should test all the combo of keys affecting sorting.
|
322
|
+
|
323
|
+
def test_confirm_start_ok(self):
|
324
|
+
"""User confirms she starts the selected picking batch (happy path)"""
|
325
|
+
# batch1 was already selected, we only need to confirm the selection
|
326
|
+
batch = self.batch
|
327
|
+
self.assertEqual(batch.state, "in_progress")
|
328
|
+
picking = batch.picking_ids[0]
|
329
|
+
first_move_line = picking.move_line_ids[0]
|
330
|
+
self.assertTrue(first_move_line)
|
331
|
+
# A package exists on the move line, because the quant created
|
332
|
+
# by ``_simulate_batch_selected`` has a package.
|
333
|
+
package = first_move_line.package_id
|
334
|
+
self.assertTrue(package)
|
335
|
+
|
336
|
+
response = self.service.dispatch(
|
337
|
+
"confirm_start", params={"picking_batch_id": self.batch.id}
|
338
|
+
)
|
339
|
+
data = self.data.move_line(first_move_line)
|
340
|
+
data["package_dest"] = None
|
341
|
+
data["picking"] = self.data.picking(picking)
|
342
|
+
data["batch"] = self.data.picking_batch(batch)
|
343
|
+
data["scan_location_or_pack_first"] = False
|
344
|
+
self.assert_response(
|
345
|
+
response,
|
346
|
+
data=data,
|
347
|
+
next_state="start_line",
|
348
|
+
)
|
349
|
+
|
350
|
+
def test_confirm_start_not_exists(self):
|
351
|
+
"""User confirms she starts but batch has been deleted meanwhile"""
|
352
|
+
batch_id = self.batch.id
|
353
|
+
self.batch.state = "draft"
|
354
|
+
self.batch.unlink()
|
355
|
+
response = self.service.dispatch(
|
356
|
+
"confirm_start", params={"picking_batch_id": batch_id}
|
357
|
+
)
|
358
|
+
self.assert_response(
|
359
|
+
response,
|
360
|
+
message={
|
361
|
+
"message_type": "error",
|
362
|
+
"body": "The record you were working on does not exist anymore.",
|
363
|
+
},
|
364
|
+
next_state="start",
|
365
|
+
)
|
366
|
+
|
367
|
+
def test_confirm_start_all_is_done(self):
|
368
|
+
"""User confirms start but all lines are already done"""
|
369
|
+
# we want to jump to the start because there are no lines
|
370
|
+
# to process anymore, but we want to set pickings and
|
371
|
+
# picking batch to done if not done yet (because the process
|
372
|
+
# was interrupted for instance)
|
373
|
+
self._set_dest_package_and_done(
|
374
|
+
self.batch.mapped("picking_ids.move_line_ids"),
|
375
|
+
self.env["stock.quant.package"].create({}),
|
376
|
+
)
|
377
|
+
self.batch.action_done()
|
378
|
+
response = self.service.dispatch(
|
379
|
+
"confirm_start", params={"picking_batch_id": self.batch.id}
|
380
|
+
)
|
381
|
+
self.assert_response(
|
382
|
+
response,
|
383
|
+
next_state="start",
|
384
|
+
message={"body": "Batch Transfer complete", "message_type": "success"},
|
385
|
+
)
|
386
|
+
|
387
|
+
# TODO: add a test for lines sorting
|
@@ -0,0 +1,90 @@
|
|
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_cluster_picking_base import ClusterPickingCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class ClusterPickingSkipLineCase(ClusterPickingCommonCase):
|
10
|
+
"""Tests covering the /skip_line endpoint"""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
14
|
+
super().setUpClassBaseData(*args, **kwargs)
|
15
|
+
# quants already existing are from demo data
|
16
|
+
cls.env["stock.quant"].sudo().search(
|
17
|
+
[("location_id", "=", cls.stock_location.id)]
|
18
|
+
).unlink()
|
19
|
+
cls.batch = cls._create_picking_batch(
|
20
|
+
[
|
21
|
+
[
|
22
|
+
cls.BatchProduct(product=cls.product_a, quantity=10),
|
23
|
+
cls.BatchProduct(product=cls.product_b, quantity=20),
|
24
|
+
],
|
25
|
+
[
|
26
|
+
cls.BatchProduct(product=cls.product_a, quantity=30),
|
27
|
+
cls.BatchProduct(product=cls.product_b, quantity=40),
|
28
|
+
],
|
29
|
+
]
|
30
|
+
)
|
31
|
+
|
32
|
+
def _skip_line(self, line, next_line=None):
|
33
|
+
response = self.service.dispatch(
|
34
|
+
"skip_line",
|
35
|
+
params={"picking_batch_id": self.batch.id, "move_line_id": line.id},
|
36
|
+
)
|
37
|
+
if next_line:
|
38
|
+
self.assert_response(
|
39
|
+
response, next_state="start_line", data=self._line_data(next_line)
|
40
|
+
)
|
41
|
+
return response
|
42
|
+
|
43
|
+
def test_skip_line(self):
|
44
|
+
# put one picking in another location
|
45
|
+
self.batch.picking_ids[1].location_id = self.shelf1
|
46
|
+
self.batch.picking_ids[1].move_ids.location_id = self.shelf1
|
47
|
+
# select batch
|
48
|
+
self._simulate_batch_selected(self.batch, in_package=True)
|
49
|
+
|
50
|
+
# enforce names to have reliable sorting
|
51
|
+
self.stock_location.sudo().name = "LOC2"
|
52
|
+
self.shelf1.sudo().name = "LOC1"
|
53
|
+
all_lines = self.batch.picking_ids.move_line_ids
|
54
|
+
loc1_lines = all_lines.filtered(lambda line: (line.location_id == self.shelf1))
|
55
|
+
loc2_lines = all_lines.filtered(
|
56
|
+
lambda line: (line.location_id == self.stock_location)
|
57
|
+
)
|
58
|
+
# no line postponed yet
|
59
|
+
self.assertEqual(
|
60
|
+
all_lines.mapped("shopfloor_postponed"), [False, False, False, False]
|
61
|
+
)
|
62
|
+
|
63
|
+
# skip line from loc 1
|
64
|
+
previous_priority = loc1_lines[0].shopfloor_priority
|
65
|
+
self._skip_line(loc1_lines[0], loc1_lines[1])
|
66
|
+
self.assertEqual(loc1_lines[0].shopfloor_priority, previous_priority + 1)
|
67
|
+
loc1_lines.invalidate_recordset(["shopfloor_postponed"])
|
68
|
+
self.assertTrue(loc1_lines[0].shopfloor_postponed)
|
69
|
+
|
70
|
+
# 2nd line, next is 1st from 2nd location
|
71
|
+
self.assertFalse(loc1_lines[1].shopfloor_postponed)
|
72
|
+
self._skip_line(loc1_lines[1], loc2_lines[0])
|
73
|
+
# Priority is now the current max + 1
|
74
|
+
self.assertEqual(
|
75
|
+
loc1_lines[1].shopfloor_priority, loc1_lines[0].shopfloor_priority + 1
|
76
|
+
)
|
77
|
+
loc1_lines.invalidate_recordset(["shopfloor_postponed"])
|
78
|
+
self.assertTrue(loc1_lines[1].shopfloor_postponed)
|
79
|
+
|
80
|
+
# 3rd line, next is 4th
|
81
|
+
self.assertFalse(loc2_lines[0].shopfloor_postponed)
|
82
|
+
self._skip_line(loc2_lines[0], loc2_lines[1])
|
83
|
+
self.assertEqual(
|
84
|
+
loc2_lines[0].shopfloor_priority, loc1_lines[1].shopfloor_priority + 1
|
85
|
+
)
|
86
|
+
loc1_lines.invalidate_recordset(["shopfloor_postponed"])
|
87
|
+
self.assertTrue(loc2_lines[0].shopfloor_postponed)
|
88
|
+
|
89
|
+
|
90
|
+
# TODO tests for transitions to next line / no next lines, ...
|