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.
Files changed (76) hide show
  1. odoo/addons/shopfloor/README.rst +1 -1
  2. odoo/addons/shopfloor/__manifest__.py +1 -1
  3. odoo/addons/shopfloor/actions/data.py +69 -34
  4. odoo/addons/shopfloor/actions/data_detail.py +20 -0
  5. odoo/addons/shopfloor/actions/message.py +94 -2
  6. odoo/addons/shopfloor/actions/move_line_search.py +2 -2
  7. odoo/addons/shopfloor/actions/packaging.py +10 -0
  8. odoo/addons/shopfloor/actions/schema.py +11 -0
  9. odoo/addons/shopfloor/actions/schema_detail.py +14 -8
  10. odoo/addons/shopfloor/actions/search.py +9 -6
  11. odoo/addons/shopfloor/components/scan_handler_product.py +2 -0
  12. odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +4 -2
  13. odoo/addons/shopfloor/docs/checkout_diag_seq.plantuml +19 -5
  14. odoo/addons/shopfloor/docs/checkout_diag_seq.png +0 -0
  15. odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.plantuml +4 -4
  16. odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.png +0 -0
  17. odoo/addons/shopfloor/i18n/ca.po +26 -8
  18. odoo/addons/shopfloor/i18n/de.po +26 -8
  19. odoo/addons/shopfloor/i18n/es_AR.po +36 -10
  20. odoo/addons/shopfloor/i18n/it.po +2015 -0
  21. odoo/addons/shopfloor/i18n/pt_BR.po +26 -8
  22. odoo/addons/shopfloor/i18n/shopfloor.pot +133 -8
  23. odoo/addons/shopfloor/migrations/16.0.2.0.0/post-migration.py +43 -0
  24. odoo/addons/shopfloor/models/shopfloor_menu.py +29 -5
  25. odoo/addons/shopfloor/models/stock_move_line.py +3 -0
  26. odoo/addons/shopfloor/models/stock_picking.py +11 -0
  27. odoo/addons/shopfloor/services/checkout.py +216 -61
  28. odoo/addons/shopfloor/services/cluster_picking.py +33 -18
  29. odoo/addons/shopfloor/services/delivery.py +25 -7
  30. odoo/addons/shopfloor/services/location_content_transfer.py +42 -28
  31. odoo/addons/shopfloor/services/single_pack_transfer.py +36 -13
  32. odoo/addons/shopfloor/services/zone_picking.py +187 -67
  33. odoo/addons/shopfloor/static/description/index.html +1 -1
  34. odoo/addons/shopfloor/tests/__init__.py +2 -0
  35. odoo/addons/shopfloor/tests/common.py +3 -1
  36. odoo/addons/shopfloor/tests/test_actions_data.py +46 -7
  37. odoo/addons/shopfloor/tests/test_actions_data_base.py +15 -0
  38. odoo/addons/shopfloor/tests/test_actions_data_detail.py +30 -8
  39. odoo/addons/shopfloor/tests/test_actions_packaging.py +43 -0
  40. odoo/addons/shopfloor/tests/test_checkout_base.py +15 -5
  41. odoo/addons/shopfloor/tests/test_checkout_done.py +40 -5
  42. odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +1 -0
  43. odoo/addons/shopfloor/tests/test_checkout_list_package.py +3 -1
  44. odoo/addons/shopfloor/tests/test_checkout_scan.py +19 -0
  45. odoo/addons/shopfloor/tests/test_checkout_scan_dest_location.py +99 -0
  46. odoo/addons/shopfloor/tests/test_checkout_scan_line.py +3 -2
  47. odoo/addons/shopfloor/tests/test_checkout_scan_line_no_prefill_qty.py +48 -0
  48. odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +26 -0
  49. odoo/addons/shopfloor/tests/test_checkout_scan_package_action_no_prefill_qty.py +16 -0
  50. odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +4 -1
  51. odoo/addons/shopfloor/tests/test_checkout_summary.py +1 -1
  52. odoo/addons/shopfloor/tests/test_cluster_picking_unload.py +37 -8
  53. odoo/addons/shopfloor/tests/test_delivery_list_stock_picking.py +5 -0
  54. odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +4 -4
  55. odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_all.py +24 -2
  56. odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +6 -4
  57. odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +45 -0
  58. odoo/addons/shopfloor/tests/test_scan_anything.py +7 -0
  59. odoo/addons/shopfloor/tests/test_single_pack_transfer.py +59 -8
  60. odoo/addons/shopfloor/tests/test_zone_picking_base.py +36 -10
  61. odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +2 -0
  62. odoo/addons/shopfloor/tests/test_zone_picking_complete_mix_pack_flux.py +59 -0
  63. odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +33 -2
  64. odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py +8 -3
  65. odoo/addons/shopfloor/tests/test_zone_picking_select_line_no_prefill_qty.py +19 -2
  66. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +88 -19
  67. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_package_not_allowed.py +94 -0
  68. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +1 -5
  69. odoo/addons/shopfloor/tests/test_zone_picking_start.py +4 -4
  70. odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +1 -1
  71. odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +4 -4
  72. odoo/addons/shopfloor/views/shopfloor_menu.xml +30 -0
  73. {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/METADATA +2 -2
  74. {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/RECORD +76 -70
  75. {odoo_addon_shopfloor-16.0.1.0.0.24.dist-info → odoo_addon_shopfloor-16.0.2.1.0.dist-info}/WHEEL +0 -0
  76. {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
@@ -53,8 +53,8 @@ class ActionsDataCase(ActionsDataCaseBase):
53
53
  "name": location.name,
54
54
  "barcode": location.name,
55
55
  "operation_progress": {
56
- "done": 2.0,
57
- "to_do": 210.0,
56
+ "done": 0.0,
57
+ "to_do": 228.0,
58
58
  },
59
59
  }
60
60
  self.assertDictEqual(data, expected)
@@ -86,13 +86,35 @@ class ActionsDataCase(ActionsDataCaseBase):
86
86
  expected = {
87
87
  "id": package.id,
88
88
  "name": package.name,
89
- "move_line_count": 1,
90
89
  "packaging": self._expected_packaging(package.product_packaging_id),
91
90
  "storage_type": self._expected_storage_type(package.package_type_id),
91
+ "total_quantity": 10.0,
92
92
  "weight": 20.0,
93
93
  }
94
94
  self.assertDictEqual(data, expected)
95
95
 
96
+ def test_data_package_with_move_line_count(self):
97
+ package = self.move_a.move_line_ids.package_id
98
+ package.product_packaging_id = self.packaging.id
99
+ package.package_type_id = self.storage_type_pallet
100
+ data = self.data.package(
101
+ package,
102
+ picking=self.picking,
103
+ with_packaging=True,
104
+ with_package_move_line_count=True,
105
+ )
106
+ self.assert_schema(self.schema.package(with_packaging=True), data)
107
+ expected = {
108
+ "id": package.id,
109
+ "name": package.name,
110
+ "move_line_count": 2,
111
+ "packaging": self._expected_packaging(package.product_packaging_id),
112
+ "storage_type": self._expected_storage_type(package.package_type_id),
113
+ "weight": 20.0,
114
+ "total_quantity": sum(package.quant_ids.mapped("quantity")),
115
+ }
116
+ self.assertDictEqual(data, expected)
117
+
96
118
  def test_data_package_level(self):
97
119
  package_level = self.picking.package_level_ids[0]
98
120
  data = self.data.package_level(package_level)
@@ -132,6 +154,7 @@ class ActionsDataCase(ActionsDataCaseBase):
132
154
  "partner": {"id": self.customer.id, "name": self.customer.name},
133
155
  "carrier": {"id": carrier.id, "name": carrier.name},
134
156
  "ship_carrier": None,
157
+ "priority": "0",
135
158
  }
136
159
  self.assertEqual(data.pop("scheduled_date").split("T")[0], "2020-08-03")
137
160
  self.assertDictEqual(data, expected)
@@ -156,6 +179,7 @@ class ActionsDataCase(ActionsDataCaseBase):
156
179
  "carrier": {"id": carrier.id, "name": carrier.name},
157
180
  "ship_carrier": None,
158
181
  "progress": 0.0,
182
+ "priority": "0",
159
183
  }
