odoo-addon-shopfloor 16.0.2.2.0__py3-none-any.whl → 16.0.2.2.1.5__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/ca.po +106 -4
- odoo/addons/shopfloor/i18n/de.po +104 -2
- odoo/addons/shopfloor/i18n/es_AR.po +108 -6
- odoo/addons/shopfloor/i18n/it.po +108 -6
- odoo/addons/shopfloor/i18n/pt_BR.po +104 -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.5.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.5.dist-info}/RECORD +28 -25
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.5.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.2.2.0.dist-info → odoo_addon_shopfloor-16.0.2.2.1.5.dist-info}/top_level.txt +0 -0
@@ -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)
|
@@ -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")
|
@@ -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)
|