odoo-addon-shopfloor 16.0.2.2.0__py3-none-any.whl → 16.0.2.2.1.4__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 +1 -1
- odoo/addons/shopfloor/__manifest__.py +1 -1
- odoo/addons/shopfloor/actions/move_line_search.py +125 -33
- odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +7 -2
- odoo/addons/shopfloor/i18n/shopfloor.pot +102 -0
- odoo/addons/shopfloor/migrations/16.0.2.2.1/post-init_search_move_line_options.py +33 -0
- odoo/addons/shopfloor/models/shopfloor_menu.py +85 -0
- odoo/addons/shopfloor/services/location_content_transfer.py +20 -41
- odoo/addons/shopfloor/services/menu.py +6 -3
- odoo/addons/shopfloor/services/service.py +28 -2
- odoo/addons/shopfloor/services/zone_picking.py +2 -2
- odoo/addons/shopfloor/static/description/index.html +1 -1
- odoo/addons/shopfloor/tests/__init__.py +2 -0
- odoo/addons/shopfloor/tests/test_actions_search_move_line.py +128 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_get_work.py +41 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_mix.py +2 -2
- odoo/addons/shopfloor/tests/test_menu_contrains.py +40 -0
- odoo/addons/shopfloor/tests/test_menu_counters.py +112 -2
- odoo/addons/shopfloor/views/shopfloor_menu.xml +54 -0
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/RECORD +23 -20
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/top_level.txt +0 -0
@@ -19,10 +19,13 @@ class ShopfloorMenu(Component):
|
|
19
19
|
# TODO: maybe to be improved w/ raw SQL as this run for each menu item
|
20
20
|
# and it's called every time the menu is opened/gets refreshed
|
21
21
|
move_line_search = self._actions_for(
|
22
|
-
"search_move_line",
|
22
|
+
"search_move_line",
|
23
|
+
picking_types=record.picking_type_ids,
|
24
|
+
additional_domain=record.move_line_search_additional_domain,
|
25
|
+
sort_order=record.move_line_search_sort_order,
|
26
|
+
sort_order_custom_code=record.move_line_search_sort_order_custom_code,
|
23
27
|
)
|
24
|
-
|
25
|
-
lines_per_menu = move_line_search.search_move_lines_by_location(locations)
|
28
|
+
lines_per_menu = move_line_search.search_move_lines()
|
26
29
|
return move_line_search.counters_for_lines(lines_per_menu)
|
27
30
|
|
28
31
|
def _one_record_parser(self, record):
|
@@ -15,7 +15,15 @@ class BaseShopfloorService(AbstractComponent):
|
|
15
15
|
@property
|
16
16
|
def search_move_line(self):
|
17
17
|
# TODO: propagating `picking_types` should probably be default
|
18
|
-
return self._actions_for(
|
18
|
+
return self._actions_for(
|
19
|
+
"search_move_line",
|
20
|
+
propagate_kwargs=[
|
21
|
+
"picking_types",
|
22
|
+
"additional_domain",
|
23
|
+
"sort_order",
|
24
|
+
"sort_order_custom_code",
|
25
|
+
],
|
26
|
+
)
|
19
27
|
|
20
28
|
|
21
29
|
class BaseShopfloorProcess(AbstractComponent):
|
@@ -38,13 +46,31 @@ class BaseShopfloorProcess(AbstractComponent):
|
|
38
46
|
)
|
39
47
|
return self.work.picking_types
|
40
48
|
|
49
|
+
@property
|
50
|
+
def additional_domain(self):
|
51
|
+
return self.work.menu.move_line_search_additional_domain
|
52
|
+
|
53
|
+
@property
|
54
|
+
def sort_order(self):
|
55
|
+
return self.work.menu.move_line_search_sort_order
|
56
|
+
|
57
|
+
@property
|
58
|
+
def sort_order_custom_code(self):
|
59
|
+
return self.work.menu.move_line_search_sort_order_custom_code
|
60
|
+
|
41
61
|
@property
|
42
62
|
def search_move_line(self):
|
43
63
|
# TODO: picking types should be set somehow straight in the work context
|
44
64
|
# by `_validate_headers_update_work_context` in this way
|
45
65
|
# we can remove this override and the need to call `_get_process_picking_types`
|
46
66
|
# every time.
|
47
|
-
return self._actions_for(
|
67
|
+
return self._actions_for(
|
68
|
+
"search_move_line",
|
69
|
+
picking_types=self.picking_types,
|
70
|
+
additional_domain=self.additional_domain,
|
71
|
+
sort_order=self.sort_order,
|
72
|
+
sort_order_custom_code=self.sort_order_custom_code,
|
73
|
+
)
|
48
74
|
|
49
75
|
def _check_picking_status(self, pickings, states=("assigned",)):
|
50
76
|
"""Check if given pickings can be processed.
|
@@ -315,7 +315,7 @@ class ZonePicking(Component):
|
|
315
315
|
return self.search_move_line.counters_for_lines(zone_lines)
|
316
316
|
|
317
317
|
def _picking_type_zone_lines(self, zone_location, picking_type):
|
318
|
-
return self.search_move_line.
|
318
|
+
return self.search_move_line.search_move_lines(
|
319
319
|
zone_location, picking_type=picking_type
|
320
320
|
)
|
321
321
|
|
@@ -429,7 +429,7 @@ class ZonePicking(Component):
|
|
429
429
|
enforce_empty_package=False,
|
430
430
|
):
|
431
431
|
"""Find lines that potentially need work in given locations."""
|
432
|
-
return self.search_move_line.
|
432
|
+
return self.search_move_line.search_move_lines(
|
433
433
|
locations or self.zone_location,
|
434
434
|
picking_type=picking_type or self.picking_type,
|
435
435
|
order=self.lines_order,
|
@@ -367,7 +367,7 @@ ul.auto-toc {
|
|
367
367
|
!! This file is generated by oca-gen-addon-readme !!
|
368
368
|
!! changes will be overwritten. !!
|
369
369
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
370
|
-
!! source digest: sha256:
|
370
|
+
!! source digest: sha256:bb5933746c40b25c45fcadac4704a1a752a86c48bb857969ca935196736d24c6
|
371
371
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
372
372
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/wms/tree/16.0/shopfloor"><img alt="OCA/wms" src="https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/wms-16-0/wms-16-0-shopfloor"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/wms&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
373
373
|
<p>Shopfloor is a barcode scanner application for internal warehouse operations.</p>
|
@@ -1,8 +1,10 @@
|
|
1
1
|
from . import test_menu_counters
|
2
|
+
from . import test_menu_contrains
|
2
3
|
from . import test_openapi
|
3
4
|
from . import test_actions_change_package_lot
|
4
5
|
from . import test_actions_data
|
5
6
|
from . import test_actions_data_detail
|
7
|
+
from . import test_actions_search_move_line
|
6
8
|
from . import test_actions_search
|
7
9
|
from . import test_actions_stock
|
8
10
|
from . import test_single_pack_transfer
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# Copyright 2024 ACSONE SA/NV (http://www.acsone.eu)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
# pylint: disable=missing-return
|
4
|
+
from contextlib import contextmanager
|
5
|
+
|
6
|
+
from odoo.addons.base_rest.controllers.main import _PseudoCollection
|
7
|
+
from odoo.addons.component.core import WorkContext
|
8
|
+
|
9
|
+
from .common import CommonCase
|
10
|
+
|
11
|
+
|
12
|
+
class TestActionsSearchMoveLine(CommonCase):
|
13
|
+
@classmethod
|
14
|
+
def setUpClass(cls):
|
15
|
+
super().setUpClass()
|
16
|
+
cls.picking_user1 = cls._create_picking(
|
17
|
+
lines=[(cls.product_a, 10)], confirm=True
|
18
|
+
)
|
19
|
+
cls.picking_user2 = cls._create_picking(
|
20
|
+
lines=[(cls.product_a, 10)], confirm=True
|
21
|
+
)
|
22
|
+
cls.picking_no_user = cls._create_picking(
|
23
|
+
lines=[(cls.product_a, 10)], confirm=True
|
24
|
+
)
|
25
|
+
|
26
|
+
cls.pickings = cls.picking_no_user | cls.picking_user1 | cls.picking_user2
|
27
|
+
cls._fill_stock_for_moves(cls.pickings.move_ids)
|
28
|
+
cls.pickings.action_assign()
|
29
|
+
cls.move_lines = cls.pickings.move_line_ids
|
30
|
+
cls.user1 = cls.env.user.copy({"name": "User 1", "login": "user1"})
|
31
|
+
cls.user2 = cls.env.user.copy({"name": "User 2", "login": "user2"})
|
32
|
+
cls.picking_no_user.user_id = False
|
33
|
+
cls.picking_user1.user_id = cls.user1.id
|
34
|
+
cls.picking_user2.move_line_ids.write({"shopfloor_user_id": cls.user2.id})
|
35
|
+
cls.picking_user2.user_id = cls.user2.id
|
36
|
+
|
37
|
+
@contextmanager
|
38
|
+
def work_on_actions(self, user=None, **params):
|
39
|
+
params = params or {}
|
40
|
+
env = self.env(user=user) if user else self.env
|
41
|
+
collection = _PseudoCollection("shopfloor.action", env)
|
42
|
+
yield WorkContext(
|
43
|
+
model_name="rest.service.registration", collection=collection, **params
|
44
|
+
)
|
45
|
+
|
46
|
+
@contextmanager
|
47
|
+
def search_move_line(self, user=None):
|
48
|
+
with self.work_on_actions(user) as work:
|
49
|
+
move_line_search = work.component(usage="search_move_line")
|
50
|
+
yield move_line_search
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def setUpClassVars(cls):
|
54
|
+
super().setUpClassVars()
|
55
|
+
cls.wh = cls.env.ref("stock.warehouse0")
|
56
|
+
cls.picking_type = cls.wh.out_type_id
|
57
|
+
|
58
|
+
def test_search_move_line_sorter(self):
|
59
|
+
with self.search_move_line() as move_line_search:
|
60
|
+
move_lines = self.move_lines.sorted(
|
61
|
+
move_line_search._sort_key_move_lines(order="assigned_to_current_user")
|
62
|
+
)
|
63
|
+
# we must get operations in the following order:
|
64
|
+
# * no shopfloor user and not user assigned to picking
|
65
|
+
# * no shopfloor user and user assigned to picking
|
66
|
+
# * shopfloor user or user assigned to picking
|
67
|
+
|
68
|
+
self.assertFalse(move_lines[0].shopfloor_user_id)
|
69
|
+
self.assertFalse(move_lines[0].picking_id.user_id)
|
70
|
+
self.assertFalse(move_lines[1].shopfloor_user_id)
|
71
|
+
self.assertTrue(move_lines[1].picking_id.user_id)
|
72
|
+
self.assertTrue(move_lines[2].shopfloor_user_id)
|
73
|
+
|
74
|
+
with self.search_move_line(user=self.user1) as move_line_search:
|
75
|
+
move_lines = self.move_lines.sorted(
|
76
|
+
move_line_search._sort_key_move_lines(order="assigned_to_current_user")
|
77
|
+
)
|
78
|
+
# user1 is only assigned at picking level
|
79
|
+
# we must get operations in the following order:
|
80
|
+
# * no shopfloor user but user assigned to picking
|
81
|
+
# * no shopfloor user and not user assigned to picking
|
82
|
+
# * shopfloor user or user assigned to picking
|
83
|
+
self.assertFalse(move_lines[0].shopfloor_user_id)
|
84
|
+
self.assertEqual(move_lines[0].picking_id.user_id, self.user1)
|
85
|
+
self.assertFalse(move_lines[1].shopfloor_user_id)
|
86
|
+
self.assertTrue(move_lines[2].shopfloor_user_id)
|
87
|
+
|
88
|
+
with self.search_move_line(user=self.user2) as move_line_search:
|
89
|
+
move_lines = self.move_lines.sorted(
|
90
|
+
move_line_search._sort_key_move_lines(order="assigned_to_current_user")
|
91
|
+
)
|
92
|
+
# user2 is only assigned at move level
|
93
|
+
# we must get operations in the following order:
|
94
|
+
# * shopfloor user or user assigned to picking
|
95
|
+
# * no shopfloor user and no user assigned to picking
|
96
|
+
# * no shopfloor user and user assigned to picking
|
97
|
+
self.assertEqual(move_lines[0].shopfloor_user_id, self.user2)
|
98
|
+
self.assertFalse(move_lines[1].shopfloor_user_id)
|
99
|
+
self.assertFalse(move_lines[1].picking_id.user_id)
|
100
|
+
self.assertTrue(move_lines[2].picking_id.user_id)
|
101
|
+
|
102
|
+
def test_search_move_line_match_user(self):
|
103
|
+
with self.search_move_line() as move_line_search:
|
104
|
+
move_lines = move_line_search.search_move_lines(
|
105
|
+
locations=self.picking_type.default_location_src_id,
|
106
|
+
match_user=True,
|
107
|
+
)
|
108
|
+
# we must only get picking not assigned to a user
|
109
|
+
self.assertFalse(move_lines.picking_id.user_id)
|
110
|
+
self.assertFalse(move_lines.shopfloor_user_id)
|
111
|
+
|
112
|
+
with self.search_move_line(user=self.user1) as move_line_search:
|
113
|
+
move_lines = move_line_search.search_move_lines(
|
114
|
+
locations=self.picking_type.default_location_src_id,
|
115
|
+
match_user=True,
|
116
|
+
)
|
117
|
+
# we must only get picking assigned to user1 or not assigned to any user
|
118
|
+
self.assertEqual(move_lines.picking_id.user_id, self.user1)
|
119
|
+
self.assertFalse(move_lines.shopfloor_user_id)
|
120
|
+
|
121
|
+
with self.search_move_line(user=self.user2) as move_line_search:
|
122
|
+
move_lines = move_line_search.search_move_lines(
|
123
|
+
locations=self.picking_type.default_location_src_id,
|
124
|
+
match_user=True,
|
125
|
+
)
|
126
|
+
# we must only get picking assigned to user2
|
127
|
+
self.assertEqual(move_lines.picking_id.user_id, self.user2)
|
128
|
+
self.assertEqual(move_lines.shopfloor_user_id, self.user2)
|
@@ -105,6 +105,47 @@ class TestLocationContentTransferGetWork(LocationContentTransferCommonCase):
|
|
105
105
|
)
|
106
106
|
self.assertEqual(response["next_state"], "scan_destination_all")
|
107
107
|
|
108
|
+
def test_find_work_additional_domain(self):
|
109
|
+
next_location = self.service._find_location_to_work_from()
|
110
|
+
self.assertTrue(next_location)
|
111
|
+
self.menu.sudo().move_line_search_additional_domain = [
|
112
|
+
("picking_id.id", "not in", self.pickings.ids)
|
113
|
+
]
|
114
|
+
next_location = self.service._find_location_to_work_from()
|
115
|
+
self.assertFalse(next_location)
|
116
|
+
response = self.service.dispatch("find_work", params={})
|
117
|
+
self.assert_response(
|
118
|
+
response,
|
119
|
+
next_state="get_work",
|
120
|
+
data={},
|
121
|
+
message=self.service.msg_store.no_work_found(),
|
122
|
+
)
|
123
|
+
|
124
|
+
def test_find_work_custom_sort_key(self):
|
125
|
+
# fmt: off
|
126
|
+
self.menu.sudo().write(
|
127
|
+
{
|
128
|
+
"move_line_search_sort_order": "custom_code",
|
129
|
+
"move_line_search_sort_order_custom_code":
|
130
|
+
f"key = (-1 if line.location_id.id == {self.content_loc.id} else 10, )",
|
131
|
+
}
|
132
|
+
)
|
133
|
+
# fmt: on
|
134
|
+
|
135
|
+
next_location = self.service._find_location_to_work_from()
|
136
|
+
self.assertEqual(next_location, self.content_loc)
|
137
|
+
self.menu.sudo().move_line_search_sort_order_custom_code = (
|
138
|
+
f"key = (-1 if line.location_id.id == {self.content_loc2.id} else 10, )"
|
139
|
+
)
|
140
|
+
next_location = self.service._find_location_to_work_from()
|
141
|
+
self.assertEqual(next_location, self.content_loc2)
|
142
|
+
self.menu.sudo().move_line_search_sort_order_custom_code = f"""
|
143
|
+
key = (-1 if line.location_id.id == {self.content_loc2.id} else 10, ) + \
|
144
|
+
get_sort_key_assigned_to_current_user(line)
|
145
|
+
"""
|
146
|
+
next_location = self.service._find_location_to_work_from()
|
147
|
+
self.assertEqual(next_location, self.content_loc2)
|
148
|
+
|
108
149
|
def test_cancel_work(self):
|
109
150
|
next_location = self.service._find_location_to_work_from()
|
110
151
|
stock = self.service._actions_for("stock")
|
@@ -370,8 +370,8 @@ class LocationContentTransferMixCase(LocationContentTransferCommonCase):
|
|
370
370
|
lambda l: not l.shopfloor_user_id and l.location_id == dest_location2
|
371
371
|
)
|
372
372
|
picking_before = pack_second_pallet.picking_id
|
373
|
-
move_lines = self.service.
|
374
|
-
pack_second_pallet.location_id
|
373
|
+
move_lines = self.service.search_move_line.search_move_lines(
|
374
|
+
locations=pack_second_pallet.location_id
|
375
375
|
)
|
376
376
|
response = self._location_content_transfer_process_line(pack_second_pallet)
|
377
377
|
response_packages = response["data"]["scan_destination_all"]["package_levels"]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright 2024 ACSONE SA/NV
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from odoo import exceptions
|
4
|
+
|
5
|
+
from .test_menu_base import MenuCountersCommonCase
|
6
|
+
|
7
|
+
|
8
|
+
class TestMenuContrains(MenuCountersCommonCase):
|
9
|
+
def test_move_line_search_sort_order_custom_code_invalid(self):
|
10
|
+
|
11
|
+
with self.assertRaises(exceptions.ValidationError):
|
12
|
+
# wrong indentation in python code
|
13
|
+
self.menu1.sudo().write(
|
14
|
+
{
|
15
|
+
"move_line_search_sort_order_custom_code": """
|
16
|
+
key = 1
|
17
|
+
toto(1)
|
18
|
+
""",
|
19
|
+
"move_line_search_sort_order": "custom_code",
|
20
|
+
}
|
21
|
+
)
|
22
|
+
|
23
|
+
self.menu1.sudo().write(
|
24
|
+
{
|
25
|
+
"move_line_search_sort_order_custom_code": "key = 1",
|
26
|
+
"move_line_search_sort_order": "custom_code",
|
27
|
+
}
|
28
|
+
)
|
29
|
+
|
30
|
+
def test_move_line_search_sort_order_mismatch(self):
|
31
|
+
with self.assertRaises(exceptions.ValidationError):
|
32
|
+
self.menu1.sudo().move_line_search_sort_order = "custom_code"
|
33
|
+
|
34
|
+
self.menu1.sudo().write(
|
35
|
+
{
|
36
|
+
"move_line_search_sort_order_custom_code": "key = 1",
|
37
|
+
"move_line_search_sort_order": "priority",
|
38
|
+
}
|
39
|
+
)
|
40
|
+
self.assertFalse(self.menu1.move_line_search_sort_order_custom_code)
|
@@ -24,6 +24,9 @@ class TestMenuCountersCommonCase(MenuCountersCommonCase):
|
|
24
24
|
.sudo()
|
25
25
|
.search([("profile_id", "=", self.wms_profile.id)])
|
26
26
|
)
|
27
|
+
# ensures expected counters are in the expected menu items
|
28
|
+
self.assertIn(self.menu1, expected_menu_items)
|
29
|
+
self.assertIn(self.menu2, expected_menu_items)
|
27
30
|
service = self.get_service("menu", profile=self.wms_profile)
|
28
31
|
response = service.dispatch("search")
|
29
32
|
self._assert_menu_response(
|
@@ -33,6 +36,51 @@ class TestMenuCountersCommonCase(MenuCountersCommonCase):
|
|
33
36
|
)
|
34
37
|
|
35
38
|
def test_menu_search_profile2(self):
|
39
|
+
expected_menu_items = (
|
40
|
+
self.env["shopfloor.menu"]
|
41
|
+
.sudo()
|
42
|
+
.search([("profile_id", "=", self.wms_profile2.id)])
|
43
|
+
)
|
44
|
+
expected_counters = {
|
45
|
+
expected_menu_item[0].id: {
|
46
|
+
"lines_count": 0,
|
47
|
+
"picking_count": 0,
|
48
|
+
"priority_lines_count": 0,
|
49
|
+
"priority_picking_count": 0,
|
50
|
+
}
|
51
|
+
for expected_menu_item in expected_menu_items
|
52
|
+
}
|
53
|
+
service = self.get_service("menu", profile=self.wms_profile2)
|
54
|
+
response = service.dispatch("search")
|
55
|
+
self._assert_menu_response(
|
56
|
+
response,
|
57
|
+
expected_menu_items.sorted("sequence"),
|
58
|
+
expected_counters=expected_counters,
|
59
|
+
)
|
60
|
+
|
61
|
+
def test_menu_counter_priority(self):
|
62
|
+
"""Test that priority counters are correctly computed.
|
63
|
+
|
64
|
+
Priority on lines must be based on the move priority.
|
65
|
+
Priority on pickings must be based on the picking priority.
|
66
|
+
"""
|
67
|
+
self.pickings.write({"priority": "0"})
|
68
|
+
self.pickings.move_ids.write({"priority": "0"})
|
69
|
+
pickings_menu_2 = self.pickings.filtered(
|
70
|
+
lambda p: p.picking_type_id == self.menu2_picking_type
|
71
|
+
)
|
72
|
+
# Set priority on pickings only for first picking of menu 2
|
73
|
+
pickings_menu_2[0].write({"priority": "1"})
|
74
|
+
pickings_menu_2[0].move_ids.write({"priority": "0"})
|
75
|
+
|
76
|
+
# set priority on move lines only for second picking of menu 2
|
77
|
+
pickings_menu_2[1].write({"priority": "0"})
|
78
|
+
pickings_menu_2[1].move_ids[0].write({"priority": "1"})
|
79
|
+
|
80
|
+
# set priority on both move lines and pickings for third picking of menu 2
|
81
|
+
pickings_menu_2[2].write({"priority": "1"})
|
82
|
+
pickings_menu_2[2].move_ids.write({"priority": "1"})
|
83
|
+
|
36
84
|
expected_counters = {
|
37
85
|
self.menu1.id: {
|
38
86
|
"lines_count": 2,
|
@@ -40,6 +88,41 @@ class TestMenuCountersCommonCase(MenuCountersCommonCase):
|
|
40
88
|
"priority_lines_count": 0,
|
41
89
|
"priority_picking_count": 0,
|
42
90
|
},
|
91
|
+
self.menu2.id: {
|
92
|
+
"lines_count": 6,
|
93
|
+
"picking_count": 3,
|
94
|
+
"priority_lines_count": 1 + len(pickings_menu_2[2].move_ids),
|
95
|
+
"priority_picking_count": 2,
|
96
|
+
},
|
97
|
+
}
|
98
|
+
expected_menu_items = (
|
99
|
+
self.env["shopfloor.menu"]
|
100
|
+
.sudo()
|
101
|
+
.search([("profile_id", "=", self.wms_profile.id)])
|
102
|
+
)
|
103
|
+
# ensures expected counters are in the expected menu items
|
104
|
+
self.assertIn(self.menu1, expected_menu_items)
|
105
|
+
self.assertIn(self.menu2, expected_menu_items)
|
106
|
+
service = self.get_service("menu", profile=self.wms_profile)
|
107
|
+
response = service.dispatch("search")
|
108
|
+
self._assert_menu_response(
|
109
|
+
response,
|
110
|
+
expected_menu_items.sorted("sequence"),
|
111
|
+
expected_counters=expected_counters,
|
112
|
+
)
|
113
|
+
|
114
|
+
def test_menu_search_additional_domain(self):
|
115
|
+
self.menu1.sudo().move_line_search_additional_domain = [
|
116
|
+
("picking_id.priority", "=", "1")
|
117
|
+
]
|
118
|
+
|
119
|
+
expected_counters = {
|
120
|
+
self.menu1.id: {
|
121
|
+
"lines_count": 0,
|
122
|
+
"picking_count": 0,
|
123
|
+
"priority_lines_count": 0,
|
124
|
+
"priority_picking_count": 0,
|
125
|
+
},
|
43
126
|
self.menu2.id: {
|
44
127
|
"lines_count": 6,
|
45
128
|
"picking_count": 3,
|
@@ -50,9 +133,36 @@ class TestMenuCountersCommonCase(MenuCountersCommonCase):
|
|
50
133
|
expected_menu_items = (
|
51
134
|
self.env["shopfloor.menu"]
|
52
135
|
.sudo()
|
53
|
-
.search([("profile_id", "=", self.
|
136
|
+
.search([("profile_id", "=", self.wms_profile.id)])
|
54
137
|
)
|
55
|
-
|
138
|
+
# ensures expected counters are in the expected menu items
|
139
|
+
self.assertIn(self.menu1, expected_menu_items)
|
140
|
+
self.assertIn(self.menu2, expected_menu_items)
|
141
|
+
service = self.get_service("menu", profile=self.wms_profile)
|
142
|
+
response = service.dispatch("search")
|
143
|
+
self._assert_menu_response(
|
144
|
+
response,
|
145
|
+
expected_menu_items.sorted("sequence"),
|
146
|
+
expected_counters=expected_counters,
|
147
|
+
)
|
148
|
+
|
149
|
+
self.menu1.sudo().move_line_search_additional_domain = [
|
150
|
+
("picking_id.priority", "=", "0")
|
151
|
+
]
|
152
|
+
expected_counters = {
|
153
|
+
self.menu1.id: {
|
154
|
+
"lines_count": 2,
|
155
|
+
"picking_count": 2,
|
156
|
+
"priority_lines_count": 0,
|
157
|
+
"priority_picking_count": 0,
|
158
|
+
},
|
159
|
+
self.menu2.id: {
|
160
|
+
"lines_count": 6,
|
161
|
+
"picking_count": 3,
|
162
|
+
"priority_lines_count": 0,
|
163
|
+
"priority_picking_count": 0,
|
164
|
+
},
|
165
|
+
}
|
56
166
|
response = service.dispatch("search")
|
57
167
|
self._assert_menu_response(
|
58
168
|
response,
|
@@ -169,6 +169,60 @@
|
|
169
169
|
/>
|
170
170
|
<field name="allow_alternative_destination_package" />
|
171
171
|
</group>
|
172
|
+
<group
|
173
|
+
name="search_move_line"
|
174
|
+
attrs="{'invisible': [('move_line_search_sort_order_is_possible', '=', False), ('move_line_search_additional_domain_is_possible', '=', False)]}"
|
175
|
+
>
|
176
|
+
<field
|
177
|
+
name="move_line_search_additional_domain_is_possible"
|
178
|
+
invisible="1"
|
179
|
+
/>
|
180
|
+
<field
|
181
|
+
name="move_line_search_additional_domain"
|
182
|
+
widget="domain"
|
183
|
+
options="{'model': 'stock.move.line'}"
|
184
|
+
attrs="{'invisible': [('move_line_search_additional_domain_is_possible', '=', False)]}"
|
185
|
+
/>
|
186
|
+
<field name="move_line_search_sort_order_is_possible" invisible="1" />
|
187
|
+
<field
|
188
|
+
name="move_line_search_sort_order"
|
189
|
+
attrs="{'invisible': [('move_line_search_sort_order_is_possible', '=', False)]}"
|
190
|
+
/>
|
191
|
+
<div
|
192
|
+
class="o_cell o_wrap_label"
|
193
|
+
style="margin-top: 4px;"
|
194
|
+
colspan="2"
|
195
|
+
attrs="{'invisible': ['|', ('move_line_search_sort_order_is_possible', '=', False), ('move_line_search_sort_order', '!=', 'custom_code')]}"
|
196
|
+
>
|
197
|
+
<label
|
198
|
+
class="text-900"
|
199
|
+
for="move_line_search_sort_order_custom_code"
|
200
|
+
>Custom code to construct the key to sort move lines</label>
|
201
|
+
<p>Three variables are available:</p>
|
202
|
+
<ul>
|
203
|
+
<li><code
|
204
|
+
>line</code>: The move line for with the key must be computed.
|
205
|
+
</li>
|
206
|
+
<li><code
|
207
|
+
>get_sort_key_priority</code>: Return sort key to sort by priority. (return a tuple)
|
208
|
+
</li>
|
209
|
+
<li><code
|
210
|
+
>get_sort_key_location</code>: Return sort key to sort by location. (return a tuple)
|
211
|
+
</li>
|
212
|
+
<li><code
|
213
|
+
>get_sort_key_assigned_to_current_user</code>: Return sort key to get line assigned to current user first. (return a tuple)
|
214
|
+
</li>
|
215
|
+
<li>To assign the key value: <code
|
216
|
+
>key = get_sort_key_assigned_to_current_user(line) + (line.x, )</code></li>
|
217
|
+
</ul>
|
218
|
+
<field
|
219
|
+
name="move_line_search_sort_order_custom_code"
|
220
|
+
attrs="{'invisible': ['|', ('move_line_search_sort_order_is_possible', '=', False), ('move_line_search_sort_order', '!=', 'custom_code')]}"
|
221
|
+
nolabel="1"
|
222
|
+
/>
|
223
|
+
</div>
|
224
|
+
|
225
|
+
</group>
|
172
226
|
</group>
|
173
227
|
</field>
|
174
228
|
</record>
|
{odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: odoo-addon-shopfloor
|
3
|
-
Version: 16.0.2.2.
|
3
|
+
Version: 16.0.2.2.1.4
|
4
4
|
Summary: manage warehouse operations with barcode scanners
|
5
5
|
Home-page: https://github.com/OCA/wms
|
6
6
|
Author: Camptocamp, BCIM, Akretion, Odoo Community Association (OCA)
|
@@ -36,7 +36,7 @@ Shopfloor
|
|
36
36
|
!! This file is generated by oca-gen-addon-readme !!
|
37
37
|
!! changes will be overwritten. !!
|
38
38
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
39
|
-
!! source digest: sha256:
|
39
|
+
!! source digest: sha256:bb5933746c40b25c45fcadac4704a1a752a86c48bb857969ca935196736d24c6
|
40
40
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
41
41
|
|
42
42
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|