160
184
  self.assertEqual(data.pop("scheduled_date").split("T")[0], "2020-08-03")
161
185
  self.assertDictEqual(data, expected)
@@ -203,16 +227,18 @@ class ActionsDataCase(ActionsDataCaseBase):
203
227
  "package_src": {
204
228
  "id": move_line.package_id.id,
205
229
  "name": move_line.package_id.name,
206
- "move_line_count": 0,
207
230
  "weight": 20.0,
208
231
  "storage_type": None,
232
+ "total_quantity": sum(
233
+ move_line.package_id.quant_ids.mapped("quantity")
234
+ ),
209
235
  },
210
236
  "package_dest": {
211
237
  "id": result_package.id,
212
238
  "name": result_package.name,
213
- "move_line_count": 1,
214
239
  "weight": 6.0,
215
240
  "storage_type": None,
241
+ "total_quantity": sum(result_package.quant_ids.mapped("quantity")),
216
242
  },
217
243
  "location_src": self._expected_location(move_line.location_id),
218
244
  "location_dest": self._expected_location(move_line.location_dest_id),
@@ -220,6 +246,10 @@ class ActionsDataCase(ActionsDataCaseBase):
220
246
  "progress": 30.0,
221
247
  }
222
248
  self.assertDictEqual(data, expected)
