odoo-addon-shopfloor 16.0.2.6.0__py3-none-any.whl → 16.0.2.7.0.1__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/message.py +50 -5
- odoo/addons/shopfloor/actions/stock_unreserve.py +11 -4
- odoo/addons/shopfloor/i18n/ca.po +35 -13
- odoo/addons/shopfloor/i18n/de.po +35 -13
- odoo/addons/shopfloor/i18n/es_AR.po +44 -14
- odoo/addons/shopfloor/i18n/it.po +44 -14
- odoo/addons/shopfloor/i18n/pt_BR.po +35 -13
- odoo/addons/shopfloor/i18n/shopfloor.pot +35 -13
- odoo/addons/shopfloor/services/checkout.py +129 -105
- odoo/addons/shopfloor/services/delivery.py +89 -64
- odoo/addons/shopfloor/services/location_content_transfer.py +34 -18
- odoo/addons/shopfloor/services/service.py +52 -15
- odoo/addons/shopfloor/static/description/index.html +1 -1
- odoo/addons/shopfloor/tests/test_checkout_scan.py +11 -3
- odoo/addons/shopfloor/tests/test_checkout_scan_line.py +35 -4
- odoo/addons/shopfloor/tests/test_checkout_select.py +3 -1
- odoo/addons/shopfloor/tests/test_delivery_scan_deliver.py +143 -1
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_line.py +1 -1
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_pack.py +1 -1
- odoo/addons/shopfloor/tests/test_location_content_transfer_start.py +24 -1
- {odoo_addon_shopfloor-16.0.2.6.0.dist-info → odoo_addon_shopfloor-16.0.2.7.0.1.dist-info}/METADATA +2 -2
- {odoo_addon_shopfloor-16.0.2.6.0.dist-info → odoo_addon_shopfloor-16.0.2.7.0.1.dist-info}/RECORD +26 -26
- {odoo_addon_shopfloor-16.0.2.6.0.dist-info → odoo_addon_shopfloor-16.0.2.7.0.1.dist-info}/WHEEL +0 -0
- {odoo_addon_shopfloor-16.0.2.6.0.dist-info → odoo_addon_shopfloor-16.0.2.7.0.1.dist-info}/top_level.txt +0 -0
@@ -140,54 +140,61 @@ class Delivery(Component):
|
|
140
140
|
barcode_valid = bool(picking)
|
141
141
|
|
142
142
|
if picking:
|
143
|
-
message = self.
|
143
|
+
message = self._check_picking_processible(picking)
|
144
144
|
if message:
|
145
145
|
return self._response_for_deliver(location=location, message=message)
|
146
146
|
|
147
147
|
if picking_id:
|
148
148
|
picking = self.env["stock.picking"].browse(picking_id)
|
149
149
|
|
150
|
-
# Validate picking anyway
|
151
150
|
if not barcode_valid:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
151
|
+
handlers_by_type = {
|
152
|
+
"package": self._scan_deliver__by_package,
|
153
|
+
"product": self._scan_deliver__by_product,
|
154
|
+
"packaging": self._scan_deliver__by_packaging,
|
155
|
+
"lot": self._scan_deliver__by_lot,
|
156
|
+
"location": self._scan_deliver__by_location,
|
157
|
+
}
|
158
|
+
search_result = search.find(barcode, handlers_by_type.keys())
|
159
|
+
handler = handlers_by_type.get(search_result.type)
|
160
|
+
if handler:
|
161
|
+
result = handler(search_result.record, picking, location)
|
162
|
+
if result:
|
163
|
+
return result
|
164
|
+
return self._scan_deliver__fallback(picking, location, barcode_valid)
|
165
|
+
|
166
|
+
def _scan_deliver__by_package(self, package, picking, location):
|
167
|
+
return self._deliver_package(picking, package, location)
|
168
|
+
|
169
|
+
def _scan_deliver__by_product(self, product, picking, location):
|
170
|
+
return self._deliver_product(picking, product, product_qty=1, location=location)
|
171
|
+
|
172
|
+
def _scan_deliver__by_packaging(self, packaging, picking, location):
|
173
|
+
# By scanning a packaging, we want to process
|
174
|
+
# the full quantity of the packaging
|
175
|
+
packaging_qty = packaging.product_uom_id._compute_quantity(
|
176
|
+
packaging.qty, packaging.product_id.uom_id
|
177
|
+
)
|
178
|
+
return self._deliver_product(
|
179
|
+
picking,
|
180
|
+
packaging.product_id,
|
181
|
+
product_qty=packaging_qty,
|
182
|
+
location=location,
|
183
|
+
)
|
177
184
|
|
178
|
-
|
179
|
-
|
180
|
-
if lot:
|
181
|
-
return self._deliver_lot(picking, lot, product_qty=1, location=location)
|
185
|
+
def _scan_deliver__by_lot(self, lot, picking, location):
|
186
|
+
return self._deliver_lot(picking, lot, product_qty=1, location=location)
|
182
187
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
)
|
188
|
-
|
189
|
-
|
188
|
+
def _scan_deliver__by_location(self, scanned_location, picking, location):
|
189
|
+
if scanned_location.is_sublocation_of(
|
190
|
+
self.picking_types.mapped("default_location_src_id")
|
191
|
+
):
|
192
|
+
message = self.msg_store.location_src_set_to_sublocation(scanned_location)
|
193
|
+
return self._response_for_deliver(
|
194
|
+
location=scanned_location, message=message
|
195
|
+
)
|
190
196
|
|
197
|
+
def _scan_deliver__fallback(self, picking, location, barcode_valid):
|
191
198
|
message = self.msg_store.barcode_not_found() if not barcode_valid else None
|
192
199
|
return self._response_for_deliver(
|
193
200
|
picking=picking, location=location, message=message
|
@@ -228,6 +235,12 @@ class Delivery(Component):
|
|
228
235
|
lines = package.move_line_ids.filtered(
|
229
236
|
lambda l: l.state in ("assigned", "partially_available")
|
230
237
|
)
|
238
|
+
if not lines:
|
239
|
+
return self._response_for_deliver(
|
240
|
+
picking=picking,
|
241
|
+
location=location,
|
242
|
+
message=self.msg_store.cannot_move_something_in_picking_type(),
|
243
|
+
)
|
231
244
|
# State of the picking might change while we reach this point: check again!
|
232
245
|
message = self._check_picking_status(lines.mapped("picking_id"))
|
233
246
|
if message:
|
@@ -240,12 +253,9 @@ class Delivery(Component):
|
|
240
253
|
]
|
241
254
|
)
|
242
255
|
return self._response_for_deliver(location=location, message=message)
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
location=location,
|
247
|
-
message=self.msg_store.cannot_move_something_in_picking_type(),
|
248
|
-
)
|
256
|
+
message = self._check_picking_type(lines.mapped("picking_id"))
|
257
|
+
if message:
|
258
|
+
return self._response_for_deliver(location=location, message=message)
|
249
259
|
# TODO add a message if any of the lines already had a qty_done > 0
|
250
260
|
new_picking = fields.first(lines.mapped("picking_id"))
|
251
261
|
if self._set_lines_done(lines):
|
@@ -255,12 +265,9 @@ class Delivery(Component):
|
|
255
265
|
return self._response_for_deliver(picking=new_picking, location=location)
|
256
266
|
|
257
267
|
def _lines_base_domain(self, no_qty_done=True):
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
("picking_id.picking_type_id", "in", self.picking_types.ids),
|
262
|
-
("picking_id.state", "not in", ("done", "cancel")),
|
263
|
-
]
|
268
|
+
# we added auto_join for this, otherwise, the ORM would search all pickings
|
269
|
+
# in the picking type, and then use IN (ids)
|
270
|
+
domain = []
|
264
271
|
if no_qty_done:
|
265
272
|
domain.append(("qty_done", "=", 0))
|
266
273
|
return domain
|
@@ -297,6 +304,16 @@ class Delivery(Component):
|
|
297
304
|
)
|
298
305
|
if location:
|
299
306
|
domain.extend([("location_id", "=", location.id)])
|
307
|
+
else:
|
308
|
+
domain.extend(
|
309
|
+
[
|
310
|
+
(
|
311
|
+
"location_id",
|
312
|
+
"child_of",
|
313
|
+
self.picking_types.default_location_src_id.ids,
|
314
|
+
)
|
315
|
+
]
|
316
|
+
)
|
300
317
|
if product_qty:
|
301
318
|
domain.extend(
|
302
319
|
[
|
@@ -351,6 +368,12 @@ class Delivery(Component):
|
|
351
368
|
message=self.msg_store.product_in_multiple_sublocation(product),
|
352
369
|
)
|
353
370
|
|
371
|
+
message = self._check_picking_type(lines.mapped("picking_id"))
|
372
|
+
if message:
|
373
|
+
return self._response_for_deliver(location=location, message=message)
|
374
|
+
lines = lines.filtered(
|
375
|
+
lambda l: l.move_id.picking_type_id in self.picking_types
|
376
|
+
)
|
354
377
|
# State of the picking might change while we reach this point: check again!
|
355
378
|
message = self._check_picking_status(lines.mapped("picking_id"))
|
356
379
|
if message:
|
@@ -413,15 +436,14 @@ class Delivery(Component):
|
|
413
436
|
return self._response_for_deliver(new_picking, location=location)
|
414
437
|
|
415
438
|
def _deliver_lot(self, picking, lot, product_qty=None, location=None):
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
picking=picking,
|
423
|
-
)
|
439
|
+
domain = self._lines_from_lot_domain(
|
440
|
+
lot,
|
441
|
+
no_qty_done=False,
|
442
|
+
product_qty=product_qty,
|
443
|
+
location=location,
|
444
|
+
picking=picking,
|
424
445
|
)
|
446
|
+
lines = self.env["stock.move.line"].search(domain)
|
425
447
|
if not lines:
|
426
448
|
return self._response_for_deliver(
|
427
449
|
picking,
|
@@ -439,6 +461,9 @@ class Delivery(Component):
|
|
439
461
|
message=self.msg_store.lot_in_multiple_sublocation(lot),
|
440
462
|
)
|
441
463
|
|
464
|
+
message = self._check_picking_type(lines.mapped("picking_id"))
|
465
|
+
if message:
|
466
|
+
return self._response_for_deliver(location=location, message=message)
|
442
467
|
# State of the picking might change while we reach this point: check again!
|
443
468
|
message = self._check_picking_status(lines.mapped("picking_id"))
|
444
469
|
if message:
|
@@ -549,7 +574,7 @@ class Delivery(Component):
|
|
549
574
|
* deliver: with information about the stock.picking
|
550
575
|
"""
|
551
576
|
picking = self.env["stock.picking"].browse(picking_id)
|
552
|
-
message = self.
|
577
|
+
message = self._check_picking_processible(picking)
|
553
578
|
if message:
|
554
579
|
return self.list_stock_picking(message=message)
|
555
580
|
if picking:
|
@@ -566,7 +591,7 @@ class Delivery(Component):
|
|
566
591
|
* deliver: always return here with updated data
|
567
592
|
"""
|
568
593
|
picking = self.env["stock.picking"].browse(picking_id)
|
569
|
-
message = self.
|
594
|
+
message = self._check_picking_processible(picking)
|
570
595
|
if message:
|
571
596
|
return self._response_for_deliver(message=message)
|
572
597
|
package = self.env["stock.quant.package"].browse(package_id).exists()
|
@@ -591,7 +616,7 @@ class Delivery(Component):
|
|
591
616
|
* deliver: always return here with updated data
|
592
617
|
"""
|
593
618
|
picking = self.env["stock.picking"].browse(picking_id)
|
594
|
-
message = self.
|
619
|
+
message = self._check_picking_processible(picking)
|
595
620
|
if message:
|
596
621
|
return self._response_for_deliver(message=message)
|
597
622
|
line = self.env["stock.move.line"].browse(move_line_id).exists()
|
@@ -618,7 +643,7 @@ class Delivery(Component):
|
|
618
643
|
* deliver: always return here with updated data
|
619
644
|
"""
|
620
645
|
picking = self.env["stock.picking"].browse(picking_id)
|
621
|
-
message = self.
|
646
|
+
message = self._check_picking_processible(picking)
|
622
647
|
if message:
|
623
648
|
return self._response_for_deliver(message=message)
|
624
649
|
package = self.env["stock.quant.package"].browse(package_id).exists()
|
@@ -651,7 +676,7 @@ class Delivery(Component):
|
|
651
676
|
* deliver: always return here with updated data
|
652
677
|
"""
|
653
678
|
picking = self.env["stock.picking"].browse(picking_id)
|
654
|
-
message = self.
|
679
|
+
message = self._check_picking_processible(picking)
|
655
680
|
if message:
|
656
681
|
return self._response_for_deliver(message=message)
|
657
682
|
line = self.env["stock.move.line"].browse(move_line_id).exists()
|
@@ -681,7 +706,7 @@ class Delivery(Component):
|
|
681
706
|
* confirm_done: when not all lines of the stock.picking are done
|
682
707
|
"""
|
683
708
|
picking = self.env["stock.picking"].browse(picking_id)
|
684
|
-
message = self.
|
709
|
+
message = self._check_picking_processible(picking)
|
685
710
|
if message:
|
686
711
|
return self._response_for_deliver(message=message)
|
687
712
|
if self._action_picking_done(picking):
|
@@ -342,7 +342,9 @@ class LocationContentTransfer(Component):
|
|
342
342
|
|
343
343
|
unreserved_moves = self.env["stock.move"].browse()
|
344
344
|
if self.work.menu.allow_unreserve_other_moves:
|
345
|
-
message = unreserve.check_unreserve(
|
345
|
+
message = unreserve.check_unreserve(
|
346
|
+
location, move_lines, allowed_types=self.picking_types
|
347
|
+
)
|
346
348
|
if message:
|
347
349
|
return self._response_for_start(message=message)
|
348
350
|
move_lines, unreserved_moves = unreserve.unreserve_moves(
|
@@ -602,20 +604,31 @@ class LocationContentTransfer(Component):
|
|
602
604
|
)
|
603
605
|
|
604
606
|
search = self._actions_for("search")
|
607
|
+
handlers = {
|
608
|
+
"package": self._scan_line__by_package,
|
609
|
+
"product": self._scan_line__by_product,
|
610
|
+
"packaging": self._scan_line__by_packaging,
|
611
|
+
"lot": self._scan_line__by_lot,
|
612
|
+
"none": self._scan_line__fallback,
|
613
|
+
}
|
614
|
+
search_result = search.find(barcode, types=handlers.keys())
|
615
|
+
handler = handlers.get(search_result.type, self._scan_line__fallback)
|
616
|
+
# handler might've been called but returned no response.
|
617
|
+
# I.E. package is scanned but doesn't matches move_line's package.
|
618
|
+
# Call explicitely fallback in such case
|
619
|
+
response = handler(search_result.record, move_line, location)
|
620
|
+
return response or self._scan_line__fallback(
|
621
|
+
search_result.record, move_line, location
|
622
|
+
)
|
605
623
|
|
606
|
-
|
607
|
-
if
|
624
|
+
def _scan_line__by_package(self, package, move_line, location):
|
625
|
+
if move_line.package_id == package:
|
608
626
|
# In case we have a source package but no package level because if
|
609
627
|
# we have a package level, we would use "scan_package".
|
610
628
|
return self._response_for_scan_destination(location, move_line)
|
611
629
|
|
612
|
-
|
613
|
-
if
|
614
|
-
packaging = search.packaging_from_scan(barcode)
|
615
|
-
if packaging:
|
616
|
-
product = packaging.product_id
|
617
|
-
|
618
|
-
if product and product == move_line.product_id:
|
630
|
+
def _scan_line__by_product(self, product, move_line, location):
|
631
|
+
if product == move_line.product_id:
|
619
632
|
if product.tracking in ("lot", "serial"):
|
620
633
|
move_lines = self._find_transfer_move_lines(location)
|
621
634
|
return self._response_for_start_single(
|
@@ -625,18 +638,21 @@ class LocationContentTransfer(Component):
|
|
625
638
|
else:
|
626
639
|
return self._response_for_scan_destination(location, move_line)
|
627
640
|
|
628
|
-
|
629
|
-
|
641
|
+
def _scan_line__by_packaging(self, packaging, move_line, location):
|
642
|
+
return self._scan_line__by_product(packaging.product_id, move_line, location)
|
643
|
+
|
644
|
+
def _scan_line__by_lot(self, lot, move_line, location):
|
645
|
+
if lot == move_line.lot_id:
|
630
646
|
return self._response_for_scan_destination(location, move_line)
|
631
647
|
|
648
|
+
def _scan_line__fallback(self, record, move_line, location):
|
632
649
|
# Nothing matches what is expected from the move line.
|
633
650
|
move_lines = self._find_transfer_move_lines(location)
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
)
|
651
|
+
if record:
|
652
|
+
return self._response_for_start_single(
|
653
|
+
move_lines.mapped("picking_id"),
|
654
|
+
message=self.msg_store.wrong_record(record),
|
655
|
+
)
|
640
656
|
return self._response_for_start_single(
|
641
657
|
move_lines.mapped("picking_id"), message=self.msg_store.barcode_not_found()
|
642
658
|
)
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# Copyright 2020 Akretion (http://www.akretion.com)
|
3
3
|
# Copyright 2020-2021 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
|
4
4
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
5
|
-
from odoo import _, exceptions
|
5
|
+
from odoo import _, exceptions, fields
|
6
|
+
from odoo.osv.expression import AND
|
6
7
|
|
7
8
|
from odoo.addons.component.core import AbstractComponent
|
8
9
|
|
@@ -34,6 +35,22 @@ class BaseShopfloorProcess(AbstractComponent):
|
|
34
35
|
"""Return picking types for the menu"""
|
35
36
|
return self.work.menu.picking_type_ids
|
36
37
|
|
38
|
+
def _get_pickings_base_domain(self):
|
39
|
+
return [
|
40
|
+
("state", "not in", ("done", "cancel")),
|
41
|
+
("location_id", "child_of", self.picking_types.default_location_src_id.ids),
|
42
|
+
]
|
43
|
+
|
44
|
+
def _get_pickings_for_package(self, package, **kwargs):
|
45
|
+
domain = self._get_pickings_base_domain()
|
46
|
+
package_domain = [("move_line_ids.package_id", "=", package.id)]
|
47
|
+
return self.env["stock.picking"].search(AND([domain, package_domain]), **kwargs)
|
48
|
+
|
49
|
+
def _get_pickings_for_product(self, product, **kwargs):
|
50
|
+
domain = self._get_pickings_base_domain()
|
51
|
+
product_domain = [("move_line_ids.product_id", "=", product.id)]
|
52
|
+
return self.env["stock.picking"].search(AND([domain, product_domain]), **kwargs)
|
53
|
+
|
37
54
|
@property
|
38
55
|
def picking_types(self):
|
39
56
|
if not hasattr(self.work, "picking_types"):
|
@@ -72,21 +89,41 @@ class BaseShopfloorProcess(AbstractComponent):
|
|
72
89
|
sort_order_custom_code=self.sort_order_custom_code,
|
73
90
|
)
|
74
91
|
|
75
|
-
def
|
76
|
-
|
92
|
+
def _check_picking_consistency(self, pickings):
|
93
|
+
if not pickings.exists():
|
94
|
+
return self.msg_store.stock_picking_not_found()
|
95
|
+
|
96
|
+
def _check_picking_type(self, pickings):
|
97
|
+
"""Check if the pickings have the right expected type."""
|
98
|
+
if not any(
|
99
|
+
picking.picking_type_id in self.picking_types for picking in pickings
|
100
|
+
):
|
101
|
+
return self.msg_store.reserved_for_other_picking_type(
|
102
|
+
fields.first(pickings)
|
103
|
+
)
|
77
104
|
|
78
|
-
|
79
|
-
|
80
|
-
""
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
105
|
+
def _check_picking_status(self, pickings, states=("assigned",)):
|
106
|
+
"""Checks if the picking exists, is already done or canceled."""
|
107
|
+
if not any(picking.state != "done" for picking in pickings):
|
108
|
+
return self.msg_store.already_done()
|
109
|
+
if not any(picking.state != "cancel" for picking in pickings):
|
110
|
+
return self.msg_store.transfer_canceled()
|
111
|
+
if not any(
|
112
|
+
picking.state in states for picking in pickings
|
113
|
+
): # the picking must be ready
|
114
|
+
return self.msg_store.stock_picking_not_available(fields.first(pickings))
|
115
|
+
|
116
|
+
def _check_picking_processible(self, pickings, states=("assigned",)):
|
117
|
+
"""Check if given pickings can be processed"""
|
118
|
+
message = self._check_picking_consistency(pickings)
|
119
|
+
if message:
|
120
|
+
return message
|
121
|
+
message = self._check_picking_type(pickings)
|
122
|
+
if message:
|
123
|
+
return message
|
124
|
+
message = self._check_picking_status(pickings, states=states)
|
125
|
+
if message:
|
126
|
+
return message
|
90
127
|
|
91
128
|
def is_src_location_valid(self, location):
|
92
129
|
"""Check the source location is valid for given process.
|
@@ -367,7 +367,7 @@ ul.auto-toc {
|
|
367
367
|
!! This file is generated by oca-gen-addon-readme !!
|
368
368
|
!! changes will be overwritten. !!
|
369
369
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
370
|
-
!! source digest: sha256:
|
370
|
+
!! source digest: sha256:986a6c228f6ee438330907a4be64b74f7291bb1acfc6498a2617ed5e69777d9e
|
371
371
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
372
372
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/wms/tree/16.0/shopfloor"><img alt="OCA/wms" src="https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/wms-16-0/wms-16-0-shopfloor"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/wms&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
373
373
|
<p>Shopfloor is a barcode scanner application for internal warehouse operations.</p>
|
@@ -41,7 +41,10 @@ class CheckoutScanCase(CheckoutCommonCase):
|
|
41
41
|
self.assert_response(
|
42
42
|
response,
|
43
43
|
next_state="select_document",
|
44
|
-
message={
|
44
|
+
message={
|
45
|
+
"message_type": "error",
|
46
|
+
"body": "No transfer found for barcode A",
|
47
|
+
},
|
45
48
|
data={"restrict_scan_first": True},
|
46
49
|
)
|
47
50
|
|
@@ -56,7 +59,10 @@ class CheckoutScanCase(CheckoutCommonCase):
|
|
56
59
|
self.assert_response(
|
57
60
|
response,
|
58
61
|
next_state="select_document",
|
59
|
-
message={
|
62
|
+
message={
|
63
|
+
"message_type": "error",
|
64
|
+
"body": "No transfer found for barcode NOPE",
|
65
|
+
},
|
60
66
|
data={"restrict_scan_first": False},
|
61
67
|
)
|
62
68
|
|
@@ -117,12 +123,14 @@ class CheckoutScanCase(CheckoutCommonCase):
|
|
117
123
|
picking.action_assign()
|
118
124
|
barcode = barcode_func(picking)
|
119
125
|
response = self.service.dispatch("scan_document", params={"barcode": barcode})
|
126
|
+
picking_name = picking.name
|
127
|
+
type_name = picking.picking_type_id.name
|
120
128
|
self.assert_response(
|
121
129
|
response,
|
122
130
|
next_state="select_document",
|
123
131
|
message={
|
124
132
|
"message_type": "error",
|
125
|
-
"body": "
|
133
|
+
"body": f"Reserved for {type_name} {picking_name}",
|
126
134
|
},
|
127
135
|
data={"restrict_scan_first": False},
|
128
136
|
)
|
@@ -164,6 +164,24 @@ class CheckoutScanLineCase(CheckoutScanLineCaseBase):
|
|
164
164
|
)
|
165
165
|
|
166
166
|
def test_scan_line_error_package_not_in_picking(self):
|
167
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
168
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
169
|
+
picking.action_assign()
|
170
|
+
# Create a package for product_a
|
171
|
+
package = self._create_package_in_location(
|
172
|
+
picking.location_id, [(self.product_a, 10, None)]
|
173
|
+
)
|
174
|
+
# we work with picking, but we scan another package (not in a pick)
|
175
|
+
self._test_scan_line_error(
|
176
|
+
picking,
|
177
|
+
package.name,
|
178
|
+
{
|
179
|
+
"message_type": "error",
|
180
|
+
"body": f"Package {package.name} not found in transfer {picking.name}",
|
181
|
+
},
|
182
|
+
)
|
183
|
+
|
184
|
+
def test_scan_line_error_package_reserved_by_another_picking(self):
|
167
185
|
picking = self._create_picking(lines=[(self.product_a, 10)])
|
168
186
|
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
169
187
|
picking2 = self._create_picking(lines=[(self.product_a, 10)])
|
@@ -176,9 +194,7 @@ class CheckoutScanLineCase(CheckoutScanLineCaseBase):
|
|
176
194
|
package.name,
|
177
195
|
{
|
178
196
|
"message_type": "error",
|
179
|
-
"body": "
|
180
|
-
package.name
|
181
|
-
),
|
197
|
+
"body": f"Reserved for Checkout {picking2.name}",
|
182
198
|
},
|
183
199
|
)
|
184
200
|
|
@@ -247,7 +263,22 @@ class CheckoutScanLineCase(CheckoutScanLineCaseBase):
|
|
247
263
|
self.product_b.barcode,
|
248
264
|
{
|
249
265
|
"message_type": "error",
|
250
|
-
"body": "Product is not in the current transfer.",
|
266
|
+
"body": "Product Product B is not in the current transfer.",
|
267
|
+
},
|
268
|
+
)
|
269
|
+
|
270
|
+
def test_scan_line_error_product_in_another_picking(self):
|
271
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
272
|
+
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
273
|
+
picking2 = self._create_picking(lines=[(self.product_b, 10)])
|
274
|
+
self._fill_stock_for_moves(picking2.move_ids, in_package=True)
|
275
|
+
(picking | picking2).action_assign()
|
276
|
+
self._test_scan_line_error(
|
277
|
+
picking,
|
278
|
+
self.product_b.barcode,
|
279
|
+
{
|
280
|
+
"message_type": "error",
|
281
|
+
"body": f"Reserved for Checkout {picking2.name}",
|
251
282
|
},
|
252
283
|
)
|
253
284
|
|
@@ -68,7 +68,9 @@ class CheckoutSelectCase(CheckoutCommonCase):
|
|
68
68
|
)
|
69
69
|
|
70
70
|
def test_select_error_not_allowed(self):
|
71
|
+
# Trying to pick a picking with wrong picking type
|
71
72
|
picking = self._create_picking(picking_type=self.wh.pick_type_id)
|
72
73
|
self._fill_stock_for_moves(picking.move_ids, in_package=True)
|
73
74
|
picking.action_assign()
|
74
|
-
|
75
|
+
expected_message = f"Reserved for {picking.picking_type_id.name} {picking.name}"
|
76
|
+
self._test_error(picking, expected_message)
|