odoo-addon-shopfloor 16.0.1.0.0.24__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 +2015 -0
- 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.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/RECORD +76 -70
- {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/top_level.txt +0 -0
@@ -41,7 +41,7 @@ class Checkout(Component):
|
|
41
41
|
_description = __doc__
|
42
42
|
|
43
43
|
def _response_for_select_line(
|
44
|
-
self, picking, message=None, need_confirm_pack_all=
|
44
|
+
self, picking, message=None, need_confirm_pack_all=""
|
45
45
|
):
|
46
46
|
if all(line.shopfloor_checkout_done for line in picking.move_line_ids):
|
47
47
|
return self._response_for_summary(picking, message=message)
|
@@ -53,7 +53,7 @@ class Checkout(Component):
|
|
53
53
|
message=message,
|
54
54
|
)
|
55
55
|
|
56
|
-
def _data_for_select_line(self, picking, need_confirm_pack_all=
|
56
|
+
def _data_for_select_line(self, picking, need_confirm_pack_all=""):
|
57
57
|
return {
|
58
58
|
"picking": self._data_for_stock_picking(picking),
|
59
59
|
"group_lines_by_location": True,
|
@@ -71,8 +71,20 @@ class Checkout(Component):
|
|
71
71
|
message=message,
|
72
72
|
)
|
73
73
|
|
74
|
+
def _response_for_select_child_location(self, picking, message=None):
|
75
|
+
return self._response(
|
76
|
+
next_state="select_child_location",
|
77
|
+
data={
|
78
|
+
"picking": self._data_for_stock_picking(
|
79
|
+
picking, done=True, with_lines=False, with_location=True
|
80
|
+
),
|
81
|
+
},
|
82
|
+
message=message,
|
83
|
+
)
|
84
|
+
|
74
85
|
def _response_for_select_document(self, message=None):
|
75
|
-
|
86
|
+
data = {"restrict_scan_first": self.work.menu.scan_location_or_pack_first}
|
87
|
+
return self._response(next_state="select_document", message=message, data=data)
|
76
88
|
|
77
89
|
def _response_for_manual_selection(self, message=None):
|
78
90
|
pickings = self.env["stock.picking"].search(
|
@@ -82,17 +94,20 @@ class Checkout(Component):
|
|
82
94
|
data = {"pickings": self.data.pickings(pickings)}
|
83
95
|
return self._response(next_state="manual_selection", data=data, message=message)
|
84
96
|
|
97
|
+
def _data_response_for_select_package(self, picking, lines):
|
98
|
+
return {
|
99
|
+
"selected_move_lines": self._data_for_move_lines(lines.sorted()),
|
100
|
+
"picking": self.data.picking(picking),
|
101
|
+
"packing_info": self._data_for_packing_info(picking),
|
102
|
+
"no_package_enabled": not self.options.get("checkout__disable_no_package"),
|
103
|
+
# Used by inheriting module
|
104
|
+
"package_allowed": True,
|
105
|
+
}
|
106
|
+
|
85
107
|
def _response_for_select_package(self, picking, lines, message=None):
|
86
108
|
return self._response(
|
87
109
|
next_state="select_package",
|
88
|
-
data=
|
89
|
-
"selected_move_lines": self._data_for_move_lines(lines.sorted()),
|
90
|
-
"picking": self.data.picking(picking),
|
91
|
-
"packing_info": self._data_for_packing_info(picking),
|
92
|
-
"no_package_enabled": not self.options.get(
|
93
|
-
"checkout__disable_no_package"
|
94
|
-
),
|
95
|
-
},
|
110
|
+
data=self._data_response_for_select_package(picking, lines),
|
96
111
|
message=message,
|
97
112
|
)
|
98
113
|
|
@@ -124,6 +139,7 @@ class Checkout(Component):
|
|
124
139
|
packages.with_context(picking_id=picking.id).sorted(),
|
125
140
|
picking=picking,
|
126
141
|
with_packaging=True,
|
142
|
+
with_package_move_line_count=True,
|
127
143
|
)
|
128
144
|
return self._response(
|
129
145
|
next_state="select_dest_package",
|
@@ -203,9 +219,8 @@ class Checkout(Component):
|
|
203
219
|
"picking",
|
204
220
|
"location",
|
205
221
|
"package",
|
206
|
-
"product",
|
207
222
|
"packaging",
|
208
|
-
)
|
223
|
+
) + (("product",) if not self.work.menu.scan_location_or_pack_first else ())
|
209
224
|
return search.find(
|
210
225
|
barcode,
|
211
226
|
types=search_types,
|
@@ -236,7 +251,7 @@ class Checkout(Component):
|
|
236
251
|
def _select_document_from_package(self, package, **kw):
|
237
252
|
pickings = package.move_line_ids.filtered(
|
238
253
|
lambda ml: ml.state not in ("cancel", "done")
|
239
|
-
).
|
254
|
+
).picking_id
|
240
255
|
if len(pickings) > 1:
|
241
256
|
# Filter only if we find several pickings to narrow the
|
242
257
|
# selection to one of the good type. If we have one picking
|
@@ -247,9 +262,7 @@ class Checkout(Component):
|
|
247
262
|
pickings = pickings.filtered(
|
248
263
|
lambda p: p.picking_type_id in self.picking_types
|
249
264
|
)
|
250
|
-
|
251
|
-
picking = pickings
|
252
|
-
return self._select_picking(picking, "select_document")
|
265
|
+
return self._select_picking(fields.first(pickings), "select_document")
|
253
266
|
|
254
267
|
def _select_document_from_product(self, product, line_domain=None, **kw):
|
255
268
|
line_domain = line_domain or []
|
@@ -317,17 +330,22 @@ class Checkout(Component):
|
|
317
330
|
def _data_for_delivery_packaging(self, packaging, **kw):
|
318
331
|
return self.data.delivery_packaging_list(packaging, **kw)
|
319
332
|
|
320
|
-
def _data_for_stock_picking(
|
333
|
+
def _data_for_stock_picking(
|
334
|
+
self, picking, done=False, with_lines=True, with_location=False
|
335
|
+
):
|
321
336
|
data = self.data.picking(picking)
|
322
337
|
line_picker = self._lines_checkout_done if done else self._lines_to_pack
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
self.
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
338
|
+
if with_lines:
|
339
|
+
data.update(
|
340
|
+
{
|
341
|
+
"move_lines": self._data_for_move_lines(
|
342
|
+
self._lines_prepare(picking, line_picker(picking)),
|
343
|
+
with_packaging=done,
|
344
|
+
)
|
345
|
+
}
|
346
|
+
)
|
347
|
+
if with_location:
|
348
|
+
data.update({"location_dest": self.data.location(picking.location_dest_id)})
|
331
349
|
return data
|
332
350
|
|
333
351
|
def _lines_checkout_done(self, picking):
|
@@ -347,7 +365,7 @@ class Checkout(Component):
|
|
347
365
|
]
|
348
366
|
|
349
367
|
def _order_for_list_stock_picking(self):
|
350
|
-
return "scheduled_date asc, id asc"
|
368
|
+
return "priority desc, scheduled_date asc, id asc"
|
351
369
|
|
352
370
|
def list_stock_picking(self):
|
353
371
|
"""List stock.picking records available
|
@@ -407,7 +425,7 @@ class Checkout(Component):
|
|
407
425
|
{"qty_done": 0, "shopfloor_user_id": False}
|
408
426
|
)
|
409
427
|
|
410
|
-
def scan_line(self, picking_id, barcode, confirm_pack_all=
|
428
|
+
def scan_line(self, picking_id, barcode, confirm_pack_all=None):
|
411
429
|
"""Scan move lines of the stock picking
|
412
430
|
|
413
431
|
It allows to select move lines of the stock picking for the next
|
@@ -436,6 +454,7 @@ class Checkout(Component):
|
|
436
454
|
if not selection_lines:
|
437
455
|
return self._response_for_summary(picking)
|
438
456
|
|
457
|
+
# Search of the destination package
|
439
458
|
search_result = self._scan_line_find(picking, barcode)
|
440
459
|
result_handler = getattr(self, "_select_lines_from_" + search_result.type)
|
441
460
|
kw = {"confirm_pack_all": confirm_pack_all}
|
@@ -466,7 +485,9 @@ class Checkout(Component):
|
|
466
485
|
picking, message=self.msg_store.barcode_not_found()
|
467
486
|
)
|
468
487
|
|
469
|
-
def _select_lines_from_package(
|
488
|
+
def _select_lines_from_package(
|
489
|
+
self, picking, selection_lines, package, prefill_qty=0, **kw
|
490
|
+
):
|
470
491
|
lines = selection_lines.filtered(
|
471
492
|
lambda l: l.package_id == package and not l.shopfloor_checkout_done
|
472
493
|
)
|
@@ -480,15 +501,15 @@ class Checkout(Component):
|
|
480
501
|
),
|
481
502
|
},
|
482
503
|
)
|
483
|
-
self._select_lines(lines)
|
504
|
+
self._select_lines(lines, prefill_qty=prefill_qty)
|
484
505
|
if self.work.menu.no_prefill_qty:
|
485
506
|
lines = picking.move_line_ids
|
486
507
|
return self._response_for_select_package(picking, lines)
|
487
508
|
|
488
509
|
def _select_lines_from_product(
|
489
|
-
self, picking, selection_lines, product, prefill_qty=1, **kw
|
510
|
+
self, picking, selection_lines, product, prefill_qty=1, check_lot=True, **kw
|
490
511
|
):
|
491
|
-
if product.tracking in ("lot", "serial"):
|
512
|
+
if product.tracking in ("lot", "serial") and check_lot:
|
492
513
|
return self._response_for_select_line(
|
493
514
|
picking, message=self.msg_store.scan_lot_on_product_tracked_by_lot()
|
494
515
|
)
|
@@ -516,7 +537,9 @@ class Checkout(Component):
|
|
516
537
|
elif packages:
|
517
538
|
# Select all the lines of the package when we scan a product in a
|
518
539
|
# package and we have only one.
|
519
|
-
return self._select_lines_from_package(
|
540
|
+
return self._select_lines_from_package(
|
541
|
+
picking, selection_lines, packages, prefill_qty=prefill_qty
|
542
|
+
)
|
520
543
|
else:
|
521
544
|
# There is no package on selected lines, so also select all other lines
|
522
545
|
# not in a package. But only the quantity on first selected lines
|
@@ -535,7 +558,9 @@ class Checkout(Component):
|
|
535
558
|
picking, selection_lines, packaging.product_id, prefill_qty=packaging.qty
|
536
559
|
)
|
537
560
|
|
538
|
-
def _select_lines_from_lot(
|
561
|
+
def _select_lines_from_lot(
|
562
|
+
self, picking, selection_lines, lot, prefill_qty=1, **kw
|
563
|
+
):
|
539
564
|
lines = selection_lines.filtered(lambda l: l.lot_id == lot)
|
540
565
|
if not lines:
|
541
566
|
return self._response_for_select_line(
|
@@ -563,28 +588,36 @@ class Checkout(Component):
|
|
563
588
|
# Select all the lines of the package when we scan a lot in a
|
564
589
|
# package and we have only one.
|
565
590
|
return self._select_lines_from_package(
|
566
|
-
picking, selection_lines, packages, **kw
|
591
|
+
picking, selection_lines, packages, prefill_qty=prefill_qty, **kw
|
567
592
|
)
|
568
593
|
|
569
|
-
|
570
|
-
return self.
|
594
|
+
first_allowed_line = fields.first(lines)
|
595
|
+
return self._select_lines_from_product(
|
596
|
+
picking,
|
597
|
+
selection_lines,
|
598
|
+
first_allowed_line.product_id,
|
599
|
+
prefill_qty=prefill_qty,
|
600
|
+
check_lot=False,
|
601
|
+
)
|
571
602
|
|
572
603
|
def _select_lines_from_serial(self, picking, selection_lines, lot, **kw):
|
573
604
|
# Search for serial number is actually the same as searching for lot (as of v14...)
|
574
605
|
return self._select_lines_from_lot(picking, selection_lines, lot, **kw)
|
575
606
|
|
607
|
+
# Handling of the destination package scanned
|
576
608
|
def _select_lines_from_delivery_packaging(
|
577
|
-
self, picking, selection_lines, packaging, confirm_pack_all=
|
609
|
+
self, picking, selection_lines, packaging, confirm_pack_all=None, **kw
|
578
610
|
):
|
579
611
|
"""Handle delivery packaging.
|
580
612
|
|
581
|
-
|
582
|
-
If a delivery pkg has been scanned:
|
613
|
+
A delivery pkg has been scanned:
|
583
614
|
|
584
615
|
1. validate it
|
585
|
-
2.
|
586
|
-
|
587
|
-
|
616
|
+
2. no lines to process (no quantities set to done)
|
617
|
+
2.a Option no prefill qty, ask to set some quantities
|
618
|
+
2.b Otherwise ask confirmation to pack everything if not yet done
|
619
|
+
3. if confirmation to pack everything set all quantities.
|
620
|
+
4. assign new package and skip `select_package` state
|
588
621
|
|
589
622
|
"""
|
590
623
|
carrier = self._get_carrier(picking)
|
@@ -600,16 +633,29 @@ class Checkout(Component):
|
|
600
633
|
packaging, carrier
|
601
634
|
),
|
602
635
|
)
|
603
|
-
|
604
|
-
|
636
|
+
message = None
|
637
|
+
need_confirm_pack_all = ""
|
638
|
+
has_lines_to_pack = any(
|
639
|
+
self._filter_lines_to_pack(ml) for ml in selection_lines
|
640
|
+
)
|
641
|
+
if not has_lines_to_pack:
|
642
|
+
if self.work.menu.no_prefill_qty:
|
643
|
+
message = self.msg_store.no_lines_to_process_set_quantities()
|
644
|
+
elif confirm_pack_all != packaging.barcode:
|
645
|
+
need_confirm_pack_all = packaging.barcode
|
646
|
+
message = self.msg_store.confirm_put_all_goods_in_delivery_package(
|
647
|
+
packaging
|
648
|
+
)
|
649
|
+
if message:
|
650
|
+
return self._response_for_select_line(
|
651
|
+
picking,
|
652
|
+
message=message,
|
653
|
+
need_confirm_pack_all=need_confirm_pack_all,
|
654
|
+
)
|
655
|
+
if confirm_pack_all == packaging.barcode:
|
605
656
|
self._select_lines(selection_lines)
|
606
|
-
|
607
|
-
|
608
|
-
)
|
609
|
-
return self._response_for_select_line(
|
610
|
-
picking,
|
611
|
-
message=self.msg_store.confirm_put_all_goods_in_delivery_package(packaging),
|
612
|
-
need_confirm_pack_all=True,
|
657
|
+
return self._create_and_assign_new_packaging(
|
658
|
+
picking, selection_lines, packaging=packaging
|
613
659
|
)
|
614
660
|
|
615
661
|
def _select_line_package(self, picking, selection_lines, package):
|
@@ -630,8 +676,12 @@ class Checkout(Component):
|
|
630
676
|
return self._select_lines_from_package(
|
631
677
|
picking, selection_lines, move_line.package_id
|
632
678
|
)
|
633
|
-
|
634
|
-
|
679
|
+
|
680
|
+
related_lines = selection_lines.filtered(
|
681
|
+
lambda l: not l.package_id and l.product_id != move_line.product_id
|
682
|
+
)
|
683
|
+
lines = self._select_lines(move_line, related_lines=related_lines)
|
684
|
+
return self._response_for_select_package(picking, lines)
|
635
685
|
|
636
686
|
def select_line(self, picking_id, package_id=None, move_line_id=None):
|
637
687
|
"""Select move lines of the stock picking
|
@@ -915,6 +965,13 @@ class Checkout(Component):
|
|
915
965
|
|
916
966
|
selected_lines = self.env["stock.move.line"].browse(selected_line_ids).exists()
|
917
967
|
search_result = self._scan_package_find(picking, barcode)
|
968
|
+
message = self._check_scan_package_find(picking, search_result)
|
969
|
+
if message:
|
970
|
+
return self._response_for_select_package(
|
971
|
+
picking,
|
972
|
+
selected_lines,
|
973
|
+
message=message,
|
974
|
+
)
|
918
975
|
result_handler = getattr(
|
919
976
|
self, "_scan_package_action_from_" + search_result.type
|
920
977
|
)
|
@@ -939,6 +996,22 @@ class Checkout(Component):
|
|
939
996
|
),
|
940
997
|
)
|
941
998
|
|
999
|
+
def _check_scan_package_find(self, picking, search_result):
|
1000
|
+
# Used by inheriting modules
|
1001
|
+
return False
|
1002
|
+
|
1003
|
+
def _find_line_to_increment(self, product_lines):
|
1004
|
+
"""Find which line should have its qty incremented.
|
1005
|
+
|
1006
|
+
Return the first line for the scanned product
|
1007
|
+
which still has some qty todo.
|
1008
|
+
If none are found, return the first line for that product.
|
1009
|
+
"""
|
1010
|
+
return next(
|
1011
|
+
(line for line in product_lines if line.qty_done < line.reserved_uom_qty),
|
1012
|
+
fields.first(product_lines),
|
1013
|
+
)
|
1014
|
+
|
942
1015
|
def _scan_package_action_from_product(
|
943
1016
|
self, picking, selected_lines, product, packaging=None, **kw
|
944
1017
|
):
|
@@ -954,7 +1027,7 @@ class Checkout(Component):
|
|
954
1027
|
return self._increment_custom_qty(
|
955
1028
|
picking,
|
956
1029
|
selected_lines,
|
957
|
-
|
1030
|
+
self._find_line_to_increment(product_lines),
|
958
1031
|
quantity_increment,
|
959
1032
|
)
|
960
1033
|
return self._switch_line_qty_done(picking, selected_lines, product_lines)
|
@@ -970,7 +1043,7 @@ class Checkout(Component):
|
|
970
1043
|
lot_lines = selected_lines.filtered(lambda l: l.lot_id == lot)
|
971
1044
|
if self.work.menu.no_prefill_qty:
|
972
1045
|
return self._increment_custom_qty(
|
973
|
-
picking, selected_lines,
|
1046
|
+
picking, selected_lines, self._find_line_to_increment(lot_lines), 1
|
974
1047
|
)
|
975
1048
|
return self._switch_line_qty_done(picking, selected_lines, lot_lines)
|
976
1049
|
|
@@ -1097,7 +1170,10 @@ class Checkout(Component):
|
|
1097
1170
|
if message:
|
1098
1171
|
return self._response_for_select_document(message=message)
|
1099
1172
|
selected_lines = self.env["stock.move.line"].browse(selected_line_ids).exists()
|
1100
|
-
selected_lines.
|
1173
|
+
selected_lines_with_qty_done = selected_lines.filtered(
|
1174
|
+
lambda line: line.qty_done > 0
|
1175
|
+
)
|
1176
|
+
selected_lines_with_qty_done.write(
|
1101
1177
|
{"shopfloor_checkout_done": True, "result_package_id": False}
|
1102
1178
|
)
|
1103
1179
|
response = self._check_allowed_qty_done(picking, selected_lines)
|
@@ -1337,6 +1413,7 @@ class Checkout(Component):
|
|
1337
1413
|
* summary: in case of error
|
1338
1414
|
* select_document: after done, goes back to start
|
1339
1415
|
* confirm_done: confirm a partial
|
1416
|
+
* select_child_location: there are child destination locations
|
1340
1417
|
"""
|
1341
1418
|
picking = self.env["stock.picking"].browse(picking_id)
|
1342
1419
|
message = self._check_picking_status(picking)
|
@@ -1359,8 +1436,51 @@ class Checkout(Component):
|
|
1359
1436
|
"body": _("Remaining raw product not packed, proceed anyway?"),
|
1360
1437
|
},
|
1361
1438
|
)
|
1439
|
+
lines_done = self._lines_checkout_done(picking)
|
1440
|
+
dest_location = lines_done.move_id.location_dest_id
|
1441
|
+
if len(dest_location) != 1 or dest_location.usage == "view":
|
1442
|
+
return self._response_for_select_child_location(
|
1443
|
+
picking,
|
1444
|
+
)
|
1362
1445
|
stock = self._actions_for("stock")
|
1446
|
+
stock.validate_moves(lines_done.move_id)
|
1447
|
+
return self._response_for_select_document(
|
1448
|
+
message=self.msg_store.transfer_done_success(lines_done.picking_id)
|
1449
|
+
)
|
1450
|
+
|
1451
|
+
def scan_dest_location(self, picking_id, barcode):
|
1452
|
+
"""Select a location destination
|
1453
|
+
|
1454
|
+
When setting the move as done, if the destination location
|
1455
|
+
has children locations, ask the user to scan one of them.
|
1456
|
+
|
1457
|
+
Transitions:
|
1458
|
+
* select_document: after done, goes back to start
|
1459
|
+
* select_child_location: in case of error
|
1460
|
+
"""
|
1461
|
+
picking = self.env["stock.picking"].browse(picking_id)
|
1462
|
+
message = self._check_picking_status(picking)
|
1463
|
+
if message:
|
1464
|
+
return self._response_for_select_document(message=message)
|
1465
|
+
search = self._actions_for("search")
|
1466
|
+
scanned_location = search.location_from_scan(barcode)
|
1467
|
+
if not scanned_location:
|
1468
|
+
return self._response_for_select_child_location(
|
1469
|
+
picking,
|
1470
|
+
message=self.msg_store.location_not_found(),
|
1471
|
+
)
|
1472
|
+
allowed_locations = self.env["stock.location"].search(
|
1473
|
+
[("id", "child_of", picking.location_dest_id.id), ("usage", "!=", "view")]
|
1474
|
+
)
|
1475
|
+
if scanned_location not in allowed_locations:
|
1476
|
+
return self._response_for_select_child_location(
|
1477
|
+
picking,
|
1478
|
+
message=self.msg_store.dest_location_not_allowed(),
|
1479
|
+
)
|
1363
1480
|
lines_done = self._lines_checkout_done(picking)
|
1481
|
+
for line in lines_done:
|
1482
|
+
line.update({"location_dest_id": scanned_location.id})
|
1483
|
+
stock = self._actions_for("stock")
|
1364
1484
|
stock.validate_moves(lines_done.move_id)
|
1365
1485
|
return self._response_for_select_document(
|
1366
1486
|
message=self.msg_store.transfer_done_success(lines_done.picking_id)
|
@@ -1388,7 +1508,7 @@ class ShopfloorCheckoutValidator(Component):
|
|
1388
1508
|
"picking_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1389
1509
|
"barcode": {"required": True, "type": "string"},
|
1390
1510
|
"confirm_pack_all": {
|
1391
|
-
"type": "
|
1511
|
+
"type": "string",
|
1392
1512
|
"nullable": True,
|
1393
1513
|
"required": False,
|
1394
1514
|
},
|
@@ -1550,6 +1670,12 @@ class ShopfloorCheckoutValidator(Component):
|
|
1550
1670
|
"confirmation": {"type": "boolean", "nullable": True, "required": False},
|
1551
1671
|
}
|
1552
1672
|
|
1673
|
+
def scan_dest_location(self):
|
1674
|
+
return {
|
1675
|
+
"picking_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1676
|
+
"barcode": {"required": True, "type": "string"},
|
1677
|
+
}
|
1678
|
+
|
1553
1679
|
|
1554
1680
|
class ShopfloorCheckoutValidatorResponse(Component):
|
1555
1681
|
"""Validators for the Checkout endpoints responses"""
|
@@ -1567,7 +1693,7 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1567
1693
|
to the next state.
|
1568
1694
|
"""
|
1569
1695
|
return {
|
1570
|
-
"select_document":
|
1696
|
+
"select_document": self._schema_for_select_document,
|
1571
1697
|
"manual_selection": self._schema_selection_list,
|
1572
1698
|
"select_line": self._schema_stock_picking_details,
|
1573
1699
|
"select_package": dict(
|
@@ -1578,6 +1704,11 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1578
1704
|
"nullable": True,
|
1579
1705
|
"required": False,
|
1580
1706
|
},
|
1707
|
+
package_allowed={
|
1708
|
+
"type": "boolean",
|
1709
|
+
"nullable": True,
|
1710
|
+
"required": False,
|
1711
|
+
},
|
1581
1712
|
),
|
1582
1713
|
"change_quantity": self._schema_selected_lines,
|
1583
1714
|
"select_dest_package": self._schema_select_package,
|
@@ -1585,6 +1716,17 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1585
1716
|
"summary": self._schema_summary,
|
1586
1717
|
"change_packaging": self._schema_select_packaging,
|
1587
1718
|
"confirm_done": self._schema_confirm_done,
|
1719
|
+
"select_child_location": self._schema_select_child_location,
|
1720
|
+
}
|
1721
|
+
|
1722
|
+
@property
|
1723
|
+
def _schema_for_select_document(self):
|
1724
|
+
return {
|
1725
|
+
"restrict_scan_first": {
|
1726
|
+
"type": "boolean",
|
1727
|
+
"nullable": False,
|
1728
|
+
"required": True,
|
1729
|
+
},
|
1588
1730
|
}
|
1589
1731
|
|
1590
1732
|
def _schema_stock_picking(self, lines_with_packaging=False):
|
@@ -1604,7 +1746,7 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1604
1746
|
self._schema_stock_picking(),
|
1605
1747
|
group_lines_by_location={"type": "boolean"},
|
1606
1748
|
show_oneline_package_content={"type": "boolean"},
|
1607
|
-
need_confirm_pack_all={"type": "
|
1749
|
+
need_confirm_pack_all={"type": "string"},
|
1608
1750
|
)
|
1609
1751
|
|
1610
1752
|
@property
|
@@ -1618,6 +1760,12 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1618
1760
|
def _schema_confirm_done(self):
|
1619
1761
|
return self._schema_stock_picking(lines_with_packaging=True)
|
1620
1762
|
|
1763
|
+
@property
|
1764
|
+
def _schema_select_child_location(self):
|
1765
|
+
return {
|
1766
|
+
"picking": {"type": "dict", "schema": self.schemas.picking()},
|
1767
|
+
}
|
1768
|
+
|
1621
1769
|
@property
|
1622
1770
|
def _schema_selection_list(self):
|
1623
1771
|
return {
|
@@ -1760,4 +1908,11 @@ class ShopfloorCheckoutValidatorResponse(Component):
|
|
1760
1908
|
return self._response_schema(next_states={"summary", "select_line"})
|
1761
1909
|
|
1762
1910
|
def done(self):
|
1763
|
-
return self._response_schema(
|
1911
|
+
return self._response_schema(
|
1912
|
+
next_states={"summary", "confirm_done", "select_child_location"}
|
1913
|
+
)
|
1914
|
+
|
1915
|
+
def scan_dest_location(self):
|
1916
|
+
return self._response_schema(
|
1917
|
+
next_states={"confirm_done", "select_document", "select_child_location"}
|
1918
|
+
)
|