249
+ data = self.data.move_line(move_line, with_package_move_line_count=True)
250
+ expected["package_src"]["move_line_count"] = 1
251
+ expected["package_dest"]["move_line_count"] = 1
252
+ self.assertDictEqual(data, expected)
223
253
 
224
254
  def test_data_move_line_lot(self):
225
255
  move_line = self.move_b.move_line_ids
@@ -264,16 +294,20 @@ class ActionsDataCase(ActionsDataCaseBase):
264
294
  "package_src": {
265
295
  "id": move_line.package_id.id,
266
296
  "name": move_line.package_id.name,
267
- "move_line_count": 1,
268
297
  "weight": 30,
269
298
  "storage_type": None,
299
+ "total_quantity": sum(
300
+ move_line.package_id.quant_ids.mapped("quantity")
301
+ ),
270
302
  },
271
303
  "package_dest": {
272
304
  "id": move_line.result_package_id.id,
273
305
  "name": move_line.result_package_id.name,
274
- "move_line_count": 1,
275
306
  "weight": 0,
276
307
  "storage_type": None,
308
+ "total_quantity": sum(
309
+ move_line.result_package_id.quant_ids.mapped("quantity")
310
+ ),
277
311
  },
278
312
  "location_src": self._expected_location(move_line.location_id),
279
313
  "location_dest": self._expected_location(move_line.location_dest_id),
@@ -281,6 +315,11 @@ class ActionsDataCase(ActionsDataCaseBase):
281
315
  "progress": 0.0,
282
316
  }
283
317
  self.assertDictEqual(data, expected)
318
+ data = self.data.move_line(move_line, with_package_move_line_count=True)
319
+ self.assert_schema(self.schema.move_line(), data)
320
+ expected["package_src"]["move_line_count"] = 2
321
+ expected["package_dest"]["move_line_count"] = 2
322
+ self.assertDictEqual(data, expected)
284
323
 
285
324
  def test_data_move_line_raw(self):
286
325
  move_line = self.move_d.move_line_ids
@@ -176,6 +176,7 @@ class ActionsDataCaseBase(CommonCase, ActionsDataTestMixin):
176
176
  "name": record.name,
177
177
  "weight": record.pack_weight or record.estimated_pack_weight_kg,
178
178
  "storage_type": None,
179
+ "total_quantity": sum(record.quant_ids.mapped("quantity")),
179
180
  }
180
181
  data.update(kw)
181
182
  return data
@@ -242,3 +243,17 @@ class ActionsDataDetailCaseBase(ActionsDataCaseBase):
242
243
  }
243
244
  )
244
245
  return dict(**self._expected_product(record), **detail)
