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
@@ -144,10 +144,10 @@ class ClusterPicking(Component):
|
|
144
144
|
message=message,
|
145
145
|
)
|
146
146
|
|
147
|
-
def _response_for_confirm_unload_all(self, batch, message=None):
|
147
|
+
def _response_for_confirm_unload_all(self, batch, message=None, confirmation=None):
|
148
148
|
return self._response(
|
149
149
|
next_state="confirm_unload_all",
|
150
|
-
data=self._data_for_unload_all(batch),
|
150
|
+
data=self._data_for_unload_all(batch, confirmation=confirmation),
|
151
151
|
message=message,
|
152
152
|
)
|
153
153
|
|
@@ -166,10 +166,14 @@ class ClusterPicking(Component):
|
|
166
166
|
message=message,
|
167
167
|
)
|
168
168
|
|
169
|
-
def _response_for_confirm_unload_set_destination(
|
169
|
+
def _response_for_confirm_unload_set_destination(
|
170
|
+
self, batch, package, confirmation=None
|
171
|
+
):
|
170
172
|
return self._response(
|
171
173
|
next_state="confirm_unload_set_destination",
|
172
|
-
data=self._data_for_unload_single(
|
174
|
+
data=self._data_for_unload_single(
|
175
|
+
batch, package, confirmation=confirmation
|
176
|
+
),
|
173
177
|
)
|
174
178
|
|
175
179
|
def find_batch(self):
|
@@ -776,8 +780,11 @@ class ClusterPicking(Component):
|
|
776
780
|
qty_done=quantity,
|
777
781
|
)
|
778
782
|
move_line.write({"qty_done": quantity, "result_package_id": bin_package.id})
|
779
|
-
|
780
|
-
zero_check =
|
783
|
+
# Only apply zero check if the product is of type "product".
|
784
|
+
zero_check = (
|
785
|
+
move_line.product_id.type == "product"
|
786
|
+
and move_line.picking_id.picking_type_id.shopfloor_zero_check
|
787
|
+
)
|
781
788
|
if zero_check and move_line.location_id.planned_qty_in_location_is_empty():
|
782
789
|
return self._response_for_zero_check(batch, move_line)
|
783
790
|
|
@@ -814,20 +821,24 @@ class ClusterPicking(Component):
|
|
814
821
|
# the lines have different destinations
|
815
822
|
return self._unload_next_package(batch)
|
816
823
|
|
817
|
-
def _data_for_unload_all(self, batch):
|
824
|
+
def _data_for_unload_all(self, batch, confirmation=None):
|
818
825
|
lines = self._lines_to_unload(batch)
|
819
826
|
# all the lines destinations are the same here, it looks
|
820
827
|
# only for the first one
|
821
828
|
first_line = fields.first(lines)
|
822
829
|
data = self.data.picking_batch(batch)
|
823
830
|
data.update({"location_dest": self.data.location(first_line.location_dest_id)})
|
831
|
+
if confirmation:
|
832
|
+
data.update({"confirmation": confirmation})
|
824
833
|
return data
|
825
834
|
|
826
|
-
def _data_for_unload_single(self, batch, package):
|
835
|
+
def _data_for_unload_single(self, batch, package, confirmation=None):
|
827
836
|
line = fields.first(
|
828
837
|
package.planned_move_line_ids.filtered(self._filter_for_unload)
|
829
838
|
)
|
830
839
|
data = self.data.picking_batch(batch)
|
840
|
+
if confirmation:
|
841
|
+
data.update({"confirmation": confirmation})
|
831
842
|
data.update(
|
832
843
|
{
|
833
844
|
"package": self.data.package(package),
|
@@ -1068,7 +1079,7 @@ class ClusterPicking(Component):
|
|
1068
1079
|
message=self.msg_store.no_package_or_lot_for_barcode(barcode),
|
1069
1080
|
)
|
1070
1081
|
|
1071
|
-
def set_destination_all(self, picking_batch_id, barcode, confirmation=
|
1082
|
+
def set_destination_all(self, picking_batch_id, barcode, confirmation=None):
|
1072
1083
|
"""Set the destination for all the lines of the batch with a dest. package
|
1073
1084
|
|
1074
1085
|
This method must be used only if all the move lines which have a destination
|
@@ -1109,10 +1120,10 @@ class ClusterPicking(Component):
|
|
1109
1120
|
batch, message=self.msg_store.dest_location_not_allowed()
|
1110
1121
|
)
|
1111
1122
|
|
1112
|
-
if
|
1123
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
1113
1124
|
first_line.location_dest_id, scanned_location
|
1114
1125
|
):
|
1115
|
-
return self._response_for_confirm_unload_all(batch)
|
1126
|
+
return self._response_for_confirm_unload_all(batch, confirmation=barcode)
|
1116
1127
|
|
1117
1128
|
self._unload_write_destination_on_lines(lines, scanned_location)
|
1118
1129
|
completion_info = self._actions_for("completion.info")
|
@@ -1220,7 +1231,7 @@ class ClusterPicking(Component):
|
|
1220
1231
|
return self._response_for_unload_set_destination(batch, package)
|
1221
1232
|
|
1222
1233
|
def unload_scan_destination(
|
1223
|
-
self, picking_batch_id, package_id, barcode, confirmation=
|
1234
|
+
self, picking_batch_id, package_id, barcode, confirmation=None
|
1224
1235
|
):
|
1225
1236
|
"""Scan the final destination for all the move lines moved with the Bin
|
1226
1237
|
|
@@ -1232,7 +1243,7 @@ class ClusterPicking(Component):
|
|
1232
1243
|
* unload_single: line is processed and the next bin can be unloaded
|
1233
1244
|
* confirm_unload_set_destination: the destination is valid but not the
|
1234
1245
|
expected, ask a confirmation. This state has to call again the
|
1235
|
-
endpoint with confirmation=
|
1246
|
+
endpoint with confirmation=barcode
|
1236
1247
|
* start_line: if the batch still has lines to pick
|
1237
1248
|
* start: if the batch is done. In this case, this method *has*
|
1238
1249
|
to handle the closing of the batch to create backorders.
|
@@ -1262,7 +1273,7 @@ class ClusterPicking(Component):
|
|
1262
1273
|
self._actions_for("lock").for_update(lines)
|
1263
1274
|
|
1264
1275
|
def _unload_scan_destination_lines(
|
1265
|
-
self, batch, package, lines, barcode, confirmation=
|
1276
|
+
self, batch, package, lines, barcode, confirmation=None
|
1266
1277
|
):
|
1267
1278
|
# Lock move lines that will be updated
|
1268
1279
|
self._lock_lines(lines)
|
@@ -1276,10 +1287,12 @@ class ClusterPicking(Component):
|
|
1276
1287
|
return self._response_for_unload_set_destination(
|
1277
1288
|
batch, package, message=self.msg_store.dest_location_not_allowed()
|
1278
1289
|
)
|
1279
|
-
if
|
1290
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
1280
1291
|
first_line.location_dest_id, scanned_location
|
1281
1292
|
):
|
1282
|
-
return self._response_for_confirm_unload_set_destination(
|
1293
|
+
return self._response_for_confirm_unload_set_destination(
|
1294
|
+
batch, package, confirmation=barcode
|
1295
|
+
)
|
1283
1296
|
|
1284
1297
|
self._unload_write_destination_on_lines(lines, scanned_location)
|
1285
1298
|
|
@@ -1384,7 +1397,7 @@ class ShopfloorClusterPickingValidator(Component):
|
|
1384
1397
|
return {
|
1385
1398
|
"picking_batch_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1386
1399
|
"barcode": {"required": True, "type": "string"},
|
1387
|
-
"confirmation": {"type": "
|
1400
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1388
1401
|
}
|
1389
1402
|
|
1390
1403
|
def unload_split(self):
|
@@ -1404,7 +1417,7 @@ class ShopfloorClusterPickingValidator(Component):
|
|
1404
1417
|
"picking_batch_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1405
1418
|
"package_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1406
1419
|
"barcode": {"required": True, "type": "string"},
|
1407
|
-
"confirmation": {"type": "
|
1420
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1408
1421
|
}
|
1409
1422
|
|
1410
1423
|
|
@@ -1599,6 +1612,7 @@ class ShopfloorClusterPickingValidatorResponse(Component):
|
|
1599
1612
|
def _schema_for_unload_all(self):
|
1600
1613
|
schema = self.schemas.picking_batch()
|
1601
1614
|
schema["location_dest"] = self.schemas._schema_dict_of(self.schemas.location())
|
1615
|
+
schema["confirmation"] = {"type": "string", "nullable": True, "required": False}
|
1602
1616
|
return schema
|
1603
1617
|
|
1604
1618
|
@property
|
@@ -1606,6 +1620,7 @@ class ShopfloorClusterPickingValidatorResponse(Component):
|
|
1606
1620
|
schema = self.schemas.picking_batch()
|
1607
1621
|
schema["package"] = self.schemas._schema_dict_of(self.schemas.package())
|
1608
1622
|
schema["location_dest"] = self.schemas._schema_dict_of(self.schemas.location())
|
1623
|
+
schema["confirmation"] = {"type": "string", "nullable": True, "required": False}
|
1609
1624
|
return schema
|
1610
1625
|
|
1611
1626
|
@property
|
@@ -259,13 +259,14 @@ class Delivery(Component):
|
|
259
259
|
# we added auto_join for this, otherwise, the ORM would search all pickings
|
260
260
|
# in the picking type, and then use IN (ids)
|
261
261
|
("picking_id.picking_type_id", "in", self.picking_types.ids),
|
262
|
+
("picking_id.state", "not in", ("done", "cancel")),
|
262
263
|
]
|
263
264
|
if no_qty_done:
|
264
265
|
domain.append(("qty_done", "=", 0))
|
265
266
|
return domain
|
266
267
|
|
267
268
|
def _lines_from_lot_domain(
|
268
|
-
self, lot, no_qty_done=True, product_qty=None, location=None
|
269
|
+
self, lot, no_qty_done=True, product_qty=None, location=None, picking=None
|
269
270
|
):
|
270
271
|
location_domain = (
|
271
272
|
[("picking_id.location_id", "=", location.id)] if location else []
|
@@ -283,10 +284,12 @@ class Delivery(Component):
|
|
283
284
|
("reserved_qty", ">=", product_qty),
|
284
285
|
]
|
285
286
|
)
|
287
|
+
if picking:
|
288
|
+
domain.extend([("picking_id", "=", picking.id)])
|
286
289
|
return domain
|
287
290
|
|
288
291
|
def _lines_from_product_domain(
|
289
|
-
self, product, no_qty_done=True, product_qty=None, location=None
|
292
|
+
self, product, no_qty_done=True, product_qty=None, location=None, picking=None
|
290
293
|
):
|
291
294
|
# TODO: searching lines is common to other scenario, to refactor
|
292
295
|
domain = expression.AND(
|
@@ -300,12 +303,17 @@ class Delivery(Component):
|
|
300
303
|
("reserved_qty", ">=", product_qty),
|
301
304
|
]
|
302
305
|
)
|
306
|
+
if picking:
|
307
|
+
domain.extend([("picking_id", "=", picking.id)])
|
303
308
|
return domain
|
304
309
|
|
305
|
-
def _lines_from_package_domain(self, package, no_qty_done=True):
|
306
|
-
|
310
|
+
def _lines_from_package_domain(self, package, no_qty_done=True, picking=None):
|
311
|
+
domain = expression.AND(
|
307
312
|
[self._lines_base_domain(no_qty_done), [("package_id", "=", package.id)]]
|
308
313
|
)
|
314
|
+
if picking:
|
315
|
+
domain.extend([("picking_id", "=", picking.id)])
|
316
|
+
return domain
|
309
317
|
|
310
318
|
def _deliver_product(self, picking, product, product_qty=None, location=None):
|
311
319
|
"""Handle the scan_deliver end point for a product."""
|
@@ -318,7 +326,11 @@ class Delivery(Component):
|
|
318
326
|
|
319
327
|
lines = self.env["stock.move.line"].search(
|
320
328
|
self._lines_from_product_domain(
|
321
|
-
product,
|
329
|
+
product,
|
330
|
+
no_qty_done=False,
|
331
|
+
product_qty=product_qty,
|
332
|
+
location=location,
|
333
|
+
picking=picking,
|
322
334
|
),
|
323
335
|
order="date_planned",
|
324
336
|
)
|
@@ -403,7 +415,11 @@ class Delivery(Component):
|
|
403
415
|
def _deliver_lot(self, picking, lot, product_qty=None, location=None):
|
404
416
|
lines = self.env["stock.move.line"].search(
|
405
417
|
self._lines_from_lot_domain(
|
406
|
-
lot,
|
418
|
+
lot,
|
419
|
+
no_qty_done=False,
|
420
|
+
product_qty=product_qty,
|
421
|
+
location=location,
|
422
|
+
picking=picking,
|
407
423
|
)
|
408
424
|
)
|
409
425
|
if not lines:
|
@@ -608,7 +624,9 @@ class Delivery(Component):
|
|
608
624
|
package = self.env["stock.quant.package"].browse(package_id).exists()
|
609
625
|
if package:
|
610
626
|
lines = self.env["stock.move.line"].search(
|
611
|
-
self._lines_from_package_domain(
|
627
|
+
self._lines_from_package_domain(
|
628
|
+
package, no_qty_done=False, picking=picking
|
629
|
+
)
|
612
630
|
)
|
613
631
|
if not lines:
|
614
632
|
return self._response_for_deliver(
|
@@ -80,7 +80,7 @@ class LocationContentTransfer(Component):
|
|
80
80
|
)
|
81
81
|
|
82
82
|
def _response_for_scan_destination_all(
|
83
|
-
self, pickings, message=None, confirmation_required=
|
83
|
+
self, pickings, message=None, confirmation_required=None
|
84
84
|
):
|
85
85
|
"""Transition to the 'scan_destination_all' state
|
86
86
|
|
@@ -116,7 +116,7 @@ class LocationContentTransfer(Component):
|
|
116
116
|
)
|
117
117
|
|
118
118
|
def _response_for_scan_destination(
|
119
|
-
self, location, next_content, message=None, confirmation_required=
|
119
|
+
self, location, next_content, message=None, confirmation_required=None
|
120
120
|
):
|
121
121
|
"""Transition to the 'scan_destination' state
|
122
122
|
|
@@ -461,7 +461,7 @@ class LocationContentTransfer(Component):
|
|
461
461
|
"""Lock move lines"""
|
462
462
|
self._actions_for("lock").for_update(lines)
|
463
463
|
|
464
|
-
def set_destination_all(self, location_id, barcode, confirmation=
|
464
|
+
def set_destination_all(self, location_id, barcode, confirmation=None):
|
465
465
|
"""Scan destination location for all the moves of the location
|
466
466
|
|
467
467
|
barcode is a stock.location for the destination
|
@@ -489,11 +489,11 @@ class LocationContentTransfer(Component):
|
|
489
489
|
return self._response_for_scan_destination_all(
|
490
490
|
pickings, message=self.msg_store.dest_location_not_allowed()
|
491
491
|
)
|
492
|
-
if
|
492
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
493
493
|
move_lines.location_dest_id, scanned_location
|
494
494
|
):
|
495
495
|
return self._response_for_scan_destination_all(
|
496
|
-
pickings, confirmation_required=
|
496
|
+
pickings, confirmation_required=barcode
|
497
497
|
)
|
498
498
|
self._lock_lines(move_lines)
|
499
499
|
|
@@ -663,7 +663,7 @@ class LocationContentTransfer(Component):
|
|
663
663
|
)
|
664
664
|
|
665
665
|
def set_destination_package(
|
666
|
-
self, location_id, package_level_id, barcode, confirmation=
|
666
|
+
self, location_id, package_level_id, barcode, confirmation=None
|
667
667
|
):
|
668
668
|
"""Scan destination location for package level
|
669
669
|
|
@@ -697,11 +697,11 @@ class LocationContentTransfer(Component):
|
|
697
697
|
package_level,
|
698
698
|
message=self.msg_store.dest_location_not_allowed(),
|
699
699
|
)
|
700
|
-
if
|
700
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
701
701
|
package_level.location_dest_id, scanned_location
|
702
702
|
):
|
703
703
|
return self._response_for_scan_destination(
|
704
|
-
location, package_level, confirmation_required=
|
704
|
+
location, package_level, confirmation_required=barcode
|
705
705
|
)
|
706
706
|
package_move_lines = package_level.move_line_ids
|
707
707
|
self._lock_lines(package_move_lines)
|
@@ -722,7 +722,7 @@ class LocationContentTransfer(Component):
|
|
722
722
|
)
|
723
723
|
|
724
724
|
def set_destination_line(
|
725
|
-
self, location_id, move_line_id, quantity, barcode, confirmation=
|
725
|
+
self, location_id, move_line_id, quantity, barcode, confirmation=None
|
726
726
|
):
|
727
727
|
"""Scan destination location for move line
|
728
728
|
|
@@ -754,11 +754,11 @@ class LocationContentTransfer(Component):
|
|
754
754
|
return self._response_for_scan_destination(
|
755
755
|
location, move_line, message=self.msg_store.dest_location_not_allowed()
|
756
756
|
)
|
757
|
-
if
|
757
|
+
if confirmation != barcode and self.is_dest_location_to_confirm(
|
758
758
|
move_line.location_dest_id, scanned_location
|
759
759
|
):
|
760
760
|
return self._response_for_scan_destination(
|
761
|
-
location, move_line, confirmation_required=
|
761
|
+
location, move_line, confirmation_required=barcode
|
762
762
|
)
|
763
763
|
|
764
764
|
self._lock_lines(move_line)
|
@@ -833,9 +833,10 @@ class LocationContentTransfer(Component):
|
|
833
833
|
splits the move to have no side-effect on the other package levels/move
|
834
834
|
lines.
|
835
835
|
|
836
|
-
|
837
|
-
|
838
|
-
|
836
|
+
If the move has been created by the shopfloor user it will be canceled
|
837
|
+
otherwise it is unreserved.
|
838
|
+
Then create an inventory at 0 in the move's source location, create a
|
839
|
+
second draft inventory (if none exists) to check later.
|
839
840
|
|
840
841
|
Transitions:
|
841
842
|
* start: no more content to move
|
@@ -861,15 +862,23 @@ class LocationContentTransfer(Component):
|
|
861
862
|
# We need to set qty_done at 0 because otherwise
|
862
863
|
# the move_line will not be deleted
|
863
864
|
package_move.move_line_ids.write({"qty_done": 0})
|
864
|
-
|
865
|
-
|
865
|
+
package = package_level.package_id
|
866
|
+
if (
|
867
|
+
self.is_allow_move_create()
|
868
|
+
and self.env.user == package_move.picking_id.create_uid
|
869
|
+
):
|
870
|
+
# Owned by the user deleting the move
|
871
|
+
package_move._action_cancel()
|
872
|
+
else:
|
873
|
+
# Not owned only unreserved
|
874
|
+
package_move._do_unreserve()
|
875
|
+
package_move._recompute_state()
|
866
876
|
# Create an inventory at 0 in the move's source location
|
867
877
|
inventory.create_stock_issue(package_move, location, package, lot)
|
868
878
|
# Create a draft inventory to control stock
|
869
879
|
inventory.create_control_stock(
|
870
880
|
location, package_move.product_id, package, lot
|
871
881
|
)
|
872
|
-
package_move._action_cancel()
|
873
882
|
# remove the package level (this is what does the `picking.do_unreserve()`
|
874
883
|
# method, but here we want to unreserve+unlink this package alone)
|
875
884
|
move_lines = self._find_transfer_move_lines(location)
|
@@ -882,9 +891,10 @@ class LocationContentTransfer(Component):
|
|
882
891
|
splits the move to have no side-effect on the other package levels/move
|
883
892
|
lines.
|
884
893
|
|
885
|
-
|
886
|
-
|
887
|
-
|
894
|
+
If the move has been created by the shopfloor user it will be canceled
|
895
|
+
otherwise it will be unreserved.
|
896
|
+
Then an inventory is created at 0 in the move's source location,
|
897
|
+
create a second draft inventory (if none exists) to check later.
|
888
898
|
|
889
899
|
Transitions:
|
890
900
|
* start: no more content to move
|
@@ -906,15 +916,19 @@ class LocationContentTransfer(Component):
|
|
906
916
|
# We need to set qty_done at 0 because otherwise
|
907
917
|
# the move_line will not be deleted
|
908
918
|
move_line.qty_done = 0
|
909
|
-
|
910
|
-
|
919
|
+
if self.is_allow_move_create() and self.env.user == move.picking_id.create_uid:
|
920
|
+
# Owned by the user deleting the move
|
921
|
+
move._action_cancel()
|
922
|
+
else:
|
923
|
+
# Not owned unreserve
|
924
|
+
move._do_unreserve()
|
925
|
+
move._recompute_state()
|
911
926
|
# Create an inventory at 0 in the move's source location
|
912
927
|
inventory.create_stock_issue(move, move_line_src_location, package, lot)
|
913
928
|
# Create a draft inventory to control stock
|
914
929
|
inventory.create_control_stock(
|
915
930
|
move_line_src_location, move.product_id, package, lot
|
916
931
|
)
|
917
|
-
move._action_cancel()
|
918
932
|
move_lines = self._find_transfer_move_lines(location)
|
919
933
|
return self._response_for_start_single(move_lines.mapped("picking_id"))
|
920
934
|
|
@@ -976,7 +990,7 @@ class ShopfloorLocationContentTransferValidator(Component):
|
|
976
990
|
return {
|
977
991
|
"location_id": {"coerce": to_int, "required": True, "type": "integer"},
|
978
992
|
"barcode": {"required": True, "type": "string"},
|
979
|
-
"confirmation": {"type": "
|
993
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
980
994
|
}
|
981
995
|
|
982
996
|
def go_to_single(self):
|
@@ -1001,7 +1015,7 @@ class ShopfloorLocationContentTransferValidator(Component):
|
|
1001
1015
|
"location_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1002
1016
|
"package_level_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1003
1017
|
"barcode": {"required": True, "type": "string"},
|
1004
|
-
"confirmation": {"type": "
|
1018
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1005
1019
|
}
|
1006
1020
|
|
1007
1021
|
def set_destination_line(self):
|
@@ -1010,7 +1024,7 @@ class ShopfloorLocationContentTransferValidator(Component):
|
|
1010
1024
|
"move_line_id": {"coerce": to_int, "required": True, "type": "integer"},
|
1011
1025
|
"quantity": {"coerce": to_float, "required": True, "type": "float"},
|
1012
1026
|
"barcode": {"required": True, "type": "string"},
|
1013
|
-
"confirmation": {"type": "
|
1027
|
+
"confirmation": {"type": "string", "nullable": True, "required": False},
|
1014
1028
|
}
|
1015
1029
|
|
1016
1030
|
def postpone_package(self):
|
@@ -1077,7 +1091,7 @@ class ShopfloorLocationContentTransferValidatorResponse(Component):
|
|
1077
1091
|
"package_levels": self.schemas._schema_list_of(package_level_schema),
|
1078
1092
|
"move_lines": self.schemas._schema_list_of(move_line_schema),
|
1079
1093
|
"confirmation_required": {
|
1080
|
-
"type": "
|
1094
|
+
"type": "string",
|
1081
1095
|
"nullable": True,
|
1082
1096
|
"required": False,
|
1083
1097
|
},
|
@@ -1092,7 +1106,7 @@ class ShopfloorLocationContentTransferValidatorResponse(Component):
|
|
1092
1106
|
"package_level": self.schemas._schema_dict_of(schema_package_level),
|
1093
1107
|
"move_line": self.schemas._schema_dict_of(schema_move_line),
|
1094
1108
|
"confirmation_required": {
|
1095
|
-
"type": "
|
1109
|
+
"type": "string",
|
1096
1110
|
"nullable": True,
|
1097
1111
|
"required": False,
|
1098
1112
|
},
|
@@ -29,6 +29,9 @@ class SinglePackTransfer(Component):
|
|
29
29
|
return {
|
30
30
|
"id": package_level.id,
|
31
31
|
"name": package.name,
|
32
|
+
"weight_uom": package.weight_uom_id.name,
|
33
|
+
"weight": package.pack_weight,
|
34
|
+
"estimated_weight_kg": package.estimated_pack_weight_kg,
|
32
35
|
"location_src": self.data.location(package.location_id),
|
33
36
|
"location_dest": self.data.location(package_level.location_dest_id),
|
34
37
|
"products": self.data.products(move_lines.product_id),
|
@@ -38,9 +41,9 @@ class SinglePackTransfer(Component):
|
|
38
41
|
def _response_for_start(self, message=None, popup=None):
|
39
42
|
return self._response(next_state="start", message=message, popup=popup)
|
40
43
|
|
41
|
-
def _response_for_confirm_start(self, package_level, message=None):
|
44
|
+
def _response_for_confirm_start(self, package_level, message=None, barcode=""):
|
42
45
|
data = self._data_after_package_scanned(package_level)
|
43
|
-
data["confirmation_required"] =
|
46
|
+
data["confirmation_required"] = barcode
|
44
47
|
return self._response(
|
45
48
|
next_state="start",
|
46
49
|
data=data,
|
@@ -48,7 +51,7 @@ class SinglePackTransfer(Component):
|
|
48
51
|
)
|
49
52
|
|
50
53
|
def _response_for_scan_location(
|
51
|
-
self, package_level, message=None, confirmation_required=
|
54
|
+
self, package_level, message=None, confirmation_required=None
|
52
55
|
):
|
53
56
|
data = self._data_after_package_scanned(package_level)
|
54
57
|
data["confirmation_required"] = confirmation_required
|
@@ -58,7 +61,7 @@ class SinglePackTransfer(Component):
|
|
58
61
|
message=message,
|
59
62
|
)
|
60
63
|
|
61
|
-
def _scan_source(self, barcode, confirmation=
|
64
|
+
def _scan_source(self, barcode, confirmation=None):
|
62
65
|
"""Search a package"""
|
63
66
|
search = self._actions_for("search")
|
64
67
|
location = search.location_from_scan(barcode)
|
@@ -90,7 +93,7 @@ class SinglePackTransfer(Component):
|
|
90
93
|
|
91
94
|
return (None, package)
|
92
95
|
|
93
|
-
def start(self, barcode, confirmation=
|
96
|
+
def start(self, barcode, confirmation=None):
|
94
97
|
picking_types = self.picking_types
|
95
98
|
message, package = self._scan_source(barcode, confirmation)
|
96
99
|
if message:
|
@@ -161,9 +164,11 @@ class SinglePackTransfer(Component):
|
|
161
164
|
message=self.msg_store.no_putaway_destination_available()
|
162
165
|
)
|
163
166
|
|
164
|
-
if package_level.is_done and
|
167
|
+
if package_level.is_done and confirmation != barcode:
|
165
168
|
return self._response_for_confirm_start(
|
166
|
-
package_level,
|
169
|
+
package_level,
|
170
|
+
message=self.msg_store.already_running_ask_confirmation(),
|
171
|
+
barcode=barcode,
|
167
172
|
)
|
168
173
|
if not package_level.is_done:
|
169
174
|
package_level.is_done = True
|
@@ -202,7 +207,7 @@ class SinglePackTransfer(Component):
|
|
202
207
|
def _is_move_state_valid(self, moves):
|
203
208
|
return all(move.state != "cancel" for move in moves)
|
204
209
|
|
205
|
-
def validate(self, package_level_id, location_barcode, confirmation=
|
210
|
+
def validate(self, package_level_id, location_barcode, confirmation=None):
|
206
211
|
"""Validate the transfer"""
|
207
212
|
search = self._actions_for("search")
|
208
213
|
|
@@ -232,12 +237,12 @@ class SinglePackTransfer(Component):
|
|
232
237
|
package_level, message=self.msg_store.dest_location_not_allowed()
|
233
238
|
)
|
234
239
|
|
235
|
-
if
|
240
|
+
if confirmation != location_barcode and self.is_dest_location_to_confirm(
|
236
241
|
package_level.location_dest_id, scanned_location
|
237
242
|
):
|
238
243
|
return self._response_for_scan_location(
|
239
244
|
package_level,
|
240
|
-
confirmation_required=
|
245
|
+
confirmation_required=location_barcode,
|
241
246
|
message=self.msg_store.confirm_location_changed(
|
242
247
|
package_level.location_dest_id, scanned_location
|
243
248
|
),
|
@@ -280,6 +285,17 @@ class SinglePackTransfer(Component):
|
|
280
285
|
return self._response_for_start(message=self.msg_store.already_done())
|
281
286
|
|
282
287
|
package_level.is_done = False
|
288
|
+
if (
|
289
|
+
self.is_allow_move_create()
|
290
|
+
and package_level.picking_id.create_uid == self.env.user
|
291
|
+
):
|
292
|
+
# Cancel the transfer when it has been created by the shopfloor user
|
293
|
+
moves.picking_id.action_cancel()
|
294
|
+
else:
|
295
|
+
# Not owned only unassign the user
|
296
|
+
stock = self._actions_for("stock")
|
297
|
+
stock.unmark_move_line_as_picked(moves.move_line_ids)
|
298
|
+
|
283
299
|
return self._response_for_start(
|
284
300
|
message=self.msg_store.confirm_canceled_scan_next_pack()
|
285
301
|
)
|
@@ -295,7 +311,7 @@ class SinglePackTransferValidator(Component):
|
|
295
311
|
def start(self):
|
296
312
|
return {
|
297
313
|
"barcode": {"type": "string", "nullable": False, "required": True},
|
298
|
-
"confirmation": {"type": "
|
314
|
+
"confirmation": {"type": "string", "required": False},
|
299
315
|
}
|
300
316
|
|
301
317
|
def cancel(self):
|
@@ -307,7 +323,7 @@ class SinglePackTransferValidator(Component):
|
|
307
323
|
return {
|
308
324
|
"package_level_id": {"coerce": to_int, "required": True, "type": "integer"},
|
309
325
|
"location_barcode": {"type": "string", "nullable": False, "required": True},
|
310
|
-
"confirmation": {"type": "
|
326
|
+
"confirmation": {"type": "string", "required": False},
|
311
327
|
}
|
312
328
|
|
313
329
|
|
@@ -347,6 +363,13 @@ class SinglePackTransferValidatorResponse(Component):
|
|
347
363
|
return {
|
348
364
|
"id": {"required": required, "type": "integer"},
|
349
365
|
"name": {"type": "string", "nullable": False, "required": required},
|
366
|
+
"weight_uom": {"type": "string", "nullable": False, "required": required},
|
367
|
+
"weight": {"type": "float", "nullable": False, "required": required},
|
368
|
+
"estimated_weight_kg": {
|
369
|
+
"type": "float",
|
370
|
+
"nullable": False,
|
371
|
+
"required": required,
|
372
|
+
},
|
350
373
|
"location_src": {"type": "dict", "schema": self.schemas.location()},
|
351
374
|
"location_dest": {"type": "dict", "schema": self.schemas.location()},
|
352
375
|
"products": {
|
@@ -359,7 +382,7 @@ class SinglePackTransferValidatorResponse(Component):
|
|
359
382
|
def _schema_confirmation_required(self):
|
360
383
|
return {
|
361
384
|
"confirmation_required": {
|
362
|
-
"type": "
|
385
|
+
"type": "string",
|
363
386
|
"nullable": True,
|
364
387
|
"required": False,
|
365
388
|
},
|