odoo-addon-shopfloor 16.0.1.0.0.25__py3-none-any.whl → 16.0.2.1.0__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/data.py +69 -34
- odoo/addons/shopfloor/actions/data_detail.py +20 -0
- odoo/addons/shopfloor/actions/message.py +94 -2
- odoo/addons/shopfloor/actions/move_line_search.py +2 -2
- odoo/addons/shopfloor/actions/packaging.py +10 -0
- odoo/addons/shopfloor/actions/schema.py +11 -0
- odoo/addons/shopfloor/actions/schema_detail.py +14 -8
- odoo/addons/shopfloor/actions/search.py +9 -6
- odoo/addons/shopfloor/components/scan_handler_product.py +2 -0
- odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +4 -2
- odoo/addons/shopfloor/docs/checkout_diag_seq.plantuml +19 -5
- odoo/addons/shopfloor/docs/checkout_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.plantuml +4 -4
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.png +0 -0
- odoo/addons/shopfloor/i18n/ca.po +26 -8
- odoo/addons/shopfloor/i18n/de.po +26 -8
- odoo/addons/shopfloor/i18n/es_AR.po +36 -10
- odoo/addons/shopfloor/i18n/it.po +628 -491
- odoo/addons/shopfloor/i18n/pt_BR.po +26 -8
- odoo/addons/shopfloor/i18n/shopfloor.pot +133 -8
- odoo/addons/shopfloor/migrations/16.0.2.0.0/post-migration.py +43 -0
- odoo/addons/shopfloor/models/shopfloor_menu.py +29 -5
- odoo/addons/shopfloor/models/stock_move_line.py +3 -0
- odoo/addons/shopfloor/models/stock_picking.py +11 -0
- odoo/addons/shopfloor/services/checkout.py +216 -61
- odoo/addons/shopfloor/services/cluster_picking.py +33 -18
- odoo/addons/shopfloor/services/delivery.py +25 -7
- odoo/addons/shopfloor/services/location_content_transfer.py +42 -28
- odoo/addons/shopfloor/services/single_pack_transfer.py +36 -13
- odoo/addons/shopfloor/services/zone_picking.py +187 -67
- odoo/addons/shopfloor/static/description/index.html +1 -1
- odoo/addons/shopfloor/tests/__init__.py +2 -0
- odoo/addons/shopfloor/tests/common.py +3 -1
- odoo/addons/shopfloor/tests/test_actions_data.py +46 -7
- odoo/addons/shopfloor/tests/test_actions_data_base.py +15 -0
- odoo/addons/shopfloor/tests/test_actions_data_detail.py +30 -8
- odoo/addons/shopfloor/tests/test_actions_packaging.py +43 -0
- odoo/addons/shopfloor/tests/test_checkout_base.py +15 -5
- odoo/addons/shopfloor/tests/test_checkout_done.py +40 -5
- odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +1 -0
- odoo/addons/shopfloor/tests/test_checkout_list_package.py +3 -1
- odoo/addons/shopfloor/tests/test_checkout_scan.py +19 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_dest_location.py +99 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line.py +3 -2
- odoo/addons/shopfloor/tests/test_checkout_scan_line_no_prefill_qty.py +48 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +26 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action_no_prefill_qty.py +16 -0
- odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +4 -1
- odoo/addons/shopfloor/tests/test_checkout_summary.py +1 -1
- odoo/addons/shopfloor/tests/test_cluster_picking_unload.py +37 -8
- odoo/addons/shopfloor/tests/test_delivery_list_stock_picking.py +5 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +4 -4
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_all.py +24 -2
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +6 -4
- odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +45 -0
- odoo/addons/shopfloor/tests/test_scan_anything.py +7 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer.py +59 -8
- odoo/addons/shopfloor/tests/test_zone_picking_base.py +36 -10
- odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +2 -0
- odoo/addons/shopfloor/tests/test_zone_picking_complete_mix_pack_flux.py +59 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +33 -2
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py +8 -3
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_no_prefill_qty.py +19 -2
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +88 -19
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_package_not_allowed.py +94 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +1 -5
- odoo/addons/shopfloor/tests/test_zone_picking_start.py +4 -4
- odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +1 -1
- odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +4 -4
- odoo/addons/shopfloor/views/shopfloor_menu.xml +30 -0
- {odoo_addon_shopfloor-16.0.1.0.0.25.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.1.0.0.25.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/RECORD +76 -71
- {odoo_addon_shopfloor-16.0.1.0.0.25.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.1.0.0.25.dist-info → odoo_addon_shopfloor-16.0.2.1.0.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:835c681f1a1e049cfb7792d6dd69c0214bfbaad4775d134f6e5a47900e8aeb4a
|
11
11
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
12
12
|
|
13
13
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
@@ -13,34 +13,23 @@ class DataAction(Component):
|
|
13
13
|
def location(self, record, **kw):
|
14
14
|
parser = self._location_parser
|
15
15
|
data = self._jsonify(record.with_context(location=record.id), parser, **kw)
|
16
|
-
if "with_operation_progress"
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def _get_location_operations_progress(self, location):
|
25
|
-
lines = self.env["stock.move.line"].search(
|
26
|
-
[
|
27
|
-
("location_id", "=", location.id),
|
16
|
+
if kw.get("with_operation_progress"):
|
17
|
+
lines_blacklist = (
|
18
|
+
kw.get("progress_lines_blacklist")
|
19
|
+
or self.env["stock.move.line"].browse()
|
20
|
+
)
|
21
|
+
domain = [
|
22
|
+
("location_id", "=", record.id),
|
28
23
|
("state", "in", ["partially_available", "assigned"]),
|
29
24
|
("picking_id.state", "=", "assigned"),
|
25
|
+
("id", "not in", lines_blacklist.ids),
|
30
26
|
]
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
for line in lines:
|
38
|
-
operations_done += line.qty_done if not line.package_id else 1
|
39
|
-
operations_to_do += line.reserved_uom_qty if not line.package_id else 1
|
40
|
-
return {
|
41
|
-
"done": operations_done,
|
42
|
-
"to_do": operations_to_do,
|
43
|
-
}
|
27
|
+
operation_progress = self._get_operation_progress(domain)
|
28
|
+
data.update({"operation_progress": operation_progress})
|
29
|
+
return data
|
30
|
+
|
31
|
+
def locations(self, records, **kw):
|
32
|
+
return [self.location(rec, **kw) for rec in records]
|
44
33
|
|
45
34
|
@property
|
46
35
|
def _location_parser(self):
|
@@ -51,16 +40,19 @@ class DataAction(Component):
|
|
51
40
|
("barcode", lambda rec, fname: rec[fname] if rec[fname] else rec.name),
|
52
41
|
]
|
53
42
|
|
54
|
-
|
55
|
-
def picking(self, record, **kw):
|
43
|
+
def _get_picking_parser(self, record, **kw):
|
56
44
|
parser = self._picking_parser
|
57
45
|
# progress is a heavy computed field,
|
58
46
|
# and it may reduce performance significatively
|
59
47
|
# when dealing with a large number of pickings.
|
60
48
|
# Thus, we make it optional.
|
61
|
-
if "with_progress"
|
49
|
+
if kw.get("with_progress"):
|
62
50
|
parser.append("progress")
|
63
|
-
return
|
51
|
+
return parser
|
52
|
+
|
53
|
+
@ensure_model("stock.picking")
|
54
|
+
def picking(self, record, **kw):
|
55
|
+
return self._jsonify(record, self._get_picking_parser(record, **kw), **kw)
|
64
56
|
|
65
57
|
def pickings(self, record, **kw):
|
66
58
|
return self.picking(record, multi=True)
|
@@ -80,6 +72,7 @@ class DataAction(Component):
|
|
80
72
|
"bulk_line_count",
|
81
73
|
"total_weight:weight",
|
82
74
|
"scheduled_date",
|
75
|
+
"priority",
|
83
76
|
]
|
84
77
|
|
85
78
|
@ensure_model("stock.quant.package")
|
@@ -93,13 +86,37 @@ class DataAction(Component):
|
|
93
86
|
if with_packaging:
|
94
87
|
parser += self._package_packaging_parser
|
95
88
|
data = self._jsonify(record, parser, **kw)
|
89
|
+
qty = len(record.quant_ids)
|
96
90
|
# handle special cases
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
91
|
+
progress_package_key = ""
|
92
|
+
if kw.get("with_operation_progress_src"):
|
93
|
+
progress_package_key = "package_id"
|
94
|
+
elif kw.get("with_operation_progress_dest"):
|
95
|
+
progress_package_key = "result_package_id"
|
96
|
+
if progress_package_key:
|
97
|
+
lines_blacklist = (
|
98
|
+
kw.get("progress_lines_blacklist")
|
99
|
+
or self.env["stock.move.line"].browse()
|
101
100
|
)
|
102
|
-
|
101
|
+
domain = [
|
102
|
+
(progress_package_key, "=", record.id),
|
103
|
+
("state", "in", ["partially_available", "assigned"]),
|
104
|
+
("picking_id.state", "=", "assigned"),
|
105
|
+
("id", "not in", lines_blacklist.ids),
|
106
|
+
]
|
107
|
+
operation_progress = self._get_operation_progress(domain)
|
108
|
+
data.update({"operation_progress": operation_progress})
|
109
|
+
if kw.get("with_package_move_line_count") and data and picking:
|
110
|
+
move_line_count = self.env["stock.move.line"].search_count(
|
111
|
+
[
|
112
|
+
("picking_id.picking_type_id", "=", picking.picking_type_id.id),
|
113
|
+
("result_package_id", "=", record.id),
|
114
|
+
("state", "in", ["partially_available", "assigned"]),
|
115
|
+
]
|
116
|
+
)
|
117
|
+
qty += move_line_count
|
118
|
+
# TODO does this name really makes sense?
|
119
|
+
data.update({"move_line_count": qty})
|
103
120
|
return data
|
104
121
|
|
105
122
|
def packages(self, records, picking=None, **kw):
|
@@ -112,6 +129,10 @@ class DataAction(Component):
|
|
112
129
|
"name",
|
113
130
|
"shopfloor_weight:weight",
|
114
131
|
("package_type_id:storage_type", ["id", "name"]),
|
132
|
+
(
|
133
|
+
"quant_ids:total_quantity",
|
134
|
+
lambda rec, fname: sum(rec.quant_ids.mapped("quantity")),
|
135
|
+
),
|
115
136
|
]
|
116
137
|
|
117
138
|
@property
|
@@ -327,3 +348,17 @@ class DataAction(Component):
|
|
327
348
|
"id",
|
328
349
|
"name",
|
329
350
|
]
|
351
|
+
|
352
|
+
def _get_operation_progress(self, domain):
|
353
|
+
lines = self.env["stock.move.line"].search(domain)
|
354
|
+
# operations_to_do = number of total operations that are pending for this location.
|
355
|
+
# operations_done = number of operations already done.
|
356
|
+
operations_to_do = 0
|
357
|
+
operations_done = 0
|
358
|
+
for line in lines:
|
359
|
+
operations_done += line.qty_done
|
360
|
+
operations_to_do += line.reserved_uom_qty - line.qty_done
|
361
|
+
return {
|
362
|
+
"done": operations_done,
|
363
|
+
"to_do": operations_to_do,
|
364
|
+
}
|
@@ -152,3 +152,23 @@ class DataDetailAction(Component):
|
|
152
152
|
"product_name",
|
153
153
|
"product_code",
|
154
154
|
]
|
155
|
+
|
156
|
+
@ensure_model("product.packaging")
|
157
|
+
def packaging_detail(self, record, **kw):
|
158
|
+
return self._jsonify(
|
159
|
+
record.with_context(packaging=record.id),
|
160
|
+
self._packaging_detail_parser,
|
161
|
+
**kw
|
162
|
+
)
|
163
|
+
|
164
|
+
@property
|
165
|
+
def _packaging_detail_parser(self):
|
166
|
+
return self._packaging_parser + [
|
167
|
+
"packaging_length:length",
|
168
|
+
"width",
|
169
|
+
"height",
|
170
|
+
("max_weight", lambda rec, fname: rec.package_type_id.max_weight),
|
171
|
+
"length_uom_name:length_uom",
|
172
|
+
"weight_uom_name:weight_uom",
|
173
|
+
"barcode:barcode",
|
174
|
+
]
|
@@ -113,8 +113,10 @@ class MessageAction(Component):
|
|
113
113
|
return {
|
114
114
|
"message_type": "warning",
|
115
115
|
"body": _(
|
116
|
-
"Package %(package_name)s is not available in transfer %(picking_name)s."
|
117
|
-
|
116
|
+
"Package %(package_name)s is not available in transfer %(picking_name)s.",
|
117
|
+
package_name=package.name,
|
118
|
+
picking_name=picking.name,
|
119
|
+
),
|
118
120
|
}
|
119
121
|
|
120
122
|
def package_not_empty(self, package):
|
@@ -129,6 +131,17 @@ class MessageAction(Component):
|
|
129
131
|
"body": _("Package {} is already used.").format(package.name),
|
130
132
|
}
|
131
133
|
|
134
|
+
def package_different_picking_type(self, package, picking_type):
|
135
|
+
return {
|
136
|
+
"message_type": "warning",
|
137
|
+
"body": _(
|
138
|
+
"Package %(package_name)s contains already lines from a different "
|
139
|
+
"operation type %(picking_type_name)s.",
|
140
|
+
package_name=package.name,
|
141
|
+
picking_type_name=picking_type.name,
|
142
|
+
),
|
143
|
+
}
|
144
|
+
|
132
145
|
def dest_package_required(self):
|
133
146
|
return {
|
134
147
|
"message_type": "warning",
|
@@ -435,6 +448,18 @@ class MessageAction(Component):
|
|
435
448
|
"body": _("No transfer found for this product."),
|
436
449
|
}
|
437
450
|
|
451
|
+
def product_not_found_in_location_or_transfer(self, product, location, picking):
|
452
|
+
return {
|
453
|
+
"message_type": "error",
|
454
|
+
"body": _(
|
455
|
+
"Product %(product_name)s not found in location %(location_name)s "
|
456
|
+
"or transfer %(picking_name)s.",
|
457
|
+
product_name=product.name,
|
458
|
+
location_name=location.name,
|
459
|
+
picking_name=picking.name,
|
460
|
+
),
|
461
|
+
}
|
462
|
+
|
438
463
|
def x_not_found_or_already_in_dest_package(self, message_code):
|
439
464
|
return {
|
440
465
|
"message_type": "warning",
|
@@ -449,6 +474,12 @@ class MessageAction(Component):
|
|
449
474
|
"body": _("Packaging not found in the current transfer."),
|
450
475
|
}
|
451
476
|
|
477
|
+
def packaging_dimension_updated(self, packaging):
|
478
|
+
return {
|
479
|
+
"message_type": "success",
|
480
|
+
"body": _("Packaging {} dimension updated.").format(packaging.name),
|
481
|
+
}
|
482
|
+
|
452
483
|
def expiration_date_missing(self):
|
453
484
|
return {
|
454
485
|
"message_type": "error",
|
@@ -512,6 +543,26 @@ class MessageAction(Component):
|
|
512
543
|
"body": _("No transfer found for this lot."),
|
513
544
|
}
|
514
545
|
|
546
|
+
def lot_not_found_in_location(self, lot, location):
|
547
|
+
return {
|
548
|
+
"message_type": "error",
|
549
|
+
"body": _(
|
550
|
+
"Lot %(lot_name)s not found in location %(location_name)s",
|
551
|
+
lot_name=lot.name,
|
552
|
+
location_name=location.name,
|
553
|
+
),
|
554
|
+
}
|
555
|
+
|
556
|
+
def lot_not_found_in_picking(self, lot, picking):
|
557
|
+
return {
|
558
|
+
"message_type": "error",
|
559
|
+
"body": _(
|
560
|
+
"Lot %(lot_name)s not found in transfer %(picking_name)s",
|
561
|
+
lot_name=lot.name,
|
562
|
+
pcking_name=picking.name,
|
563
|
+
),
|
564
|
+
}
|
565
|
+
|
515
566
|
def batch_transfer_complete(self):
|
516
567
|
return {
|
517
568
|
"message_type": "success",
|
@@ -662,6 +713,12 @@ class MessageAction(Component):
|
|
662
713
|
"body": _("No lines to process."),
|
663
714
|
}
|
664
715
|
|
716
|
+
def no_lines_to_process_set_quantities(self):
|
717
|
+
return {
|
718
|
+
"message_type": "info",
|
719
|
+
"body": _("No lines to process, set quantities on some"),
|
720
|
+
}
|
721
|
+
|
665
722
|
def location_empty(self, location):
|
666
723
|
return {
|
667
724
|
"message_type": "error",
|
@@ -686,6 +743,12 @@ class MessageAction(Component):
|
|
686
743
|
"body": _("You must not pick more than {} units.").format(quantity),
|
687
744
|
}
|
688
745
|
|
746
|
+
def unable_to_pick_qty(self):
|
747
|
+
return {
|
748
|
+
"message_type": "error",
|
749
|
+
"body": _("You cannot process that much units."),
|
750
|
+
}
|
751
|
+
|
689
752
|
def lot_replaced_by_lot(self, old_lot, new_lot):
|
690
753
|
return {
|
691
754
|
"message_type": "success",
|
@@ -729,6 +792,26 @@ class MessageAction(Component):
|
|
729
792
|
),
|
730
793
|
}
|
731
794
|
|
795
|
+
def package_not_found_in_location(self, package, location):
|
796
|
+
return {
|
797
|
+
"message_type": "error",
|
798
|
+
"body": _(
|
799
|
+
"Package %(package_name)s not found in location %(location_name)s",
|
800
|
+
package_name=package.name,
|
801
|
+
location_name=location.name,
|
802
|
+
),
|
803
|
+
}
|
804
|
+
|
805
|
+
def package_not_found_in_picking(self, package, picking):
|
806
|
+
return {
|
807
|
+
"message_type": "error",
|
808
|
+
"body": _(
|
809
|
+
"Package %(package_name)s not found in transfer %(picking_name)s",
|
810
|
+
package_name=package.name,
|
811
|
+
picking_name=picking.name,
|
812
|
+
),
|
813
|
+
}
|
814
|
+
|
732
815
|
def cannot_change_lot_already_picked(self, lot):
|
733
816
|
return {
|
734
817
|
"message_type": "error",
|
@@ -844,3 +927,12 @@ class MessageAction(Component):
|
|
844
927
|
"message_type": "warning",
|
845
928
|
"body": _("No line to pack found."),
|
846
929
|
}
|
930
|
+
|
931
|
+
def package_transfer_not_allowed_scan_location(self):
|
932
|
+
return {
|
933
|
+
"message_type": "warning",
|
934
|
+
"body": _(
|
935
|
+
"Transferring to a different package is not allowed, "
|
936
|
+
"please scan a location instead."
|
937
|
+
),
|
938
|
+
}
|
@@ -101,8 +101,8 @@ class MoveLineSearch(Component):
|
|
101
101
|
)
|
102
102
|
elif order == "location":
|
103
103
|
return lambda line: (
|
104
|
-
line.
|
105
|
-
line.
|
104
|
+
line.location_id.shopfloor_picking_sequence or "",
|
105
|
+
line.location_id.name,
|
106
106
|
line.move_id.date,
|
107
107
|
line.move_id.id,
|
108
108
|
)
|
@@ -57,3 +57,13 @@ class PackagingAction(Component):
|
|
57
57
|
|
58
58
|
def package_has_several_lots(self, package):
|
59
59
|
return len(package.quant_ids.lot_id) > 1
|
60
|
+
|
61
|
+
def is_complete_mix_pack(self, package):
|
62
|
+
"""Check if a package is mixed and completely reserved.
|
63
|
+
|
64
|
+
Will return true if the package has multiple distinct products and
|
65
|
+
all the package quantities are reserved.
|
66
|
+
"""
|
67
|
+
return self.package_has_several_products(package) and all(
|
68
|
+
quant.quantity == quant.reserved_quantity for quant in package.quant_ids
|
69
|
+
)
|
@@ -26,6 +26,8 @@ class ShopfloorSchemaAction(Component):
|
|
26
26
|
"ship_carrier": self._schema_dict_of(self._simple_record(), required=False),
|
27
27
|
"scheduled_date": {"type": "string", "nullable": False, "required": True},
|
28
28
|
"progress": {"type": "float", "nullable": True},
|
29
|
+
"location_dest": self._schema_dict_of(self.location(), required=False),
|
30
|
+
"priority": {"type": "string", "nullable": True, "required": False},
|
29
31
|
}
|
30
32
|
|
31
33
|
def move_line(self, with_packaging=False, with_picking=False):
|
@@ -91,6 +93,15 @@ class ShopfloorSchemaAction(Component):
|
|
91
93
|
"weight": {"required": True, "nullable": True, "type": "float"},
|
92
94
|
"move_line_count": {"required": False, "nullable": True, "type": "integer"},
|
93
95
|
"storage_type": self._schema_dict_of(self._simple_record()),
|
96
|
+
"operation_progress": {
|
97
|
+
"type": "dict",
|
98
|
+
"required": False,
|
99
|
+
"schema": {
|
100
|
+
"done": {"type": "float", "required": False},
|
101
|
+
"to_do": {"type": "float", "required": False},
|
102
|
+
},
|
103
|
+
},
|
104
|
+
"total_quantity": {"required": False, "type": "float"},
|
94
105
|
}
|
95
106
|
if with_packaging:
|
96
107
|
schema["packaging"] = self._schema_dict_of(self.packaging())
|
@@ -88,11 +88,17 @@ class ShopfloorSchemaDetailAction(Component):
|
|
88
88
|
"product_code": {"type": "string", "nullable": True, "required": False},
|
89
89
|
}
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
def packaging_detail(self):
|
92
|
+
schema = self.packaging()
|
93
|
+
schema.update(
|
94
|
+
{
|
95
|
+
"length": {"type": "float", "nullable": True, "required": False},
|
96
|
+
"width": {"type": "float", "nullable": True, "required": False},
|
97
|
+
"height": {"type": "float", "nullable": True, "required": False},
|
98
|
+
"max_weight": {"type": "float", "nullable": True, "required": False},
|
99
|
+
"length_uom": {"type": "string", "nullable": True, "required": False},
|
100
|
+
"weight_uom": {"type": "string", "nullable": True, "required": False},
|
101
|
+
"barcode": {"type": "string", "nullable": True, "required": False},
|
102
|
+
}
|
103
|
+
)
|
104
|
+
return schema
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# Copyright 2024 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
2
3
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
4
|
|
4
5
|
from odoo.osv.expression import AND
|
@@ -74,18 +75,20 @@ class SearchAction(Component):
|
|
74
75
|
barcode = barcode or ""
|
75
76
|
return self.generic_find(barcode, types=types, handler_kw=handler_kw)
|
76
77
|
|
77
|
-
def
|
78
|
+
def _find_record_by_type(self, barcode, btype, handler_kw=None):
|
78
79
|
handler_kw = handler_kw or {}
|
80
|
+
handler = self._barcode_type_handler.get(btype)
|
81
|
+
if not handler:
|
82
|
+
return
|
83
|
+
return handler(barcode, **handler_kw.get(btype, {}))
|
84
|
+
|
85
|
+
def generic_find(self, barcode, types=None, handler_kw=None):
|
79
86
|
_types = types or self._barcode_type_handler.keys()
|
80
87
|
# TODO: decide the best default order in case we don't pass `types`
|
81
88
|
for btype in _types:
|
82
|
-
|
83
|
-
if not handler:
|
84
|
-
continue
|
85
|
-
record = handler(barcode, **handler_kw.get(btype, {}))
|
89
|
+
record = self._find_record_by_type(barcode, btype, handler_kw)
|
86
90
|
if record:
|
87
91
|
return self._make_search_result(record=record, code=barcode, type=btype)
|
88
|
-
|
89
92
|
return self._make_search_result(type="none")
|
90
93
|
|
91
94
|
def location_from_scan(self, barcode, limit=1):
|
@@ -16,6 +16,8 @@ class ProductHandler(Component):
|
|
16
16
|
|
17
17
|
def search(self, identifier):
|
18
18
|
res = self._search.find(identifier, types=("product", "packaging"))
|
19
|
+
if res.record and res.type == "packaging":
|
20
|
+
return res.record.product_id
|
19
21
|
return res.record if res.record else self.env["product.product"]
|
20
22
|
|
21
23
|
@property
|
@@ -20,7 +20,8 @@
|
|
20
20
|
"unload_package_at_destination": true,
|
21
21
|
"multiple_move_single_pack": true,
|
22
22
|
"no_prefill_qty": true,
|
23
|
-
"scan_location_or_pack_first": true
|
23
|
+
"scan_location_or_pack_first": true,
|
24
|
+
"allow_alternative_destination_package": true
|
24
25
|
}
|
25
26
|
</field>
|
26
27
|
</record>
|
@@ -43,7 +44,8 @@
|
|
43
44
|
{
|
44
45
|
"no_prefill_qty": true,
|
45
46
|
"show_oneline_package_content": true,
|
46
|
-
"auto_post_line": true
|
47
|
+
"auto_post_line": true,
|
48
|
+
"scan_location_or_pack_first": true
|
47
49
|
}
|
48
50
|
</field>
|
49
51
|
</record>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Diagram to generate with PlantUML (https://plantuml.com/)
|
2
2
|
#
|
3
3
|
# $ sudo apt install plantuml
|
4
|
-
# $ plantuml
|
4
|
+
# $ plantuml checkout_diag_seq.plantuml
|
5
5
|
#
|
6
6
|
|
7
7
|
@startuml
|
@@ -32,18 +32,29 @@ manual_selection -> select_line: **/select**(picking_id)
|
|
32
32
|
select_document -> select_line: **/scan_document**(barcode, picking_id=None) \n(scan a picking a product or ???)
|
33
33
|
|
34
34
|
== /select_line ==
|
35
|
-
select_line -> select_package: **/select_line**(picking_id, package_id,
|
35
|
+
select_line -> select_package: **/select_line**(picking_id, package_id, move_line_id)
|
36
|
+
|
37
|
+
== /scan_line ==
|
38
|
+
select_line -> select_package: **/scan_line**(picking_id, barcode, confirm_pack_all, confirm_lot)
|
36
39
|
|
37
40
|
== /list_delivery_packaging ==
|
38
41
|
select_package -> select_delivery_packaging: **/list_delivery_packaging**(picking_id, selected_line_ids)
|
39
42
|
|
40
|
-
|
41
43
|
== /list_dest_package ==
|
42
44
|
select_package -> select_dest_package: **/list_dest_package**(picking_id, selected_line_ids)
|
43
45
|
select_package -> select_package: **/list_dest_package**(picking_id, selected_line_ids) \n No Valid package to select
|
44
46
|
|
47
|
+
== edit line ==
|
48
|
+
select_package -> change_quantity: (State change without call to backend)
|
49
|
+
|
45
50
|
== /set_dest_package ==
|
46
|
-
select_dest_package -> select_line: **/set_dest_package(picking_id, selected_line_ids, package_id)
|
51
|
+
select_dest_package -> select_line: **/set_dest_package**(picking_id, selected_line_ids, package_id)
|
52
|
+
|
53
|
+
== /set_custom_qty ==
|
54
|
+
change_quantity -> select_package: **/set_custom_qty**(picking_id, selected_line_ids, move_line_id, qty_done)
|
55
|
+
|
56
|
+
== /reset_line_qty ==
|
57
|
+
select_package -> select_package: **/reset_line_qty**(picking_id, selected_line_ids, move_line_id)
|
47
58
|
|
48
59
|
== /scan_package_action ==
|
49
60
|
select_delivery_packaging -> summary: **/scan_package_action**(picking_id, selected_line_ids, barcode)
|
@@ -52,10 +63,13 @@ select_delivery_packaging -> summary: **/scan_package_action**(picking_id, selec
|
|
52
63
|
summary -> select_line: **/cancel_line**(picking_id, package_id=None, line_id=None)
|
53
64
|
|
54
65
|
== /select ==
|
55
|
-
|
66
|
+
summary -> select_line: **/select**(picking_id) \nContinue checkout
|
56
67
|
|
57
68
|
== /done ==
|
58
69
|
summary -> select_document: **/done(picking_id)**
|
70
|
+
summary -> select_child_location: **/done(picking_id)**
|
59
71
|
|
72
|
+
== /scan_dest_location ==
|
73
|
+
select_child_location -> select_document: **/scan_dest_location(picking_id, barcode)**
|
60
74
|
|
61
75
|
@enduml
|
Binary file
|
@@ -23,14 +23,14 @@ header <img:oca_logo.png>
|
|
23
23
|
title <size:35>Single Pack Transfer scenario</size>
|
24
24
|
|
25
25
|
== /start ==
|
26
|
-
start -> scan_location: **/start**(barcode[pack|location], confirmation=
|
27
|
-
start -> start: **/start**(barcode[pack|location], confirmation=
|
26
|
+
start -> scan_location: **/start**(barcode[pack|location], confirmation=None)
|
27
|
+
start -> start: **/start**(barcode[pack|location], confirmation=None)
|
28
28
|
|
29
29
|
== /cancel ==
|
30
30
|
scan_location -> start: **/cancel**(package_level_id)
|
31
31
|
|
32
32
|
== /validate ==
|
33
|
-
scan_location -> scan_location: **/validate**(package_level_id, location_barcode, confirmation=
|
34
|
-
scan_location -> start: **/validate**(package_level_id, location_barcode, confirmation=
|
33
|
+
scan_location -> scan_location: **/validate**(package_level_id, location_barcode, confirmation=None)
|
34
|
+
scan_location -> start: **/validate**(package_level_id, location_barcode, confirmation=None)
|
35
35
|
|
36
36
|
@enduml
|
Binary file
|
odoo/addons/shopfloor/i18n/ca.po
CHANGED
@@ -897,6 +897,12 @@ msgstr ""
|
|
897
897
|
msgid "Packaging not found in the current transfer."
|
898
898
|
msgstr ""
|
899
899
|
|
900
|
+
#. module: shopfloor
|
901
|
+
#: code:addons/shopfloor/actions/message.py:0
|
902
|
+
#, python-format
|
903
|
+
msgid "Packaging {} dimension updated."
|
904
|
+
msgstr ""
|
905
|
+
|
900
906
|
#. module: shopfloor
|
901
907
|
#: code:addons/shopfloor/actions/message.py:0
|
902
908
|
#, python-format
|
@@ -1063,6 +1069,11 @@ msgstr ""
|
|
1063
1069
|
msgid "Restart the operation, someone has canceled it."
|
1064
1070
|
msgstr ""
|
1065
1071
|
|
1072
|
+
#. module: shopfloor
|
1073
|
+
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__scan_location_or_pack_first
|
1074
|
+
msgid "Restrict scannable barcode at work selection"
|
1075
|
+
msgstr ""
|
1076
|
+
|
1066
1077
|
#. module: shopfloor
|
1067
1078
|
#: model_terms:ir.ui.view,arch_db:shopfloor.view_stock_move_line_detailed_operation_tree
|
1068
1079
|
msgid "SF Priority"
|
@@ -1094,11 +1105,6 @@ msgstr ""
|
|
1094
1105
|
msgid "Scan Location Or Pack First Is Possible"
|
1095
1106
|
msgstr ""
|
1096
1107
|
|
1097
|
-
#. module: shopfloor
|
1098
|
-
#: model:ir.model.fields,field_description:shopfloor.field_shopfloor_menu__scan_location_or_pack_first
|
1099
|
-
msgid "Scan first location or pack"
|
1100
|
-
msgstr ""
|
1101
|
-
|
1102
1108
|
#. module: shopfloor
|
1103
1109
|
#: code:addons/shopfloor/actions/message.py:0
|
1104
1110
|
#, python-format
|
@@ -1638,6 +1644,18 @@ msgid ""
|
|
1638
1644
|
"with the remaining lines."
|
1639
1645
|
msgstr ""
|
1640
1646
|
|
1647
|
+
#. module: shopfloor
|
1648
|
+
#: model:ir.model.fields,help:shopfloor.field_shopfloor_menu__scan_location_or_pack_first
|
1649
|
+
msgid ""
|
1650
|
+
"When checked, the user will be restricted by the type of object barcode "
|
1651
|
+
"that he can scan to select the document/transfer/move line to work on."
|
1652
|
+
msgstr ""
|
1653
|
+
|
1654
|
+
#. module: shopfloor
|
1655
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
1656
|
+
msgid "When choosing an order to pack, do not allow to scan a product."
|
1657
|
+
msgstr ""
|
1658
|
+
|
1641
1659
|
#. module: shopfloor
|
1642
1660
|
#: model:ir.model.fields,help:shopfloor.field_shopfloor_menu__allow_get_work
|
1643
1661
|
msgid ""
|
@@ -1657,10 +1675,10 @@ msgid "When picking, prevent unloading the whole bin when full."
|
|
1657
1675
|
msgstr ""
|
1658
1676
|
|
1659
1677
|
#. module: shopfloor
|
1660
|
-
#:
|
1678
|
+
#: model_terms:ir.ui.view,arch_db:shopfloor.shopfloor_menu_form_view
|
1661
1679
|
msgid ""
|
1662
|
-
"When selecting
|
1663
|
-
"
|
1680
|
+
"When selecting a move line, force the user to first scan a package or a "
|
1681
|
+
"location and not a product or a lot."
|
1664
1682
|
msgstr ""
|
1665
1683
|
|
1666
1684
|
#. module: shopfloor
|