246
+
247
+ def _expected_packaging_detail(self, record, **kw):
248
+ return dict(
249
+ **self._expected_packaging(record),
250
+ **{
251
+ "length": record.packaging_length,
252
+ "width": record.width,
253
+ "height": record.height,
254
+ "max_weight": record.package_type_id.max_weight,
255
+ "length_uom": record.length_uom_name,
256
+ "weight_uom": record.weight_uom_name,
257
+ "barcode": record.barcode,
258
+ }
259
+ )
@@ -33,9 +33,10 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
33
33
  )
34
34
 
35
35
  def test_data_packaging(self):
36
- data = self.data_detail.packaging(self.packaging)
37
- self.assert_schema(self.schema_detail.packaging(), data)
38
- self.assertDictEqual(data, self._expected_packaging(self.packaging))
36
+ self.packaging.barcode = "barcode"
37
+ data = self.data_detail.packaging_detail(self.packaging)
38
+ self.assert_schema(self.schema_detail.packaging_detail(), data)
39
+ self.assertDictEqual(data, self._expected_packaging_detail(self.packaging))
39
40
 
40
41
  def test_data_lot(self):
41
42
  lot = self.env["stock.lot"].create(
@@ -81,7 +82,6 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
81
82
  "name": package.location_id.display_name,
82
83
  },
83
84
  "name": package.name,
84
- "move_line_count": 1,
85
85
  "packaging": self.data_detail.packaging(package.product_packaging_id),
86
86
  "weight": 20.0,
87
87
  "pickings": self.data_detail.pickings(pickings),
@@ -90,8 +90,14 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
90
90
  "id": self.storage_type_pallet.id,
91
91
  "name": self.storage_type_pallet.name,
92
92
  },
93
+ "total_quantity": sum(package.quant_ids.mapped("quantity")),
93
94
  }
94
95
  self.assertDictEqual(data, expected)
96
+ data = self.data_detail.package_detail(
97
+ package, picking=self.picking, with_package_move_line_count=True
98
+ )
99
+ expected.update({"move_line_count": 2})
100
+ self.assertDictEqual(data, expected)
95
101
 
96
102
  def test_data_picking(self):
97
103
  picking = self.picking
@@ -186,16 +192,20 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
186
192
  "package_src": {
187
193
  "id": move_line.package_id.id,
188
194
  "name": move_line.package_id.name,
189
- "move_line_count": 0,
190
195
  "weight": 20.0,
191
196
  "storage_type": None,
197
+ "total_quantity": sum(
198
+ move_line.package_id.quant_ids.mapped("quantity")
199
+ ),
192
200
  },
193
201
  "package_dest": {
194
202
  "id": result_package.id,
195
203
  "name": result_package.name,
196
- "move_line_count": 1,
197
204
  "weight": 6.0,
198
205
  "storage_type": None,
206
+ "total_quantity": sum(
207
+ move_line.result_package_id.quant_ids.mapped("quantity")
208
+ ),
199
209
  },
200
210
  "location_src": self._expected_location(move_line.location_id),
201
211
  "location_dest": self._expected_location(move_line.location_dest_id),
@@ -203,6 +213,10 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
203
213
  "progress": 30.0,
204
214
  }
205
215
  self.assertDictEqual(data, expected)
216
+ data = self.data_detail.move_line(move_line, with_package_move_line_count=True)
217
+ expected["package_src"]["move_line_count"] = 1
218
+ expected["package_dest"]["move_line_count"] = 1
219
+ self.assertDictEqual(data, expected)
206
220
 
207
221
  def test_data_move_line_lot(self):
208
222
  move_line = self.move_b.move_line_ids
@@ -248,16 +262,20 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
248
262
  "package_src": {
249
263
  "id": move_line.package_id.id,
250
264
  "name": move_line.package_id.name,
251
- "move_line_count": 1,
252
265
  "weight": 30.0,
253
266
  "storage_type": None,
267
+ "total_quantity": sum(
268
+ move_line.package_id.quant_ids.mapped("quantity")
269
+ ),
254
270
  },
