odoo-addon-shopfloor 16.0.2.1.2__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/cluster_picking.py +1 -2
- 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.1.2.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.2.1.2.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/RECORD +24 -21
- {odoo_addon_shopfloor-16.0.2.1.2.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.2.1.2.dist-info → odoo_addon_shopfloor-16.0.2.2.1.4.dist-info}/top_level.txt +0 -0
odoo/addons/shopfloor/README.rst
CHANGED
@@ -7,7 +7,7 @@ Shopfloor
|
|
7
7
|
!! This file is generated by oca-gen-addon-readme !!
|
8
8
|
!! changes will be overwritten. !!
|
9
9
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
10
|
-
!! source digest: sha256:
|
10
|
+
!! source digest: sha256:bb5933746c40b25c45fcadac4704a1a752a86c48bb857969ca935196736d24c6
|
11
11
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
12
12
|
|
13
13
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# Copyright 2024 ACSONE SA/NV
|
2
3
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
4
|
+
from odoo.tools import safe_eval
|
5
|
+
|
3
6
|
from odoo.addons.component.core import Component
|
4
7
|
|
5
8
|
|
@@ -20,9 +23,42 @@ class MoveLineSearch(Component):
|
|
20
23
|
self.work, "picking_types", self.env["stock.picking.type"].browse()
|
21
24
|
)
|
22
25
|
|
23
|
-
|
26
|
+
@property
|
27
|
+
def additional_domain(self):
|
28
|
+
return getattr(self.work, "additional_domain", [])
|
29
|
+
|
30
|
+
@property
|
31
|
+
def sort_order(self):
|
32
|
+
return getattr(self.work, "sort_order", "priority")
|
33
|
+
|
34
|
+
@property
|
35
|
+
def sort_order_custom_code(self):
|
36
|
+
return getattr(self.work, "sort_order_custom_code", None)
|
37
|
+
|
38
|
+
def _get_additional_domain_eval_context(self):
|
39
|
+
"""Prepare the context used when evaluating the additional domain
|
40
|
+
:returns: dict -- evaluation context given to safe_eval
|
41
|
+
"""
|
42
|
+
return {
|
43
|
+
"datetime": safe_eval.datetime,
|
44
|
+
"dateutil": safe_eval.dateutil,
|
45
|
+
"time": safe_eval.time,
|
46
|
+
"uid": self.env.uid,
|
47
|
+
"user": self.env.user,
|
48
|
+
}
|
49
|
+
|
50
|
+
def _sort_key_custom_code_eval_context(self, line):
|
51
|
+
return {
|
52
|
+
"line": line,
|
53
|
+
"key": None,
|
54
|
+
"get_sort_key_priority": self._sort_key_move_lines_priority,
|
55
|
+
"get_sort_key_location": self._sort_key_move_lines_location,
|
56
|
+
"get_sort_key_assigned_to_current_user": self._sort_key_assigned_to_current_user,
|
57
|
+
}
|
58
|
+
|
59
|
+
def _search_move_lines_domain(
|
24
60
|
self,
|
25
|
-
locations,
|
61
|
+
locations=None,
|
26
62
|
picking_type=None,
|
27
63
|
package=None,
|
28
64
|
product=None,
|
@@ -32,16 +68,26 @@ class MoveLineSearch(Component):
|
|
32
68
|
# When True, adds the package in the domain even if the package is False
|
33
69
|
enforce_empty_package=False,
|
34
70
|
):
|
71
|
+
"""Return a domain to search move lines.
|
72
|
+
|
73
|
+
Be careful on the use of the picking_type parameter. This paramater can take
|
74
|
+
a recordset or None as value. The interpretation of the value is as follows:
|
75
|
+
* If picking_type is None, the domain will be filtered on all picking types
|
76
|
+
defined in the work. (most probably those defined on the menu)
|
77
|
+
* If picking_type is a recordset, the domain will be filtered on the given
|
78
|
+
picking types if the recordset is not empty. If the recordset is empty,
|
79
|
+
the domain will not be filtered on any picking type.
|
80
|
+
"""
|
35
81
|
domain = [
|
36
|
-
("location_id", "child_of", locations.ids),
|
37
82
|
("qty_done", "=", 0),
|
38
83
|
("state", "in", ("assigned", "partially_available")),
|
39
84
|
]
|
40
|
-
if picking_type
|
41
|
-
|
42
|
-
domain += [("picking_id.picking_type_id", "
|
43
|
-
|
44
|
-
|
85
|
+
picking_types = picking_type if picking_type is not None else self.picking_types
|
86
|
+
if picking_types:
|
87
|
+
domain += [("picking_id.picking_type_id", "in", picking_types.ids)]
|
88
|
+
locations = locations or picking_types.default_location_src_id
|
89
|
+
if locations:
|
90
|
+
domain += [("location_id", "child_of", locations.ids)]
|
45
91
|
if package or package is not None and enforce_empty_package:
|
46
92
|
domain += [("package_id", "=", package.id if package else False)]
|
47
93
|
if product:
|
@@ -49,23 +95,26 @@ class MoveLineSearch(Component):
|
|
49
95
|
if lot:
|
50
96
|
domain += [("lot_id", "=", lot.id)]
|
51
97
|
if match_user:
|
98
|
+
# we only want to see the lines assigned to the current user
|
52
99
|
domain += [
|
53
|
-
"
|
54
|
-
("
|
55
|
-
("shopfloor_user_id", "=", self.env.uid),
|
100
|
+
("shopfloor_user_id", "in", (False, self.env.uid)),
|
101
|
+
("picking_id.user_id", "in", (False, self.env.uid)),
|
56
102
|
]
|
57
103
|
if picking_ready:
|
58
104
|
domain += [("picking_id.state", "=", "assigned")]
|
105
|
+
if self.additional_domain:
|
106
|
+
eval_context = self._get_additional_domain_eval_context()
|
107
|
+
domain += safe_eval.safe_eval(self.additional_domain, eval_context)
|
59
108
|
return domain
|
60
109
|
|
61
|
-
def
|
110
|
+
def search_move_lines(
|
62
111
|
self,
|
63
|
-
locations,
|
112
|
+
locations=None,
|
64
113
|
picking_type=None,
|
65
114
|
package=None,
|
66
115
|
product=None,
|
67
116
|
lot=None,
|
68
|
-
order=
|
117
|
+
order=None,
|
69
118
|
match_user=False,
|
70
119
|
sort_keys_func=None,
|
71
120
|
picking_ready=True,
|
@@ -73,7 +122,7 @@ class MoveLineSearch(Component):
|
|
73
122
|
):
|
74
123
|
"""Find lines that potentially need work in given locations."""
|
75
124
|
move_lines = self.env["stock.move.line"].search(
|
76
|
-
self.
|
125
|
+
self._search_move_lines_domain(
|
77
126
|
locations,
|
78
127
|
picking_type,
|
79
128
|
package,
|
@@ -84,36 +133,79 @@ class MoveLineSearch(Component):
|
|
84
133
|
enforce_empty_package=enforce_empty_package,
|
85
134
|
)
|
86
135
|
)
|
136
|
+
order = order or self.sort_order
|
87
137
|
sort_keys_func = sort_keys_func or self._sort_key_move_lines(order)
|
88
138
|
move_lines = move_lines.sorted(sort_keys_func)
|
89
139
|
return move_lines
|
90
140
|
|
91
|
-
|
92
|
-
def _sort_key_move_lines(order):
|
141
|
+
def _sort_key_move_lines(self, order=None):
|
93
142
|
"""Return a sorting function to order lines."""
|
143
|
+
if order is None:
|
144
|
+
return lambda line: tuple()
|
94
145
|
|
95
146
|
if order == "priority":
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
147
|
+
return self._sort_key_move_lines_priority
|
148
|
+
|
149
|
+
if order == "location":
|
150
|
+
return self._sort_key_move_lines_location
|
151
|
+
|
152
|
+
if order == "custom_code":
|
153
|
+
return self._sort_key_custom_code
|
154
|
+
|
155
|
+
if order == "assigned_to_current_user":
|
156
|
+
return self._sort_key_assigned_to_current_user
|
157
|
+
|
158
|
+
raise ValueError(f"Unknown order '{order}'")
|
159
|
+
|
160
|
+
def _sort_key_move_lines_priority(self, line):
|
161
|
+
# make prority negative to keep sorting ascending
|
162
|
+
return self._sort_key_assigned_to_current_user(line) + (
|
163
|
+
-int(line.move_id.priority or "0"),
|
164
|
+
line.move_id.date,
|
165
|
+
line.move_id.id,
|
166
|
+
)
|
167
|
+
|
168
|
+
def _sort_key_move_lines_location(self, line):
|
169
|
+
return self._sort_key_assigned_to_current_user(line) + (
|
170
|
+
line.location_id.shopfloor_picking_sequence or "",
|
171
|
+
line.location_id.name,
|
172
|
+
line.move_id.date,
|
173
|
+
line.move_id.id,
|
174
|
+
)
|
175
|
+
|
176
|
+
def _sort_key_assigned_to_current_user(self, line):
|
177
|
+
user_id = line.shopfloor_user_id.id or line.picking_id.user_id.id or None
|
178
|
+
# Determine sort priority
|
179
|
+
# Priority 0: Assigned to the current user
|
180
|
+
# Priority 1: Not assigned to any user
|
181
|
+
# Priority 2: Assigned to a different user
|
182
|
+
if user_id == self.env.uid:
|
183
|
+
priority = 0
|
184
|
+
elif user_id is None:
|
185
|
+
priority = 1
|
186
|
+
else:
|
187
|
+
priority = 2
|
188
|
+
return (priority,)
|
189
|
+
|
190
|
+
def _sort_key_custom_code(self, line):
|
191
|
+
context = self._sort_key_custom_code_eval_context(line)
|
192
|
+
safe_eval.safe_eval(
|
193
|
+
self.sort_order_custom_code, context, mode="exec", nocopy=True
|
194
|
+
)
|
195
|
+
return context["key"]
|
110
196
|
|
111
197
|
def counters_for_lines(self, lines):
|
112
198
|
# Not using mapped/filtered to support simple lists and generators
|
113
|
-
priority_lines =
|
199
|
+
priority_lines = set()
|
200
|
+
priority_pickings = set()
|
201
|
+
for line in lines:
|
202
|
+
if int(line.move_id.priority or "0") > 0:
|
203
|
+
priority_lines.add(line)
|
204
|
+
if int(line.picking_id.priority or "0") > 0:
|
205
|
+
priority_pickings.add(line.picking_id)
|
114
206
|
return {
|
115
207
|
"lines_count": len(lines),
|
116
208
|
"picking_count": len({x.picking_id.id for x in lines}),
|
117
209
|
"priority_lines_count": len(priority_lines),
|
118
|
-
"priority_picking_count": len(
|
210
|
+
"priority_picking_count": len(priority_pickings),
|
119
211
|
}
|
@@ -21,7 +21,9 @@
|
|
21
21
|
"multiple_move_single_pack": true,
|
22
22
|
"no_prefill_qty": true,
|
23
23
|
"scan_location_or_pack_first": true,
|
24
|
-
"allow_alternative_destination_package": true
|
24
|
+
"allow_alternative_destination_package": true,
|
25
|
+
"allow_move_line_search_sort_order": false,
|
26
|
+
"allow_move_line_search_additional_domain": true
|
25
27
|
}
|
26
28
|
</field>
|
27
29
|
</record>
|
@@ -68,7 +70,10 @@
|
|
68
70
|
"allow_unreserve_other_moves": true,
|
69
71
|
"allow_ignore_no_putaway_available": true,
|
70
72
|
"allow_get_work": true,
|
71
|
-
"no_prefill_qty": true
|
73
|
+
"no_prefill_qty": true,
|
74
|
+
"allow_move_line_search_sort_order": true,
|
75
|
+
"allow_move_line_search_additional_domain": true
|
76
|
+
|
72
77
|
}
|
73
78
|
</field>
|
74
79
|
</record>
|
@@ -110,6 +110,32 @@ msgid ""
|
|
110
110
|
"destination'."
|
111
111
|
msgstr ""
|
112
112
|
|
113
|
+
#. module: shopfloor
|
114
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
115
|
+
msgid ""
|
116
|
+
"<code>get_sort_key_assigned_to_current_user</code>: Return sort key to get "
|
117
|
+
"line assigned to current user first. (return a tuple)"
|
118
|
+
msgstr ""
|
119
|
+
|
120
|
+
#. module: shopfloor
|
121
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
122
|
+
msgid ""
|
123
|
+
"<code>get_sort_key_location</code>: Return sort key to sort by location. "
|
124
|
+
"(return a tuple)"
|
125
|
+
msgstr ""
|
126
|
+
|
127
|
+
#. module: shopfloor
|
128
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
129
|
+
msgid ""
|
130
|
+
"<code>get_sort_key_priority</code>: Return sort key to sort by priority. "
|
131
|
+
"(return a tuple)"
|
132
|
+
msgstr ""
|
133
|
+
|
134
|
+
#. module: shopfloor
|
135
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
136
|
+
msgid "<code>line</code>: The move line for with the key must be computed."
|
137
|
+
msgstr ""
|
138
|
+
|
113
139
|
#. module: shopfloor
|
114
140
|
#: model:ir.model,name:shopfloor.model_shopfloor_app
|
115
141
|
msgid "A Shopfloor application"
|
@@ -127,6 +153,11 @@ msgstr ""
|
|
127
153
|
msgid "Activate Zero Check"
|
128
154
|
msgstr ""
|
129
155
|
|
156
|
+
#. module: shopfloor
|
157
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__move_line_search_additional_domain
|
158
|
+
msgid "Additional domain used when searching move lines"
|
159
|
+
msgstr ""
|
160
|
+
|
130
161
|
#. module: shopfloor
|
131
162
|
#: model:ir.model,name:shopfloor.model_shopfloor_priority_postpone_mixin
|
132
163
|
msgid "Adds shopfloor priority/postpone fields"
|
@@ -324,6 +355,35 @@ msgstr ""
|
|
324
355
|
msgid "Creation of moves is not allowed for menu {}."
|
325
356
|
msgstr ""
|
326
357
|
|
358
|
+
#. module: shopfloor
|
359
|
+
#: model:ir.model.fields.selection,name:shopfloor.selection__shopfloor_menu__move_line_search_sort_order__custom_code
|
360
|
+
msgid "Custom code"
|
361
|
+
msgstr ""
|
362
|
+
|
363
|
+
#. module: shopfloor
|
364
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
365
|
+
msgid "Custom code to construct the key to sort move lines"
|
366
|
+
msgstr ""
|
367
|
+
|
368
|
+
#. module: shopfloor
|
369
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__move_line_search_sort_order_custom_code
|
370
|
+
msgid "Custom sort key code"
|
371
|
+
msgstr ""
|
372
|
+
|
373
|
+
#. module: shopfloor
|
374
|
+
#. odoo-python
|
375
|
+
#: code:addons/shopfloor/models/shopfloor_menu.py:0
|
376
|
+
#, python-format
|
377
|
+
msgid "Custom sort key code is only allowed when 'Custom code' is selected."
|
378
|
+
msgstr ""
|
379
|
+
|
380
|
+
#. module: shopfloor
|
381
|
+
#. odoo-python
|
382
|
+
#: code:addons/shopfloor/models/shopfloor_menu.py:0
|
383
|
+
#, python-format
|
384
|
+
msgid "Custom sort key code is required when 'Custom code' is selected."
|
385
|
+
msgstr ""
|
386
|
+
|
327
387
|
#. module: shopfloor
|
328
388
|
#: model:ir.model.fields,field_description:shopfloor.field_stock_move_line__date_planned
|
329
389
|
msgid "Date Scheduled"
|
@@ -460,6 +520,11 @@ msgstr ""
|
|
460
520
|
msgid "Lines have different destination location."
|
461
521
|
msgstr ""
|
462
522
|
|
523
|
+
#. module: shopfloor
|
524
|
+
#: model:ir.model.fields.selection,name:shopfloor.selection__shopfloor_menu__move_line_search_sort_order__location
|
525
|
+
msgid "Location"
|
526
|
+
msgstr ""
|
527
|
+
|
463
528
|
#. module: shopfloor
|
464
529
|
#. odoo-python
|
465
530
|
#: code:addons/shopfloor/actions/message.py:0
|
@@ -585,6 +650,16 @@ msgstr ""
|
|
585
650
|
msgid "Move Line Count"
|
586
651
|
msgstr ""
|
587
652
|
|
653
|
+
#. module: shopfloor
|
654
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__move_line_search_additional_domain_is_possible
|
655
|
+
msgid "Move Line Search Additional Domain Is Possible"
|
656
|
+
msgstr ""
|
657
|
+
|
658
|
+
#. module: shopfloor
|
659
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__move_line_search_sort_order_is_possible
|
660
|
+
msgid "Move Line Search Sort Order Is Possible"
|
661
|
+
msgstr ""
|
662
|
+
|
588
663
|
#. module: shopfloor
|
589
664
|
#. odoo-python
|
590
665
|
#: code:addons/shopfloor/actions/message.py:0
|
@@ -1086,6 +1161,11 @@ msgstr ""
|
|
1086
1161
|
msgid "Prepackaged Product Is Possible"
|
1087
1162
|
msgstr ""
|
1088
1163
|
|
1164
|
+
#. module: shopfloor
|
1165
|
+
#: model:ir.model.fields.selection,name:shopfloor.selection__shopfloor_menu__move_line_search_sort_order__priority
|
1166
|
+
msgid "Priority"
|
1167
|
+
msgstr ""
|
1168
|
+
|
1089
1169
|
#. module: shopfloor
|
1090
1170
|
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__allow_prepackaged_product
|
1091
1171
|
msgid "Process as pre-packaged"
|
@@ -1147,6 +1227,11 @@ msgstr ""
|
|
1147
1227
|
msgid "Product(s) processed as raw product(s)"
|
1148
1228
|
msgstr ""
|
1149
1229
|
|
1230
|
+
#. module: shopfloor
|
1231
|
+
#: model:ir.model.fields,help:shopfloor.field_shopfloor_menu__move_line_search_sort_order_custom_code
|
1232
|
+
msgid "Python code to sort move lines. "
|
1233
|
+
msgstr ""
|
1234
|
+
|
1150
1235
|
#. module: shopfloor
|
1151
1236
|
#: model:ir.model,name:shopfloor.model_stock_quant
|
1152
1237
|
msgid "Quants"
|
@@ -1421,6 +1506,11 @@ msgstr ""
|
|
1421
1506
|
msgid "Someone is already working on these transfers"
|
1422
1507
|
msgstr ""
|
1423
1508
|
|
1509
|
+
#. module: shopfloor
|
1510
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__move_line_search_sort_order
|
1511
|
+
msgid "Sort method used when searching move lines"
|
1512
|
+
msgstr ""
|
1513
|
+
|
1424
1514
|
#. module: shopfloor
|
1425
1515
|
#: model:ir.model.fields,field_description:shopfloor.field_stock_location__source_move_line_ids
|
1426
1516
|
msgid "Source Move Line"
|
@@ -1735,6 +1825,18 @@ msgstr ""
|
|
1735
1825
|
msgid "This transfer does not exist or is not available anymore."
|
1736
1826
|
msgstr ""
|
1737
1827
|
|
1828
|
+
#. module: shopfloor
|
1829
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
1830
|
+
msgid "Three variables are available:"
|
1831
|
+
msgstr ""
|
1832
|
+
|
1833
|
+
#. module: shopfloor
|
1834
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
1835
|
+
msgid ""
|
1836
|
+
"To assign the key value: <code>key = "
|
1837
|
+
"get_sort_key_assigned_to_current_user(line) + (line.x, )</code>"
|
1838
|
+
msgstr ""
|
1839
|
+
|
1738
1840
|
#. module: shopfloor
|
1739
1841
|
#: model:ir.model.fields,field_description:shopfloor.field_stock_picking__total_weight
|
1740
1842
|
#: model:ir.model.fields,field_description:shopfloor.field_stock_picking_batch__total_weight
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright 2024 ACSONE SA/NV (http://acsone.eu)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
import json
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from odoo import SUPERUSER_ID, api
|
8
|
+
|
9
|
+
_logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
def migrate(cr, version):
|
13
|
+
if not version:
|
14
|
+
return
|
15
|
+
env = api.Environment(cr, SUPERUSER_ID, {})
|
16
|
+
zone_picking = env.ref("shopfloor.scenario_zone_picking")
|
17
|
+
_update_scenario_options(zone_picking, sort_order=False, additional_domain=True)
|
18
|
+
location_content_transfer = env.ref("shopfloor.scenario_location_content_transfer")
|
19
|
+
_update_scenario_options(
|
20
|
+
location_content_transfer, sort_order=True, additional_domain=True
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
def _update_scenario_options(scenario, sort_order=True, additional_domain=True):
|
25
|
+
options = scenario.options
|
26
|
+
options["allow_move_line_search_sort_order"] = sort_order
|
27
|
+
options["allow_move_line_search_additional_domain"] = additional_domain
|
28
|
+
options_edit = json.dumps(options or {}, indent=4, sort_keys=True)
|
29
|
+
scenario.write({"options_edit": options_edit})
|
30
|
+
_logger.info(
|
31
|
+
"Option allow_alternative_destination_package added to scenario %s",
|
32
|
+
scenario.name,
|
33
|
+
)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# Copyright 2024 ACSONE SA/NV (http://www.acsone.eu)
|
2
3
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
4
|
from odoo import _, api, exceptions, fields, models
|
5
|
+
from odoo.tools.safe_eval import test_python_expr
|
4
6
|
|
5
7
|
PICK_PACK_SAME_TIME_HELP = """
|
6
8
|
If you tick this box, while picking goods from a location
|
@@ -227,6 +229,28 @@ class ShopfloorMenu(models.Model):
|
|
227
229
|
compute="_compute_allow_alternative_destination_package_is_possible"
|
228
230
|
)
|
229
231
|
|
232
|
+
move_line_search_additional_domain_is_possible = fields.Boolean(
|
233
|
+
compute="_compute_move_line_search_additional_domain_is_possible"
|
234
|
+
)
|
235
|
+
move_line_search_additional_domain = fields.Char(
|
236
|
+
string="Additional domain used when searching move lines"
|
237
|
+
)
|
238
|
+
move_line_search_sort_order_is_possible = fields.Boolean(
|
239
|
+
compute="_compute_move_line_search_sort_order_is_possible"
|
240
|
+
)
|
241
|
+
move_line_search_sort_order = fields.Selection(
|
242
|
+
selection=[
|
243
|
+
("priority", "Priority"),
|
244
|
+
("location", "Location"),
|
245
|
+
("custom_code", "Custom code"),
|
246
|
+
],
|
247
|
+
string="Sort method used when searching move lines",
|
248
|
+
default="priority",
|
249
|
+
)
|
250
|
+
move_line_search_sort_order_custom_code = fields.Text(
|
251
|
+
string="Custom sort key code", help="Python code to sort move lines. "
|
252
|
+
)
|
253
|
+
|
230
254
|
@api.onchange("unload_package_at_destination")
|
231
255
|
def _onchange_unload_package_at_destination(self):
|
232
256
|
# Uncheck pick_pack_same_time when unload_package_at_destination is set to True
|
@@ -458,3 +482,64 @@ class ShopfloorMenu(models.Model):
|
|
458
482
|
menu.allow_alternative_destination_package_is_possible = (
|
459
483
|
menu.scenario_id.has_option("allow_alternative_destination_package")
|
460
484
|
)
|
485
|
+
|
486
|
+
@api.depends("scenario_id")
|
487
|
+
def _compute_move_line_search_additional_domain_is_possible(self):
|
488
|
+
for menu in self:
|
489
|
+
menu.move_line_search_additional_domain_is_possible = (
|
490
|
+
menu.scenario_id.has_option("allow_move_line_search_additional_domain")
|
491
|
+
)
|
492
|
+
|
493
|
+
@api.depends("scenario_id")
|
494
|
+
def _compute_move_line_search_sort_order_is_possible(self):
|
495
|
+
for menu in self:
|
496
|
+
menu.move_line_search_sort_order_is_possible = menu.scenario_id.has_option(
|
497
|
+
"allow_move_line_search_sort_order"
|
498
|
+
)
|
499
|
+
|
500
|
+
@api.constrains(
|
501
|
+
"move_line_search_sort_order", "move_line_search_sort_order_custom_code"
|
502
|
+
)
|
503
|
+
def _check_move_line_search_sort_order_custom_code(self):
|
504
|
+
for menu in self:
|
505
|
+
if (
|
506
|
+
menu.move_line_search_sort_order == "custom_code"
|
507
|
+
and not menu.move_line_search_sort_order_custom_code
|
508
|
+
):
|
509
|
+
raise exceptions.ValidationError(
|
510
|
+
_(
|
511
|
+
"Custom sort key code is required when 'Custom code' is selected."
|
512
|
+
)
|
513
|
+
)
|
514
|
+
if (
|
515
|
+
menu.move_line_search_sort_order != "custom_code"
|
516
|
+
and menu.move_line_search_sort_order_custom_code
|
517
|
+
):
|
518
|
+
raise exceptions.ValidationError(
|
519
|
+
_(
|
520
|
+
"Custom sort key code is only allowed when 'Custom code' is selected."
|
521
|
+
)
|
522
|
+
)
|
523
|
+
code = (
|
524
|
+
menu.move_line_search_sort_order_custom_code
|
525
|
+
and menu.move_line_search_sort_order_custom_code.strip()
|
526
|
+
)
|
527
|
+
if code:
|
528
|
+
msg = test_python_expr(code, mode="exec")
|
529
|
+
if msg:
|
530
|
+
raise exceptions.ValidationError(msg)
|
531
|
+
|
532
|
+
@api.model_create_multi
|
533
|
+
def create(self, vals_list):
|
534
|
+
for vals in vals_list:
|
535
|
+
if vals.get("move_line_search_sort_order", "") != "custom_code":
|
536
|
+
vals["move_line_search_sort_order_custom_code"] = ""
|
537
|
+
return super().create(vals_list)
|
538
|
+
|
539
|
+
def write(self, vals):
|
540
|
+
if (
|
541
|
+
"move_line_search_sort_order" in vals
|
542
|
+
and vals["move_line_search_sort_order"] != "custom_code"
|
543
|
+
):
|
544
|
+
vals["move_line_search_sort_order_custom_code"] = ""
|
545
|
+
return super().write(vals)
|
@@ -339,8 +339,7 @@ class ClusterPicking(Component):
|
|
339
339
|
return self.prepare_unload(batch.id)
|
340
340
|
return self._response_for_start_line(next_line, message=message)
|
341
341
|
|
342
|
-
|
343
|
-
def _sort_key_lines(line):
|
342
|
+
def _sort_key_lines(self, line):
|
344
343
|
return (
|
345
344
|
line.shopfloor_priority or 10,
|
346
345
|
line.location_id.shopfloor_picking_sequence or "",
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# Copyright 2020-2022 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
|
3
3
|
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
4
4
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
5
|
-
from odoo import _
|
5
|
+
from odoo import _
|
6
|
+
from odoo.fields import first
|
6
7
|
|
7
8
|
from odoo.addons.base_rest.components.service import to_int
|
8
9
|
from odoo.addons.component.core import Component
|
@@ -208,24 +209,6 @@ class LocationContentTransfer(Component):
|
|
208
209
|
response = self._recover_started_picking()
|
209
210
|
return response or self._response_for_start()
|
210
211
|
|
211
|
-
def _find_location_move_lines_domain(self, location):
|
212
|
-
return [
|
213
|
-
("location_id", "=", location.id),
|
214
|
-
("qty_done", "=", 0),
|
215
|
-
("state", "in", ("assigned", "partially_available")),
|
216
|
-
("picking_id.user_id", "in", (False, self.env.uid)),
|
217
|
-
("picking_id.state", "=", "assigned"),
|
218
|
-
]
|
219
|
-
|
220
|
-
def _find_location_move_lines_from_scan_location(self, *args, **kwargs):
|
221
|
-
return self._find_location_move_lines(*args, **kwargs)
|
222
|
-
|
223
|
-
def _find_location_move_lines(self, location):
|
224
|
-
"""Find lines that potentially are to move in the location"""
|
225
|
-
return self.env["stock.move.line"].search(
|
226
|
-
self._find_location_move_lines_domain(location)
|
227
|
-
)
|
228
|
-
|
229
212
|
def _create_moves_from_location(self, location):
|
230
213
|
# get all quants from the scanned location
|
231
214
|
quants = self.env["stock.quant"].search(
|
@@ -251,24 +234,12 @@ class LocationContentTransfer(Component):
|
|
251
234
|
return self.env["stock.move"].create(move_vals_list)
|
252
235
|
|
253
236
|
def _find_location_to_work_from(self):
|
254
|
-
|
255
|
-
|
256
|
-
[
|
257
|
-
("picking_type_id", "in", self.picking_types.ids),
|
258
|
-
("state", "=", "assigned"),
|
259
|
-
("user_id", "in", (False, self.env.user.id)),
|
260
|
-
],
|
261
|
-
order="user_id, priority desc, scheduled_date asc, id desc",
|
262
|
-
)
|
237
|
+
move_lines = self.search_move_line.search_move_lines(match_user=True)
|
238
|
+
return first(move_lines).location_id
|
263
239
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
)
|
268
|
-
location = fields.first(move_lines).location_id
|
269
|
-
if location:
|
270
|
-
break
|
271
|
-
return location
|
240
|
+
def _select_move_lines_first_location(self, move_lines):
|
241
|
+
location = first(move_lines).location_id
|
242
|
+
return move_lines.filtered(lambda line: line.location_id == location)
|
272
243
|
|
273
244
|
def find_work(self):
|
274
245
|
"""Find the next location to work from, for a user.
|
@@ -285,14 +256,15 @@ class LocationContentTransfer(Component):
|
|
285
256
|
response = self._recover_started_picking()
|
286
257
|
if response:
|
287
258
|
return response
|
259
|
+
|
288
260
|
self._actions_for("lock").advisory(self._advisory_lock_find_work)
|
289
|
-
|
290
|
-
if not
|
261
|
+
move_lines = self.search_move_line.search_move_lines(match_user=True)
|
262
|
+
if not move_lines:
|
291
263
|
return self._response_for_start(message=self.msg_store.no_work_found())
|
292
|
-
move_lines = self.
|
264
|
+
move_lines = self._select_move_lines_first_location(move_lines)
|
293
265
|
stock = self._actions_for("stock")
|
294
266
|
stock.mark_move_line_as_picked(move_lines, quantity=0)
|
295
|
-
return self._response_for_scan_location(location=
|
267
|
+
return self._response_for_scan_location(location=move_lines.location_id)
|
296
268
|
|
297
269
|
def _find_move_lines_to_cancel_work(self, location):
|
298
270
|
unreserve = self._actions_for("stock.unreserve")
|
@@ -356,7 +328,14 @@ class LocationContentTransfer(Component):
|
|
356
328
|
message=self.msg_store.cannot_move_something_in_picking_type()
|
357
329
|
)
|
358
330
|
|
359
|
-
move_lines = self.
|
331
|
+
move_lines = self.search_move_line.search_move_lines(
|
332
|
+
locations=location,
|
333
|
+
match_user=True,
|
334
|
+
picking_type=self.env[
|
335
|
+
"stock.picking.type"
|
336
|
+
], # disable filtering on picking types
|
337
|
+
)
|
338
|
+
move_lines = self._select_move_lines_first_location(move_lines)
|
360
339
|
|
361
340
|
savepoint = self._actions_for("savepoint").new()
|
362
341
|
unreserve = self._actions_for("stock.unreserve")
|