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
@@ -161,6 +161,13 @@ class ZonePicking(Component):
|
|
161
161
|
def _pick_pack_same_time(self):
|
162
162
|
return self.work.menu.pick_pack_same_time
|
163
163
|
|
164
|
+
def _handle_complete_mix_pack(self, package):
|
165
|
+
packaging = self._actions_for("packaging")
|
166
|
+
return (
|
167
|
+
packaging.is_complete_mix_pack(package)
|
168
|
+
and not self.work.menu.no_prefill_qty
|
169
|
+
)
|
170
|
+
|
164
171
|
def _response_for_start(self, message=None):
|
165
172
|
zones = self.work.menu.picking_type_ids.mapped(
|
166
173
|
"default_location_src_id.child_ids"
|
@@ -197,7 +204,7 @@ class ZonePicking(Component):
|
|
197
204
|
move_lines,
|
198
205
|
message=None,
|
199
206
|
popup=None,
|
200
|
-
confirmation_required=
|
207
|
+
confirmation_required=None,
|
201
208
|
product=False,
|
202
209
|
sublocation=False,
|
203
210
|
package=False,
|
@@ -220,7 +227,7 @@ class ZonePicking(Component):
|
|
220
227
|
self,
|
221
228
|
move_line,
|
222
229
|
message=None,
|
223
|
-
confirmation_required=
|
230
|
+
confirmation_required=None,
|
224
231
|
**kw,
|
225
232
|
):
|
226
233
|
if confirmation_required and not message:
|
@@ -228,6 +235,12 @@ class ZonePicking(Component):
|
|
228
235
|
data = self._data_for_move_line(move_line)
|
229
236
|
data["move_line"].update(kw)
|
230
237
|
data["confirmation_required"] = confirmation_required
|
238
|
+
data[
|
239
|
+
"allow_alternative_destination_package"
|
240
|
+
] = self.work.menu.allow_alternative_destination_package
|
241
|
+
data["handle_complete_mix_pack"] = self._handle_complete_mix_pack(
|
242
|
+
move_line.package_id
|
243
|
+
)
|
231
244
|
return self._response(
|
232
245
|
next_state="set_line_destination", data=data, message=message
|
233
246
|
)
|
@@ -252,7 +265,7 @@ class ZonePicking(Component):
|
|
252
265
|
self,
|
253
266
|
move_lines,
|
254
267
|
message=None,
|
255
|
-
confirmation_required=
|
268
|
+
confirmation_required=None,
|
256
269
|
):
|
257
270
|
if confirmation_required and not message:
|
258
271
|
message = self.msg_store.need_confirmation()
|
@@ -275,7 +288,7 @@ class ZonePicking(Component):
|
|
275
288
|
self,
|
276
289
|
move_line,
|
277
290
|
message=None,
|
278
|
-
confirmation_required=
|
291
|
+
confirmation_required=None,
|
279
292
|
):
|
280
293
|
if confirmation_required and not message:
|
281
294
|
message = self.msg_store.need_confirmation()
|
@@ -346,11 +359,19 @@ class ZonePicking(Component):
|
|
346
359
|
# to retrieve if location will be empty.
|
347
360
|
# Maybe group lines by location and compute only once.
|
348
361
|
move_line = self.env["stock.move.line"].browse(data_move_line["id"])
|
362
|
+
handle_complete_mix_pack = self._handle_complete_mix_pack(
|
363
|
+
move_line.package_id
|
364
|
+
)
|
365
|
+
data_move_line["handle_complete_mix_pack"] = handle_complete_mix_pack
|
349
366
|
# `location_will_be_empty` flag states if, by processing this move line
|
350
367
|
# and picking the product, the location will be emptied.
|
351
368
|
data_move_line[
|
352
369
|
"location_will_be_empty"
|
353
|
-
] = move_line.location_id.planned_qty_in_location_is_empty(
|
370
|
+
] = move_line.location_id.planned_qty_in_location_is_empty(
|
371
|
+
move_line.package_id.move_line_ids
|
372
|
+
if handle_complete_mix_pack
|
373
|
+
else move_line
|
374
|
+
)
|
354
375
|
return data
|
355
376
|
|
356
377
|
def _data_for_location(self, location, zone_location=None, picking_type=None):
|
@@ -506,7 +527,7 @@ class ZonePicking(Component):
|
|
506
527
|
def _scan_source_location(
|
507
528
|
self,
|
508
529
|
barcode,
|
509
|
-
confirmation=
|
530
|
+
confirmation=None,
|
510
531
|
product_id=False,
|
511
532
|
sublocation=False,
|
512
533
|
package=False,
|
@@ -576,7 +597,7 @@ class ZonePicking(Component):
|
|
576
597
|
def _scan_source_package(
|
577
598
|
self,
|
578
599
|
barcode,
|
579
|
-
confirmation=
|
600
|
+
confirmation=None,
|
580
601
|
product_id=False,
|
581
602
|
sublocation=False,
|
582
603
|
package=False,
|
@@ -594,21 +615,28 @@ class ZonePicking(Component):
|
|
594
615
|
package = search.package_from_scan(barcode)
|
595
616
|
if not package:
|
596
617
|
return response, message
|
597
|
-
if
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
618
|
+
if package.location_id:
|
619
|
+
if not package.location_id.is_sublocation_of(self.zone_location):
|
620
|
+
# Package is not in an allowed location
|
621
|
+
response = self._list_move_lines(self.zone_location)
|
622
|
+
message = self.msg_store.location_not_allowed()
|
623
|
+
return response, message
|
602
624
|
|
603
625
|
move_lines = self._find_location_move_lines(
|
604
626
|
locations=sublocation, package=package
|
605
627
|
)
|
628
|
+
handle_complete_mix_pack = self._handle_complete_mix_pack(package)
|
606
629
|
if move_lines:
|
630
|
+
if handle_complete_mix_pack:
|
631
|
+
response = self._response_for_set_line_destination(
|
632
|
+
first(move_lines), qty_done=0
|
633
|
+
)
|
634
|
+
return response, message
|
607
635
|
if packaging.package_has_several_products(package):
|
608
636
|
message = self.msg_store.several_products_in_package(package)
|
609
637
|
if packaging.package_has_several_lots(package):
|
610
638
|
message = self.msg_store.several_lots_in_package(package)
|
611
|
-
if message:
|
639
|
+
if message or self.work.menu.no_prefill_qty:
|
612
640
|
return (
|
613
641
|
self._list_move_lines(
|
614
642
|
self.zone_location,
|
@@ -617,8 +645,8 @@ class ZonePicking(Component):
|
|
617
645
|
),
|
618
646
|
message,
|
619
647
|
)
|
648
|
+
|
620
649
|
move_line = first(move_lines)
|
621
|
-
# Fix me for a package prefill qty is zero ?
|
622
650
|
qty_done = self._get_prefill_qty(move_line)
|
623
651
|
response = self._response_for_set_line_destination(
|
624
652
|
move_line, qty_done=qty_done
|
@@ -632,19 +660,22 @@ class ZonePicking(Component):
|
|
632
660
|
product=product,
|
633
661
|
)
|
634
662
|
if move_lines:
|
635
|
-
if
|
663
|
+
if confirmation != barcode:
|
636
664
|
message = self.msg_store.package_different_change()
|
637
665
|
response = self._response_for_select_line(
|
638
|
-
move_lines, message, confirmation_required=
|
666
|
+
move_lines, message, confirmation_required=barcode
|
639
667
|
)
|
640
668
|
else:
|
641
669
|
change_package_lot = self._actions_for("change.package.lot")
|
670
|
+
move_line = first(move_lines)
|
642
671
|
response = change_package_lot.change_package(
|
643
|
-
|
672
|
+
move_line,
|
644
673
|
package,
|
645
|
-
|
646
|
-
|
647
|
-
|
674
|
+
response_ok_func=functools.partial(
|
675
|
+
self._response_for_set_line_destination,
|
676
|
+
qty_done=self._get_prefill_qty(move_line, qty=0),
|
677
|
+
),
|
678
|
+
response_error_func=self._response_for_change_pack_lot,
|
648
679
|
)
|
649
680
|
else:
|
650
681
|
response = self._list_move_lines(sublocation or self.zone_location)
|
@@ -668,7 +699,7 @@ class ZonePicking(Component):
|
|
668
699
|
def _scan_source_product(
|
669
700
|
self,
|
670
701
|
barcode,
|
671
|
-
confirmation=
|
702
|
+
confirmation=None,
|
672
703
|
product_id=False,
|
673
704
|
sublocation=False,
|
674
705
|
package=False,
|
@@ -730,7 +761,7 @@ class ZonePicking(Component):
|
|
730
761
|
def _scan_source_lot(
|
731
762
|
self,
|
732
763
|
barcode,
|
733
|
-
confirmation=
|
764
|
+
confirmation=None,
|
734
765
|
product_id=False,
|
735
766
|
sublocation=False,
|
736
767
|
package=False,
|
@@ -783,7 +814,7 @@ class ZonePicking(Component):
|
|
783
814
|
def scan_source(
|
784
815
|
self,
|
785
816
|
barcode,
|
786
|
-
confirmation=
|
817
|
+
confirmation=None,
|
787
818
|
product_id=None,
|
788
819
|
sublocation_id=None,
|
789
820
|
package_id=None,
|
@@ -860,13 +891,15 @@ class ZonePicking(Component):
|
|
860
891
|
base_response=response, message=self.msg_store.barcode_not_found()
|
861
892
|
)
|
862
893
|
|
863
|
-
def _set_destination_location(
|
894
|
+
def _set_destination_location(
|
895
|
+
self, move_line, package, quantity, confirmation, location, barcode
|
896
|
+
):
|
864
897
|
location_changed = False
|
865
898
|
response = None
|
866
899
|
|
867
900
|
# A valid location is a sub-location of the original destination, or a
|
868
901
|
# any sub-location of the picking type's default destination location
|
869
|
-
# if `confirmation is
|
902
|
+
# if `confirmation is equal to the barcode scanned.
|
870
903
|
# Ask confirmation to the user if the scanned location is not in the
|
871
904
|
# expected ones but is valid (in picking type's default destination)
|
872
905
|
if not self.is_dest_location_valid(move_line.move_id, location):
|
@@ -877,7 +910,7 @@ class ZonePicking(Component):
|
|
877
910
|
)
|
878
911
|
return (location_changed, response)
|
879
912
|
|
880
|
-
if
|
913
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
881
914
|
move_line.location_dest_id, location
|
882
915
|
):
|
883
916
|
response = self._response_for_set_line_destination(
|
@@ -885,7 +918,7 @@ class ZonePicking(Component):
|
|
885
918
|
message=self.msg_store.confirm_location_changed(
|
886
919
|
move_line.location_dest_id, location
|
887
920
|
),
|
888
|
-
confirmation_required=
|
921
|
+
confirmation_required=barcode,
|
889
922
|
qty_done=quantity,
|
890
923
|
)
|
891
924
|
return (location_changed, response)
|
@@ -899,24 +932,41 @@ class ZonePicking(Component):
|
|
899
932
|
)
|
900
933
|
return (location_changed, response)
|
901
934
|
# destination location set to the scanned one
|
902
|
-
|
935
|
+
|
903
936
|
stock = self._actions_for("stock")
|
937
|
+
move_lines = move_line
|
938
|
+
if package:
|
939
|
+
quantity = None
|
940
|
+
# Handling all move lines from a complete mix pack
|
941
|
+
for _move_line in package.move_line_ids:
|
942
|
+
if _move_line.state not in ("assigned", "partially_available"):
|
943
|
+
continue
|
944
|
+
_move_line.qty_done = move_line.reserved_uom_qty
|
945
|
+
move_lines |= _move_line
|
946
|
+
self._write_destination_on_lines(move_lines, location)
|
947
|
+
|
904
948
|
try:
|
905
|
-
stock.mark_move_line_as_picked(
|
949
|
+
stock.mark_move_line_as_picked(move_lines, quantity, check_user=True)
|
906
950
|
except ConcurentWorkOnTransfer as error:
|
951
|
+
values = {"qty_done": quantity} if quantity is not None else {}
|
907
952
|
response = self._response_for_set_line_destination(
|
908
953
|
move_line,
|
909
954
|
message={
|
910
955
|
"message_type": "error",
|
911
956
|
"body": str(error),
|
912
957
|
},
|
913
|
-
|
958
|
+
**values,
|
914
959
|
)
|
915
960
|
return (location_changed, response)
|
916
|
-
stock.validate_moves(
|
961
|
+
stock.validate_moves(move_lines.move_id)
|
962
|
+
|
917
963
|
location_changed = True
|
918
964
|
# Zero check
|
919
|
-
|
965
|
+
# Only apply zero check if the product is of type "product".
|
966
|
+
zero_check = (
|
967
|
+
move_line.product_id.type == "product"
|
968
|
+
and self.picking_type.shopfloor_zero_check
|
969
|
+
)
|
920
970
|
if zero_check and move_line.location_id.planned_qty_in_location_is_empty():
|
921
971
|
response = self._response_for_zero_check(move_line)
|
922
972
|
return (location_changed, response)
|
@@ -940,25 +990,34 @@ class ZonePicking(Component):
|
|
940
990
|
move_line.reserved_uom_qty - qty, precision_rounding=rounding
|
941
991
|
)
|
942
992
|
|
943
|
-
def
|
944
|
-
|
945
|
-
response = None
|
993
|
+
def _is_package_not_valid(self, package):
|
994
|
+
message = False
|
946
995
|
# A valid package is:
|
947
996
|
# * an empty package
|
948
997
|
# * not used as destination for another move line
|
998
|
+
# * not contains move lines with different operation type
|
949
999
|
if not self._is_package_empty(package):
|
1000
|
+
message = self.msg_store.package_not_empty(package)
|
1001
|
+
elif package.planned_move_line_ids:
|
1002
|
+
if not self.work.menu.multiple_move_single_pack:
|
1003
|
+
message = self.msg_store.package_already_used(package)
|
1004
|
+
else:
|
1005
|
+
for line in package.planned_move_line_ids:
|
1006
|
+
if line.picking_id.picking_type_id.id in self.picking_types.ids:
|
1007
|
+
continue
|
1008
|
+
message = self.msg_store.package_different_picking_type(
|
1009
|
+
package, line.picking_id.picking_type_id
|
1010
|
+
)
|
1011
|
+
break
|
1012
|
+
return message
|
1013
|
+
|
1014
|
+
def _set_destination_package(self, move_line, quantity, package):
|
1015
|
+
package_changed = False
|
1016
|
+
response = None
|
1017
|
+
package_invalid_message = self._is_package_not_valid(package)
|
1018
|
+
if package_invalid_message:
|
950
1019
|
response = self._response_for_set_line_destination(
|
951
|
-
move_line,
|
952
|
-
message=self.msg_store.package_not_empty(package),
|
953
|
-
qty_done=quantity,
|
954
|
-
)
|
955
|
-
return (package_changed, response)
|
956
|
-
multiple_move_allowed = self.work.menu.multiple_move_single_pack
|
957
|
-
if package.planned_move_line_ids and not multiple_move_allowed:
|
958
|
-
response = self._response_for_set_line_destination(
|
959
|
-
move_line,
|
960
|
-
message=self.msg_store.package_already_used(package),
|
961
|
-
qty_done=quantity,
|
1020
|
+
move_line, message=package_invalid_message, qty_done=quantity
|
962
1021
|
)
|
963
1022
|
return (package_changed, response)
|
964
1023
|
# the quantity done is set to the passed quantity
|
@@ -973,6 +1032,7 @@ class ZonePicking(Component):
|
|
973
1032
|
)
|
974
1033
|
return (package_changed, response)
|
975
1034
|
stock = self._actions_for("stock")
|
1035
|
+
self._lock_lines(move_line)
|
976
1036
|
try:
|
977
1037
|
stock.mark_move_line_as_picked(
|
978
1038
|
move_line, quantity, package, check_user=True
|
@@ -989,7 +1049,11 @@ class ZonePicking(Component):
|
|
989
1049
|
return (package_changed, response)
|
990
1050
|
package_changed = True
|
991
1051
|
# Zero check
|
992
|
-
|
1052
|
+
# Only apply zero check if the product is of type "product".
|
1053
|
+
zero_check = (
|
1054
|
+
move_line.product_id.type == "product"
|
1055
|
+
and self.picking_type.shopfloor_zero_check
|
1056
|
+
)
|
993
1057
|
if zero_check and move_line.location_id.planned_qty_in_location_is_empty():
|
994
1058
|
response = self._response_for_zero_check(move_line)
|
995
1059
|
return (package_changed, response)
|
@@ -1028,7 +1092,8 @@ class ZonePicking(Component):
|
|
1028
1092
|
move_line_id,
|
1029
1093
|
barcode,
|
1030
1094
|
quantity,
|
1031
|
-
|
1095
|
+
handle_complete_mix_pack=False,
|
1096
|
+
confirmation=None,
|
1032
1097
|
):
|
1033
1098
|
"""Set a destination location (and done) or a destination package (in buffer)
|
1034
1099
|
|
@@ -1068,6 +1133,9 @@ class ZonePicking(Component):
|
|
1068
1133
|
When the barcode is the product (or its packaging) or the lot on the line:
|
1069
1134
|
* The done quantity is incremented by one or the packaging quantity.
|
1070
1135
|
|
1136
|
+
The `handle_complete_mix_pack` option, when it is set to true. Will move all they
|
1137
|
+
lines contained in the package of the move line passed in parameter.
|
1138
|
+
|
1071
1139
|
Transitions:
|
1072
1140
|
* select_line: destination has been set, showing the next lines to pick
|
1073
1141
|
* zero_check: if the option is active and if the quantity of product
|
@@ -1086,12 +1154,12 @@ class ZonePicking(Component):
|
|
1086
1154
|
|
1087
1155
|
pkg_moved = False
|
1088
1156
|
search = self._actions_for("search")
|
1089
|
-
|
1157
|
+
|
1158
|
+
moving_full_quantity = self._move_line_full_qty(move_line, quantity)
|
1090
1159
|
|
1091
1160
|
response = self._set_destination_update_quantity(move_line, quantity, barcode)
|
1092
1161
|
if response:
|
1093
1162
|
return response
|
1094
|
-
|
1095
1163
|
if quantity <= 0:
|
1096
1164
|
message = self.msg_store.picking_zero_quantity()
|
1097
1165
|
return self._response_for_set_line_destination(
|
@@ -1101,10 +1169,14 @@ class ZonePicking(Component):
|
|
1101
1169
|
)
|
1102
1170
|
|
1103
1171
|
extra_message = ""
|
1104
|
-
if
|
1105
|
-
# When the barcode is a location
|
1172
|
+
if moving_full_quantity:
|
1173
|
+
# When the barcode is a location,
|
1174
|
+
# only allow it if moving the full qty.
|
1106
1175
|
location = search.location_from_scan(barcode)
|
1107
1176
|
if location:
|
1177
|
+
package = None
|
1178
|
+
if handle_complete_mix_pack:
|
1179
|
+
package = move_line.package_id
|
1108
1180
|
if self._pick_pack_same_time():
|
1109
1181
|
(
|
1110
1182
|
good_for_packing,
|
@@ -1121,9 +1193,11 @@ class ZonePicking(Component):
|
|
1121
1193
|
)
|
1122
1194
|
pkg_moved, response = self._set_destination_location(
|
1123
1195
|
move_line,
|
1196
|
+
package,
|
1124
1197
|
quantity,
|
1125
1198
|
confirmation,
|
1126
1199
|
location,
|
1200
|
+
barcode,
|
1127
1201
|
)
|
1128
1202
|
if response:
|
1129
1203
|
if extra_message:
|
@@ -1136,6 +1210,28 @@ class ZonePicking(Component):
|
|
1136
1210
|
# When the barcode is a package
|
1137
1211
|
package = search.package_from_scan(barcode)
|
1138
1212
|
if package:
|
1213
|
+
|
1214
|
+
if not moving_full_quantity and move_line.package_id == package:
|
1215
|
+
# Check we're not using the source package as transfer package.
|
1216
|
+
message = self.msg_store.dest_package_not_valid(package)
|
1217
|
+
return self._response_for_set_line_destination(
|
1218
|
+
move_line, message=message, qty_done=quantity
|
1219
|
+
)
|
1220
|
+
|
1221
|
+
allow_alternative_package = (
|
1222
|
+
self.work.menu.allow_alternative_destination_package
|
1223
|
+
)
|
1224
|
+
if (
|
1225
|
+
not allow_alternative_package
|
1226
|
+
and move_line.result_package_id
|
1227
|
+
and move_line.result_package_id != package
|
1228
|
+
):
|
1229
|
+
# Check whether the user can move a whole package to a different package.
|
1230
|
+
message = self.msg_store.package_transfer_not_allowed_scan_location()
|
1231
|
+
return self._response_for_set_line_destination(
|
1232
|
+
move_line, message=message, qty_done=quantity
|
1233
|
+
)
|
1234
|
+
|
1139
1235
|
if self._pick_pack_same_time():
|
1140
1236
|
(
|
1141
1237
|
good_for_packing,
|
@@ -1155,7 +1251,7 @@ class ZonePicking(Component):
|
|
1155
1251
|
message = None
|
1156
1252
|
|
1157
1253
|
if not pkg_moved and not package:
|
1158
|
-
if
|
1254
|
+
if not moving_full_quantity:
|
1159
1255
|
message = self.msg_store.package_not_found_for_barcode(barcode)
|
1160
1256
|
else:
|
1161
1257
|
# we don't know if user wanted to scan a location or a package
|
@@ -1341,7 +1437,10 @@ class ZonePicking(Component):
|
|
1341
1437
|
# pre-configured callable used to generate the response as the
|
1342
1438
|
# change.package.lot component is not aware of the needed response type
|
1343
1439
|
# and related parameters for zone picking scenario
|
1344
|
-
response_ok_func = functools.partial(
|
1440
|
+
response_ok_func = functools.partial(
|
1441
|
+
self._response_for_set_line_destination,
|
1442
|
+
qty_done=self._get_prefill_qty(move_line),
|
1443
|
+
)
|
1345
1444
|
response_error_func = functools.partial(self._response_for_change_pack_lot)
|
1346
1445
|
response = None
|
1347
1446
|
change_package_lot = self._actions_for("change.package.lot")
|
@@ -1416,7 +1515,7 @@ class ZonePicking(Component):
|
|
1416
1515
|
return self._response_for_select_line(move_lines, message=message)
|
1417
1516
|
return self._response_for_start(message=message)
|
1418
1517
|
|
1419
|
-
def set_destination_all(self, barcode, confirmation=
|
1518
|
+
def set_destination_all(self, barcode, confirmation=None):
|
1420
1519
|
"""Set the destination for all the lines in the buffer
|
1421
1520
|
|
1422
1521
|
Look in ``prepare_unload`` for the definition of the buffer.
|
@@ -1458,7 +1557,7 @@ class ZonePicking(Component):
|
|
1458
1557
|
# destination set on buffer lines
|
1459
1558
|
# - To confirm if the scanned destination is not a child of the
|
1460
1559
|
# current destination set on buffer lines
|
1461
|
-
if
|
1560
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
1462
1561
|
buffer_lines.location_dest_id, location
|
1463
1562
|
):
|
1464
1563
|
return self._response_for_unload_all(
|
@@ -1466,7 +1565,7 @@ class ZonePicking(Component):
|
|
1466
1565
|
message=self.msg_store.confirm_location_changed(
|
1467
1566
|
first(buffer_lines.location_dest_id), location
|
1468
1567
|
),
|
1469
|
-
confirmation_required=
|
1568
|
+
confirmation_required=barcode,
|
1470
1569
|
)
|
1471
1570
|
# the scanned location is still valid, use it
|
1472
1571
|
self._write_destination_on_lines(buffer_lines, location)
|
@@ -1568,7 +1667,7 @@ class ZonePicking(Component):
|
|
1568
1667
|
"""Lock move lines"""
|
1569
1668
|
self._actions_for("lock").for_update(lines)
|
1570
1669
|
|
1571
|
-
def unload_set_destination(self, package_id, barcode, confirmation=
|
1670
|
+
def unload_set_destination(self, package_id, barcode, confirmation=None):
|
1572
1671
|
"""Scan the final destination for move lines in the buffer with the
|
1573
1672
|
destination package
|
1574
1673
|
|
@@ -1610,7 +1709,7 @@ class ZonePicking(Component):
|
|
1610
1709
|
# destination set on buffer lines
|
1611
1710
|
# - To confirm if the scanned destination is not a child of the
|
1612
1711
|
# current destination set on buffer lines
|
1613
|
-
if not confirmation and self.is_dest_location_to_confirm(
|
1712
|
+
if not confirmation == barcode and self.is_dest_location_to_confirm(
|
1614
1713
|
buffer_lines.location_dest_id, location
|
1615
1714
|
):
|
1616
1715
|
return self._response_for_unload_set_destination(
|
@@ -1618,7 +1717,7 @@ class ZonePicking(Component):
|
|
1618
1717
|
message=self.msg_store.confirm_location_changed(
|
1619
1718
|
first(buffer_lines.location_dest_id), location
|
1620
1719
|
),
|
1621
|
-
confirmation_required=
|
1720
|
+
confirmation_required=barcode,
|
1622
1721
|
)
|
1623
1722
|
# the scanned location is valid, use it
|
1624
1723
|
self._write_destination_on_lines(buffer_lines, location)
|
@@ -1674,7 +1773,7 @@ class ShopfloorZonePickingValidator(Component):
|
|
1674
1773
|
def scan_source(self):
|
1675
1774
|
return {
|
1676
1775
|
"barcode": {"required": False, "nullable": True, "type": "string"},
|
1677
|
-
"confirmation": {"type": "
|
1776
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1678
1777
|
"product_id": {"required": False, "nullable": True, "type": "integer"},
|
1679
1778
|
"sublocation_id": {"required": False, "nullable": True, "type": "integer"},
|
1680
1779
|
"package_id": {"required": False, "nullable": True, "type": "integer"},
|
@@ -1689,7 +1788,12 @@ class ShopfloorZonePickingValidator(Component):
|
|
1689
1788
|
"required": True,
|
1690
1789
|
"type": "float",
|
1691
1790
|
},
|
1692
|
-
"confirmation": {"type": "
|
1791
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1792
|
+
"handle_complete_mix_pack": {
|
1793
|
+
"type": "boolean",
|
1794
|
+
"nullable": True,
|
1795
|
+
"required": False,
|
1796
|
+
},
|
1693
1797
|
}
|
1694
1798
|
|
1695
1799
|
def is_zero(self):
|
@@ -1715,7 +1819,7 @@ class ShopfloorZonePickingValidator(Component):
|
|
1715
1819
|
def set_destination_all(self):
|
1716
1820
|
return {
|
1717
1821
|
"barcode": {"required": False, "nullable": True, "type": "string"},
|
1718
|
-
"confirmation": {"type": "
|
1822
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1719
1823
|
}
|
1720
1824
|
|
1721
1825
|
def unload_split(self):
|
@@ -1731,7 +1835,7 @@ class ShopfloorZonePickingValidator(Component):
|
|
1731
1835
|
return {
|
1732
1836
|
"package_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1733
1837
|
"barcode": {"required": False, "nullable": True, "type": "string"},
|
1734
|
-
"confirmation": {"type": "
|
1838
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1735
1839
|
}
|
1736
1840
|
|
1737
1841
|
|
@@ -1872,10 +1976,11 @@ class ShopfloorZonePickingValidatorResponse(Component):
|
|
1872
1976
|
"zone_location": self.schemas._schema_dict_of(self.schemas.location()),
|
1873
1977
|
"picking_type": self.schemas._schema_dict_of(self.schemas.picking_type()),
|
1874
1978
|
"move_line": self.schemas._schema_dict_of(
|
1875
|
-
self.schemas.move_line(with_picking=True)
|
1979
|
+
self.schemas.move_line(with_picking=True),
|
1980
|
+
required=False,
|
1876
1981
|
),
|
1877
1982
|
"confirmation_required": {
|
1878
|
-
"type": "
|
1983
|
+
"type": "string",
|
1879
1984
|
"nullable": True,
|
1880
1985
|
"required": False,
|
1881
1986
|
},
|
@@ -1884,6 +1989,16 @@ class ShopfloorZonePickingValidatorResponse(Component):
|
|
1884
1989
|
"nullable": True,
|
1885
1990
|
"required": False,
|
1886
1991
|
},
|
1992
|
+
"allow_alternative_destination_package": {
|
1993
|
+
"type": "boolean",
|
1994
|
+
"nullable": True,
|
1995
|
+
"required": False,
|
1996
|
+
},
|
1997
|
+
"handle_complete_mix_pack": {
|
1998
|
+
"type": "boolean",
|
1999
|
+
"nullable": True,
|
2000
|
+
"required": False,
|
2001
|
+
},
|
1887
2002
|
}
|
1888
2003
|
return schema
|
1889
2004
|
|
@@ -1896,7 +2011,7 @@ class ShopfloorZonePickingValidatorResponse(Component):
|
|
1896
2011
|
self.schemas.move_line(with_picking=True)
|
1897
2012
|
),
|
1898
2013
|
"confirmation_required": {
|
1899
|
-
"type": "
|
2014
|
+
"type": "string",
|
1900
2015
|
"nullable": True,
|
1901
2016
|
"required": False,
|
1902
2017
|
},
|
@@ -1925,6 +2040,11 @@ class ShopfloorZonePickingValidatorResponse(Component):
|
|
1925
2040
|
"nullable": False,
|
1926
2041
|
"required": True,
|
1927
2042
|
}
|
2043
|
+
schema["move_lines"]["schema"]["schema"]["handle_complete_mix_pack"] = {
|
2044
|
+
"type": "boolean",
|
2045
|
+
"nullable": False,
|
2046
|
+
"required": True,
|
2047
|
+
}
|
1928
2048
|
return schema
|
1929
2049
|
|
1930
2050
|
@property
|
@@ -366,7 +366,7 @@ ul.auto-toc {
|
|
366
366
|
!! This file is generated by oca-gen-addon-readme !!
|
367
367
|
!! changes will be overwritten. !!
|
368
368
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
369
|
-
!! source digest: sha256:
|
369
|
+
!! source digest: sha256:835c681f1a1e049cfb7792d6dd69c0214bfbaad4775d134f6e5a47900e8aeb4a
|
370
370
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
371
371
|
<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>
|
372
372
|
<p>Shopfloor is a barcode scanner application for internal warehouse operations.</p>
|
@@ -26,6 +26,7 @@ from . import test_checkout_select
|
|
26
26
|
from . import test_checkout_scan_line
|
27
27
|
from . import test_checkout_scan_line_no_prefill_qty
|
28
28
|
from . import test_checkout_scan_line_base
|
29
|
+
from . import test_checkout_scan_dest_location
|
29
30
|
from . import test_checkout_select_line
|
30
31
|
from . import test_checkout_select_package_base
|
31
32
|
from . import test_checkout_set_qty
|
@@ -60,6 +61,7 @@ from . import test_location_content_transfer_set_destination_package_or_line
|
|
60
61
|
from . import test_location_content_transfer_putaway
|
61
62
|
from . import test_location_content_transfer_mix
|
62
63
|
from . import test_zone_picking_base
|
64
|
+
from . import test_zone_picking_complete_mix_pack_flux
|
63
65
|
from . import test_zone_picking_start
|
64
66
|
from . import test_zone_picking_select_picking_type
|
65
67
|
from . import test_zone_picking_select_line
|
@@ -27,6 +27,7 @@ class CommonCase(BaseCommonCase):
|
|
27
27
|
cls.input_location = cls.env.ref("stock.stock_location_company")
|
28
28
|
cls.shelf1 = cls.env.ref("stock.stock_location_components")
|
29
29
|
cls.shelf2 = cls.env.ref("stock.stock_location_14")
|
30
|
+
cls.shelf3 = cls.shelf2.sudo().copy({"barcode": "26019853"})
|
30
31
|
|
31
32
|
@classmethod
|
32
33
|
def _shopfloor_user_values(cls):
|
@@ -220,6 +221,7 @@ class CommonCase(BaseCommonCase):
|
|
220
221
|
package = cls.env["stock.quant.package"].create({})
|
221
222
|
product_packages[key] = package
|
222
223
|
for (product, location), qty in product_locations.items():
|
224
|
+
product_package = product_packages.get((product, location))
|
223
225
|
lot = None
|
224
226
|
if in_lot:
|
225
227
|
if isinstance(in_lot, models.BaseModel):
|
@@ -235,7 +237,7 @@ class CommonCase(BaseCommonCase):
|
|
235
237
|
# of units to pick a package
|
236
238
|
qty *= 2
|
237
239
|
cls._update_qty_in_location(
|
238
|
-
location, product, qty, package=
|
240
|
+
location, product, qty, package=product_package, lot=lot
|
239
241
|
)
|
240
242
|
|
241
243
|
# used by _create_package_in_location
|