255
271
  "package_dest": {
256
272
  "id": move_line.result_package_id.id,
257
273
  "name": move_line.result_package_id.name,
258
- "move_line_count": 1,
259
274
  "weight": 0.0,
260
275
  "storage_type": None,
276
+ "total_quantity": sum(
277
+ move_line.result_package_id.quant_ids.mapped("quantity")
278
+ ),
261
279
  },
262
280
  "location_src": self._expected_location(move_line.location_id),
263
281
  "location_dest": self._expected_location(move_line.location_dest_id),
@@ -265,6 +283,10 @@ class TestActionsDataDetailCase(ActionsDataDetailCaseBase):
265
283
  "progress": 0.0,
266
284
  }
267
285
  self.assertDictEqual(data, expected)
286
+ data = self.data_detail.move_line(move_line, with_package_move_line_count=True)
287
+ expected["package_src"]["move_line_count"] = 2
288
+ expected["package_dest"]["move_line_count"] = 2
289
+ self.assertDictEqual(data, expected)
268
290
 
269
291
  def test_data_move_line_raw(self):
270
292
  move_line = self.move_d.move_line_ids
@@ -0,0 +1,43 @@
1
+ # Copyright 2023 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ # pylint: disable=missing-return
5
+
6
+
7
+ from .common import CommonCase
8
+
9
+
10
+ class TestActionsPackaging(CommonCase):
11
+ """Tests covering methods to work on product packaging."""
12
+
13
+ @classmethod
14
+ def setUpClass(cls):
15
+ super().setUpClass()
16
+ with cls.work_on_actions(cls) as work:
17
+ cls.packaging = work.component(usage="packaging")
18
+ cls.picking = cls._create_picking(
19
+ lines=[(cls.product_a, 10), (cls.product_b, 10)], confirm=True
20
+ )
21
+ cls.move0 = cls.picking.move_ids[0]
22
+ cls.move1 = cls.picking.move_ids[1]
23
+ cls._fill_stock_for_moves(
24
+ cls.picking.move_ids, in_package=True, same_package=True
25
+ )
26
+ cls.picking.action_assign()
27
+ cls.package1 = cls.move0.move_line_ids.package_id
28
+
29
+ @classmethod
30
+ def setUpClassVars(cls):
31
+ super().setUpClassVars()
32
+ cls.wh = cls.env.ref("stock.warehouse0")
33
+ cls.picking_type = cls.wh.out_type_id
34
+
35
+ def test_package_is_complete_mix_pack(self):
36
+ self.assertTrue(self.packaging.is_complete_mix_pack(self.package1))
37
+
38
+ def test_package_partially_reserved(self):
39
+ # Package has 2 products from pick 1 reserved
40
+ pick2 = self._create_picking(lines=[(self.product_c, 10)], confirm=True)
41
+ # But adding 1 more product from pick 2 that is not yet reserved
42
+ self._fill_stock_for_moves(pick2.move_ids, in_package=self.package1)
43
+ self.assertFalse(self.packaging.is_complete_mix_pack(self.package1))
@@ -35,8 +35,15 @@ class CheckoutCommonCase(CommonCase):
35
35
  "checkout", menu=self.menu, profile=self.profile
36
36
  )
37
37
 
38
- def _stock_picking_data(self, picking, **kw):
39
- return self.service._data_for_stock_picking(picking, **kw)
38
+ def _stock_picking_data(
39
+ self, picking, done=False, with_lines=True, with_location=False, **kw
40
+ ):
41
+ return self.service._data_for_stock_picking(
42
+ picking, done, with_lines, with_location, **kw
43
+ )
44
+
45
+ def _stock_locations_data(self, locations, **kw):
46
+ return self.service._data_for_locations(locations, **kw)
40
47
 
41
48
  # we test the methods that structure data in test_actions_data.py
42
49
  def _picking_summary_data(self, picking):
@@ -45,8 +52,10 @@ class CheckoutCommonCase(CommonCase):
45
52
  def _move_line_data(self, move_line):
46
53
  return self.data.move_line(move_line)
47
54
 
48
- def _package_data(self, package, picking):
49
- return self.data.package(package, picking=picking, with_packaging=True)
55
+ def _package_data(self, package, picking, **kwargs):
56
+ return self.data.package(
57
+ package, picking=picking, with_packaging=True, **kwargs
58
+ )
50
59
 
51
60
  def _packaging_data(self, packaging):
52
61
  return self.data.delivery_packaging(packaging)
@@ -56,7 +65,7 @@ class CheckoutCommonCase(CommonCase):
56
65
  "picking": self._stock_picking_data(picking),
57
66
  "group_lines_by_location": True,
58
67
  "show_oneline_package_content": False,
59
- "need_confirm_pack_all": False,
68
+ "need_confirm_pack_all": "",
60
69
  }
61
70
  data.update(kw)
62
71
  return data
@@ -72,6 +81,7 @@ class CheckoutCommonCase(CommonCase):
72
81
  "picking": self._picking_summary_data(picking),
73
82
  "packing_info": "",
74
83
  "no_package_enabled": True,
84
+ "package_allowed": True,
75
85
  },
76
86
  message={
77
87
  "message_type": "warning",
@@ -22,6 +22,7 @@ class CheckoutDoneCase(CheckoutCommonCase):
22
22
  "message_type": "success",
23
23
  "body": "Transfer {} done".format(picking.name),
24
24
  },
25
+ data={"restrict_scan_first": False},
25
26
  )
26
27
 
27
28
 
@@ -39,6 +40,13 @@ class CheckoutDonePartialCase(CheckoutCommonCase):
39
40
  cls.line1.write({"qty_done": 10, "shopfloor_checkout_done": True})
40
41
  cls.line2.write({"qty_done": 2, "shopfloor_checkout_done": True})
41
42
 
43
+ cls.dest_location = picking.location_dest_id
44
+ cls.child_location = (
45
+ cls.env["stock.location"]
46
+ .sudo()
47
+ .create({"name": "Child Location", "location_id": cls.dest_location.id})
48
+ )
49
+
42
50
  def test_done_partial(self):
43
51
  # line is done
44
52
  response = self.service.dispatch("done", params={"picking_id": self.picking.id})
@@ -57,17 +65,43 @@ class CheckoutDonePartialCase(CheckoutCommonCase):
57
65
  response = self.service.dispatch(
58
66
  "done", params={"picking_id": self.picking.id, "confirmation": True}
59
67
  )
60
- # as they are all the lines that relate to the picking, they didn't have
61
- # been extracted in a separate transfer. An usual backorder has been
62
- # created for the unprocessed qty.
68
+
63
69
  self.assertRecordValues(self.picking, [{"state": "done"}])
64
- self.assertTrue(self.picking.backorder_ids)
65
- self.assertEqual(self.picking.backorder_ids.move_line_ids.reserved_uom_qty, 8)
66
70
 
67
71
  self.assert_response(
68
72
  response,
69
73
  next_state="select_document",
70
74
  message=self.service.msg_store.transfer_done_success(self.picking),
75
+ data={"restrict_scan_first": False},
76
+ )
77
+
78
+ def test_done_ask_destination_location(self):
79
+ """Check asking for destination location for view type location."""
80
+ view_location = (
81
+ self.env["stock.location"]
82
+ .sudo()
83
+ .create(
84
+ {
85
+ "name": "Test Location Usage View",
86
+ "location_id": self.picking.move_ids.location_dest_id.id,
87
+ "usage": "view",
88
+ }
89
+ )
90
+ )
91
+ self.picking.move_ids.location_dest_id = view_location
92
+ response = self.service.dispatch(
93
+ "done", params={"picking_id": self.picking.id, "confirmation": True}
94
+ )
95
+
96
+ self.assertRecordValues(self.picking, [{"state": "assigned"}])
97
+ self.assert_response(
98
+ response,
99
+ next_state="select_child_location",
100
+ data={
101
+ "picking": self._stock_picking_data(
102
+ self.picking, done=True, with_lines=False, with_location=True
103
+ ),
104
+ },
71
105
  )
72
106
 
73
107
 
@@ -130,4 +164,5 @@ class CheckoutDoneRawUnpackedCase(CheckoutCommonCase):
130
164
  response,
131
165
  next_state="select_document",
132
166
  message=self.service.msg_store.transfer_done_success(picking_done),
167
+ data={"restrict_scan_first": False},
133
168
  )
@@ -126,6 +126,7 @@ class CheckoutListDeliveryPackagingCase(CheckoutCommonCase, CheckoutSelectPackag
126
126
  "no_package_enabled": not self.service.options.get(
127
127
  "checkout__disable_no_package"
128
128
  ),
129
+ "package_allowed": True,
129
130
  },
130
131
  message=self.service.msg_store.no_delivery_packaging_available(),
131
132
  )
@@ -28,7 +28,9 @@ class SelectDestPackageMixin:
28
28
  "picking": picking_data,
29
29
  "packages": [
30
30
  self._package_data(
31
- package.with_context(picking_id=picking.id), picking
31
+ package.with_context(picking_id=picking.id),
32
+ picking,
33
+ with_package_move_line_count=True,
32
34
  )
33
35
  for package in packages
34
36
  ],
@@ -31,6 +31,20 @@ class CheckoutScanCase(CheckoutCommonCase):
31
31
  in_package=False,
32
32
  )
33
33
 
34
+ def test_scan_document_with_option_product_not_ok(self):
35
+ self.menu.sudo().scan_location_or_pack_first = True
36
+ picking = self._create_picking()
37
+ self._fill_stock_for_moves(picking.move_ids)
38
+ picking.action_assign()
39
+ barcode = picking.move_line_ids.product_id[0].barcode
40
+ response = self.service.dispatch("scan_document", params={"barcode": barcode})
41
+ self.assert_response(
42
+ response,
43
+ next_state="select_document",
44
+ message={"message_type": "error", "body": "Barcode not found"},
45
+ data={"restrict_scan_first": True},
46
+ )
47
+
34
48
  def test_scan_document_packaging_ok(self):
35
49
  self._test_scan_ok(
36
50
  lambda picking: picking.move_line_ids.product_id[0].packaging_ids.barcode,
@@ -43,6 +57,7 @@ class CheckoutScanCase(CheckoutCommonCase):
43
57
  response,
44
58
  next_state="select_document",
45
59
  message={"message_type": "error", "body": "Barcode not found"},
60
+ data={"restrict_scan_first": False},
46
61
  )
47
62
 
48
63
  def _test_scan_document_error_not_available(self, barcode_func):
@@ -65,6 +80,7 @@ class CheckoutScanCase(CheckoutCommonCase):
65
80
  "message_type": "error",
66
81
  "body": "Transfer {} is not available.".format(picking.name),
67
82
  },
83
+ data={"restrict_scan_first": False},
68
84
  )
69
85
 
70
86
  def test_scan_document_error_not_available_picking(self):
@@ -92,6 +108,7 @@ class CheckoutScanCase(CheckoutCommonCase):
92
108
  response,
93
109
  next_state="select_document",
94
110
  message={"message_type": "error", "body": "Location not allowed here."},
111
+ data={"restrict_scan_first": False},
95
112
  )
96
113
 
97
114
  def _test_scan_document_error_different_picking_type(self, barcode_func):
@@ -107,6 +124,7 @@ class CheckoutScanCase(CheckoutCommonCase):
107
124
  "message_type": "error",
108
125
  "body": "You cannot move this using this menu.",
109
126
  },
127
+ data={"restrict_scan_first": False},
110
128
  )
111
129
 
112
130
  def test_scan_document_error_different_picking_type_picking(self):
@@ -139,6 +157,7 @@ class CheckoutScanCase(CheckoutCommonCase):
139
157
  "body": "Several transfers found, please scan a package"
140
158
  " or select a transfer manually.",
141
159
  },
160
+ data={"restrict_scan_first": False},
142
161
  )
143
162
 
144
163
  def test_scan_document_recover(self):
@@ -0,0 +1,99 @@
1
+ # Copyright 2023 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ # pylint: disable=missing-return
5
+
6
+ from .test_checkout_base import CheckoutCommonCase
7
+
8
+
9
+ class CheckoutSelectChildLocationCase(CheckoutCommonCase):
10
+ @classmethod
11
+ def setUpClassBaseData(cls):
12
+ super().setUpClassBaseData()
13
+ cls.picking = picking = cls._create_picking(
14
+ lines=[(cls.product_a, 10), (cls.product_b, 10)]
15
+ )
16
+ cls._fill_stock_for_moves(picking.move_ids)
17
+ picking.action_assign()
18
+ cls.line1 = picking.move_line_ids[0]
19
+ cls.line2 = picking.move_line_ids[1]
20
+ cls.line1.write({"qty_done": 10, "shopfloor_checkout_done": True})
21
+ cls.line2.write({"qty_done": 2, "shopfloor_checkout_done": True})
22
+
23
+ cls.dest_location = picking.location_dest_id
24
+ cls.child_location = (
25
+ cls.env["stock.location"]
26
+ .sudo()
27
+ .create({"name": "Child Location", "location_id": cls.dest_location.id})
28
+ )
29
+ cls.child_location_view = (
30
+ cls.env["stock.location"]
31
+ .sudo()
32
+ .create(
33
+ {
34
+ "name": "Child Location View",
35
+ "location_id": cls.dest_location.id,
36
+ "usage": "view",
37
+ }
38
+ )
39
+ )
40
+
41
+ def test_scan_dest_location_ok(self):
42
+ response = self.service.dispatch(
43
+ "scan_dest_location",
44
+ params={
45
+ "picking_id": self.picking.id,
46
+ "barcode": self.child_location.name,
47
+ },
48
+ )
49
+
50
+ self.assertRecordValues(self.picking, [{"state": "done"}])
51
+ self.assertTrue(self.picking.backorder_ids)
52
+ self.assertEqual(self.picking.backorder_ids.move_line_ids.reserved_uom_qty, 8)
53
+
54
+ self.assert_response(
55
+ response,
56
+ next_state="select_document",
57
+ data={"restrict_scan_first": False},
58
+ message=self.service.msg_store.transfer_done_success(self.picking),
59
+ )
60
+
61
+ def test_scan_dest_location_not_found(self):
62
+ response = self.service.dispatch(
63
+ "scan_dest_location",
64
+ params={
65
+ "picking_id": self.picking.id,
66
+ "barcode": "not-a-location",
67
+ },
68
+ )
69
+
70
+ self.assert_response(
71
+ response,
72
+ next_state="select_child_location",
73
+ data={
74
+ "picking": self._stock_picking_data(
75
+ self.picking, done=True, with_lines=False, with_location=True
76
+ ),
77
+ },
78
+ message=self.service.msg_store.location_not_found(),
79
+ )
80
+
81
+ def test_scan_dest_location_not_allowed(self):
82
+ response = self.service.dispatch(
83
+ "scan_dest_location",
84
+ params={
85
+ "picking_id": self.picking.id,
86
+ "barcode": self.child_location_view.name,
87
+ },
88
+ )
89
+
90
+ self.assert_response(
91
+ response,
92
+ next_state="select_child_location",
93
+ data={
94
+ "picking": self._stock_picking_data(
95
+ self.picking, done=True, with_lines=False, with_location=True
96
+ ),
97
+ },
98
+ message=self.service.msg_store.dest_location_not_allowed(),
99
+ )
@@ -119,7 +119,8 @@ class CheckoutScanLineCase(CheckoutScanLineCaseBase):
119
119
  picking.action_assign()
120
120
  first_line = picking.move_line_ids[0]
121
121
  lot = first_line.lot_id
122
- self._test_scan_line_ok(lot.name, first_line)
122
+ related_lines = picking.move_line_ids - first_line
123
+ self._test_scan_line_ok(lot.name, first_line, related_lines)
123
124
 
124
125
  def test_scan_line_product_in_one_package_all_package_lines_ok(self):
125
126
  picking = self._create_picking(
@@ -365,7 +366,7 @@ class CheckoutScanLineCase(CheckoutScanLineCaseBase):
365
366
  params={
366
367
  "picking_id": picking.id,
367
368
  "barcode": self.delivery_packaging.barcode,
368
- "confirm_pack_all": True,
369
+ "confirm_pack_all": self.delivery_packaging.barcode,
369
370
  },
370
371
  )
371
372
  # move to summary as all lines are done