odoo-addon-shopfloor 16.0.1.0.0.24__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 (182) hide show
  1. odoo/addons/shopfloor/README.rst +160 -0
  2. odoo/addons/shopfloor/__init__.py +4 -0
  3. odoo/addons/shopfloor/__manifest__.py +65 -0
  4. odoo/addons/shopfloor/actions/__init__.py +15 -0
  5. odoo/addons/shopfloor/actions/change_package_lot.py +164 -0
  6. odoo/addons/shopfloor/actions/completion_info.py +42 -0
  7. odoo/addons/shopfloor/actions/data.py +329 -0
  8. odoo/addons/shopfloor/actions/data_detail.py +154 -0
  9. odoo/addons/shopfloor/actions/inventory.py +150 -0
  10. odoo/addons/shopfloor/actions/location_content_transfer_sorter.py +89 -0
  11. odoo/addons/shopfloor/actions/message.py +846 -0
  12. odoo/addons/shopfloor/actions/move_line_search.py +119 -0
  13. odoo/addons/shopfloor/actions/packaging.py +59 -0
  14. odoo/addons/shopfloor/actions/savepoint.py +44 -0
  15. odoo/addons/shopfloor/actions/schema.py +182 -0
  16. odoo/addons/shopfloor/actions/schema_detail.py +98 -0
  17. odoo/addons/shopfloor/actions/search.py +187 -0
  18. odoo/addons/shopfloor/actions/stock.py +239 -0
  19. odoo/addons/shopfloor/actions/stock_unreserve.py +66 -0
  20. odoo/addons/shopfloor/components/__init__.py +5 -0
  21. odoo/addons/shopfloor/components/scan_handler_location.py +26 -0
  22. odoo/addons/shopfloor/components/scan_handler_lot.py +26 -0
  23. odoo/addons/shopfloor/components/scan_handler_package.py +26 -0
  24. odoo/addons/shopfloor/components/scan_handler_product.py +26 -0
  25. odoo/addons/shopfloor/components/scan_handler_transfer.py +26 -0
  26. odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +73 -0
  27. odoo/addons/shopfloor/demo/shopfloor_app_demo.xml +12 -0
  28. odoo/addons/shopfloor/demo/shopfloor_menu_demo.xml +64 -0
  29. odoo/addons/shopfloor/demo/shopfloor_profile_demo.xml +8 -0
  30. odoo/addons/shopfloor/demo/stock_picking_type_demo.xml +93 -0
  31. odoo/addons/shopfloor/docs/checkout_diag_seq.plantuml +61 -0
  32. odoo/addons/shopfloor/docs/checkout_diag_seq.png +0 -0
  33. odoo/addons/shopfloor/docs/cluster_picking_diag_seq.plantuml +112 -0
  34. odoo/addons/shopfloor/docs/cluster_picking_diag_seq.png +0 -0
  35. odoo/addons/shopfloor/docs/delivery_diag_seq.plantuml +56 -0
  36. odoo/addons/shopfloor/docs/delivery_diag_seq.png +0 -0
  37. odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.plantuml +66 -0
  38. odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.png +0 -0
  39. odoo/addons/shopfloor/docs/oca_logo.png +0 -0
  40. odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.plantuml +36 -0
  41. odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.png +0 -0
  42. odoo/addons/shopfloor/docs/zone_picking_diag_seq.plantuml +85 -0
  43. odoo/addons/shopfloor/docs/zone_picking_diag_seq.png +0 -0
  44. odoo/addons/shopfloor/exceptions.py +6 -0
  45. odoo/addons/shopfloor/i18n/ca.po +1802 -0
  46. odoo/addons/shopfloor/i18n/de.po +1791 -0
  47. odoo/addons/shopfloor/i18n/es_AR.po +2147 -0
  48. odoo/addons/shopfloor/i18n/pt_BR.po +1791 -0
  49. odoo/addons/shopfloor/i18n/shopfloor.pot +1877 -0
  50. odoo/addons/shopfloor/models/__init__.py +12 -0
  51. odoo/addons/shopfloor/models/priority_postpone_mixin.py +41 -0
  52. odoo/addons/shopfloor/models/shopfloor_app.py +9 -0
  53. odoo/addons/shopfloor/models/shopfloor_menu.py +436 -0
  54. odoo/addons/shopfloor/models/stock_location.py +76 -0
  55. odoo/addons/shopfloor/models/stock_move.py +119 -0
  56. odoo/addons/shopfloor/models/stock_move_line.py +307 -0
  57. odoo/addons/shopfloor/models/stock_package_level.py +50 -0
  58. odoo/addons/shopfloor/models/stock_picking.py +118 -0
  59. odoo/addons/shopfloor/models/stock_picking_batch.py +41 -0
  60. odoo/addons/shopfloor/models/stock_picking_type.py +26 -0
  61. odoo/addons/shopfloor/models/stock_quant.py +31 -0
  62. odoo/addons/shopfloor/models/stock_quant_package.py +101 -0
  63. odoo/addons/shopfloor/readme/CONTRIBUTORS.rst +18 -0
  64. odoo/addons/shopfloor/readme/CREDITS.rst +5 -0
  65. odoo/addons/shopfloor/readme/DESCRIPTION.rst +17 -0
  66. odoo/addons/shopfloor/readme/HISTORY.rst +4 -0
  67. odoo/addons/shopfloor/readme/ROADMAP.rst +4 -0
  68. odoo/addons/shopfloor/readme/USAGE.rst +6 -0
  69. odoo/addons/shopfloor/security/groups.xml +17 -0
  70. odoo/addons/shopfloor/services/__init__.py +16 -0
  71. odoo/addons/shopfloor/services/checkout.py +1763 -0
  72. odoo/addons/shopfloor/services/cluster_picking.py +1628 -0
  73. odoo/addons/shopfloor/services/delivery.py +828 -0
  74. odoo/addons/shopfloor/services/forms/__init__.py +1 -0
  75. odoo/addons/shopfloor/services/forms/picking_form.py +78 -0
  76. odoo/addons/shopfloor/services/location_content_transfer.py +1194 -0
  77. odoo/addons/shopfloor/services/menu.py +60 -0
  78. odoo/addons/shopfloor/services/picking_batch.py +126 -0
  79. odoo/addons/shopfloor/services/service.py +101 -0
  80. odoo/addons/shopfloor/services/single_pack_transfer.py +366 -0
  81. odoo/addons/shopfloor/services/zone_picking.py +1938 -0
  82. odoo/addons/shopfloor/static/description/icon.png +0 -0
  83. odoo/addons/shopfloor/static/description/index.html +500 -0
  84. odoo/addons/shopfloor/tests/__init__.py +83 -0
  85. odoo/addons/shopfloor/tests/common.py +324 -0
  86. odoo/addons/shopfloor/tests/models.py +29 -0
  87. odoo/addons/shopfloor/tests/test_actions_change_package_lot.py +1175 -0
  88. odoo/addons/shopfloor/tests/test_actions_data.py +376 -0
  89. odoo/addons/shopfloor/tests/test_actions_data_base.py +244 -0
  90. odoo/addons/shopfloor/tests/test_actions_data_detail.py +322 -0
  91. odoo/addons/shopfloor/tests/test_actions_search.py +248 -0
  92. odoo/addons/shopfloor/tests/test_actions_stock.py +48 -0
  93. odoo/addons/shopfloor/tests/test_checkout_auto_post.py +67 -0
  94. odoo/addons/shopfloor/tests/test_checkout_base.py +81 -0
  95. odoo/addons/shopfloor/tests/test_checkout_cancel_line.py +154 -0
  96. odoo/addons/shopfloor/tests/test_checkout_change_packaging.py +184 -0
  97. odoo/addons/shopfloor/tests/test_checkout_done.py +133 -0
  98. odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +131 -0
  99. odoo/addons/shopfloor/tests/test_checkout_list_package.py +327 -0
  100. odoo/addons/shopfloor/tests/test_checkout_new_package.py +88 -0
  101. odoo/addons/shopfloor/tests/test_checkout_no_package.py +95 -0
  102. odoo/addons/shopfloor/tests/test_checkout_scan.py +174 -0
  103. odoo/addons/shopfloor/tests/test_checkout_scan_line.py +377 -0
  104. odoo/addons/shopfloor/tests/test_checkout_scan_line_base.py +25 -0
  105. odoo/addons/shopfloor/tests/test_checkout_scan_line_no_prefill_qty.py +91 -0
  106. odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +451 -0
  107. odoo/addons/shopfloor/tests/test_checkout_scan_package_action_no_prefill_qty.py +107 -0
  108. odoo/addons/shopfloor/tests/test_checkout_select.py +74 -0
  109. odoo/addons/shopfloor/tests/test_checkout_select_line.py +130 -0
  110. odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +64 -0
  111. odoo/addons/shopfloor/tests/test_checkout_set_qty.py +257 -0
  112. odoo/addons/shopfloor/tests/test_checkout_summary.py +69 -0
  113. odoo/addons/shopfloor/tests/test_cluster_picking_base.py +83 -0
  114. odoo/addons/shopfloor/tests/test_cluster_picking_batch.py +109 -0
  115. odoo/addons/shopfloor/tests/test_cluster_picking_change_pack_lot.py +111 -0
  116. odoo/addons/shopfloor/tests/test_cluster_picking_is_zero.py +98 -0
  117. odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination.py +376 -0
  118. odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination_no_prefill_qty.py +115 -0
  119. odoo/addons/shopfloor/tests/test_cluster_picking_scan_line.py +402 -0
  120. odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_location_or_pack_first.py +114 -0
  121. odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_no_prefill_qty.py +70 -0
  122. odoo/addons/shopfloor/tests/test_cluster_picking_select.py +387 -0
  123. odoo/addons/shopfloor/tests/test_cluster_picking_skip.py +90 -0
  124. odoo/addons/shopfloor/tests/test_cluster_picking_stock_issue.py +364 -0
  125. odoo/addons/shopfloor/tests/test_cluster_picking_unload.py +911 -0
  126. odoo/addons/shopfloor/tests/test_delivery_base.py +155 -0
  127. odoo/addons/shopfloor/tests/test_delivery_done.py +108 -0
  128. odoo/addons/shopfloor/tests/test_delivery_list_stock_picking.py +49 -0
  129. odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_line.py +119 -0
  130. odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_pack.py +107 -0
  131. odoo/addons/shopfloor/tests/test_delivery_scan_deliver.py +557 -0
  132. odoo/addons/shopfloor/tests/test_delivery_select.py +38 -0
  133. odoo/addons/shopfloor/tests/test_delivery_set_qty_done_line.py +91 -0
  134. odoo/addons/shopfloor/tests/test_delivery_set_qty_done_pack.py +135 -0
  135. odoo/addons/shopfloor/tests/test_delivery_sublocation.py +180 -0
  136. odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +136 -0
  137. odoo/addons/shopfloor/tests/test_location_content_transfer_get_work.py +125 -0
  138. odoo/addons/shopfloor/tests/test_location_content_transfer_mix.py +509 -0
  139. odoo/addons/shopfloor/tests/test_location_content_transfer_putaway.py +143 -0
  140. odoo/addons/shopfloor/tests/test_location_content_transfer_scan_location.py +34 -0
  141. odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_all.py +343 -0
  142. odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +1074 -0
  143. odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +748 -0
  144. odoo/addons/shopfloor/tests/test_location_content_transfer_start.py +359 -0
  145. odoo/addons/shopfloor/tests/test_menu_base.py +261 -0
  146. odoo/addons/shopfloor/tests/test_menu_counters.py +61 -0
  147. odoo/addons/shopfloor/tests/test_misc.py +25 -0
  148. odoo/addons/shopfloor/tests/test_move_action_assign.py +87 -0
  149. odoo/addons/shopfloor/tests/test_openapi.py +21 -0
  150. odoo/addons/shopfloor/tests/test_picking_form.py +62 -0
  151. odoo/addons/shopfloor/tests/test_scan_anything.py +49 -0
  152. odoo/addons/shopfloor/tests/test_single_pack_transfer.py +1121 -0
  153. odoo/addons/shopfloor/tests/test_single_pack_transfer_base.py +32 -0
  154. odoo/addons/shopfloor/tests/test_single_pack_transfer_putaway.py +104 -0
  155. odoo/addons/shopfloor/tests/test_stock_split.py +204 -0
  156. odoo/addons/shopfloor/tests/test_user.py +42 -0
  157. odoo/addons/shopfloor/tests/test_zone_picking_base.py +608 -0
  158. odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +140 -0
  159. odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +723 -0
  160. odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py +207 -0
  161. odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py.bak +202 -0
  162. odoo/addons/shopfloor/tests/test_zone_picking_select_line_no_prefill_qty.py +107 -0
  163. odoo/addons/shopfloor/tests/test_zone_picking_select_picking_type.py +26 -0
  164. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +643 -0
  165. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_no_prefill_qty.py +146 -0
  166. odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +241 -0
  167. odoo/addons/shopfloor/tests/test_zone_picking_start.py +206 -0
  168. odoo/addons/shopfloor/tests/test_zone_picking_stock_issue.py +121 -0
  169. odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +353 -0
  170. odoo/addons/shopfloor/tests/test_zone_picking_unload_buffer_lines.py +113 -0
  171. odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +374 -0
  172. odoo/addons/shopfloor/tests/test_zone_picking_unload_single.py +123 -0
  173. odoo/addons/shopfloor/tests/test_zone_picking_zero_check.py +43 -0
  174. odoo/addons/shopfloor/utils.py +13 -0
  175. odoo/addons/shopfloor/views/shopfloor_menu.xml +167 -0
  176. odoo/addons/shopfloor/views/stock_location.xml +20 -0
  177. odoo/addons/shopfloor/views/stock_move_line.xml +52 -0
  178. odoo/addons/shopfloor/views/stock_picking_type.xml +19 -0
  179. odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/METADATA +192 -0
  180. odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/RECORD +182 -0
  181. odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/WHEEL +5 -0
  182. odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/top_level.txt +1 -0
@@ -0,0 +1,451 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from itertools import product
4
+ from unittest import mock
5
+
6
+ from .test_checkout_base import CheckoutCommonCase
7
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
8
+
9
+
10
+ class CheckoutScanPackageActionCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
11
+ def _test_select_product(
12
+ self, barcode_func, origin_qty_func, expected_qty_func, in_lot=False
13
+ ):
14
+ picking = self._create_picking(
15
+ lines=[(self.product_a, 10), (self.product_b, 10), (self.product_c, 10)]
16
+ )
17
+ for move_line in picking.move_ids:
18
+ # put in 3 different packages
19
+ self._fill_stock_for_moves(move_line, in_package=True, in_lot=in_lot)
20
+ picking.action_assign()
21
+
22
+ # we have selected the pack that contains product a
23
+ line_a = picking.move_line_ids[0]
24
+ line_a.qty_done = origin_qty_func(line_a)
25
+
26
+ response = self.service.dispatch(
27
+ "scan_package_action",
28
+ params={
29
+ "picking_id": picking.id,
30
+ "selected_line_ids": line_a.ids,
31
+ "barcode": barcode_func(line_a),
32
+ },
33
+ )
34
+
35
+ # since we scanned the barcode of the product and we had a
36
+ # qty_done, the qty_done should flip to 0
37
+ self._assert_selected_qties(
38
+ response, line_a, {line_a: expected_qty_func(line_a)}
39
+ )
40
+
41
+ def test_scan_package_action_select_product(self):
42
+ self._test_select_product(
43
+ lambda l: l.product_id.barcode, lambda l: l.reserved_uom_qty, lambda __: 0
44
+ )
45
+
46
+ def test_scan_package_action_deselect_product(self):
47
+ self._test_select_product(
48
+ lambda l: l.product_id.barcode, lambda __: 0, lambda l: l.reserved_uom_qty
49
+ )
50
+
51
+ def test_scan_package_action_select_product_packaging(self):
52
+ self._test_select_product(
53
+ lambda l: l.product_id.packaging_ids.barcode,
54
+ lambda l: l.reserved_uom_qty,
55
+ lambda __: 0,
56
+ )
57
+
58
+ def test_scan_package_action_deselect_product_packaging(self):
59
+ self._test_select_product(
60
+ lambda l: l.product_id.packaging_ids.barcode,
61
+ lambda __: 0,
62
+ lambda l: l.reserved_uom_qty,
63
+ )
64
+
65
+ def test_scan_package_action_select_product_lot(self):
66
+ self._test_select_product(
67
+ lambda l: l.lot_id.name,
68
+ lambda __: 0,
69
+ lambda l: l.reserved_uom_qty,
70
+ in_lot=True,
71
+ )
72
+
73
+ def test_scan_package_action_deselect_product_lot(self):
74
+ self._test_select_product(
75
+ lambda l: l.lot_id.name,
76
+ lambda l: l.reserved_uom_qty,
77
+ lambda __: 0,
78
+ in_lot=True,
79
+ )
80
+
81
+ def _test_scan_package_action_scan_product_error_tracked_by(
82
+ self, tracked_by, barcode
83
+ ):
84
+ self.product_a.tracking = tracked_by
85
+ picking = self._create_picking(lines=[(self.product_a, 1)])
86
+ self._fill_stock_for_moves(picking.move_ids, in_package=True)
87
+ picking.action_assign()
88
+ move_line = picking.move_line_ids
89
+ origin_qty_done = move_line.qty_done
90
+ response = self.service.dispatch(
91
+ "scan_package_action",
92
+ params={
93
+ "picking_id": picking.id,
94
+ "selected_line_ids": move_line.ids,
95
+ "barcode": barcode,
96
+ },
97
+ )
98
+ self._assert_selected_qties(
99
+ response,
100
+ move_line,
101
+ # no change as the scan was not valid
102
+ {move_line: origin_qty_done},
103
+ message={
104
+ "message_type": "warning",
105
+ "body": "Product tracked by lot, please scan one.",
106
+ },
107
+ )
108
+
109
+ def test_scan_package_action_scan_product_error_tracking(self):
110
+ trackings = ("lot", "serial")
111
+ barcodes = (self.product_a.barcode, self.product_a.packaging_ids.barcode)
112
+ for tracking, barcode in product(trackings, barcodes):
113
+ self._test_scan_package_action_scan_product_error_tracked_by(
114
+ tracking, barcode
115
+ )
116
+
117
+ def test_scan_package_action_scan_package_keep_source_package_error(self):
118
+ picking = self._create_picking(
119
+ lines=[
120
+ (self.product_a, 10),
121
+ (self.product_b, 10),
122
+ (self.product_c, 10),
123
+ (self.product_d, 10),
124
+ ]
125
+ )
126
+ pack1_moves = picking.move_ids[:3]
127
+ pack2_moves = picking.move_ids[3:]
128
+ # put in 2 packs, for this test, we'll work on pack1
129
+ self._fill_stock_for_moves(pack1_moves, in_package=True)
130
+ self._fill_stock_for_moves(pack2_moves, in_package=True)
131
+ picking.action_assign()
132
+
133
+ selected_lines = pack1_moves.move_line_ids
134
+ pack1 = pack1_moves.move_line_ids.package_id
135
+
136
+ move_line1, move_line2, move_line3 = selected_lines
137
+ # We'll put only product A and B in the package
138
+ move_line1.qty_done = move_line1.reserved_uom_qty
139
+ move_line2.qty_done = move_line2.reserved_uom_qty
140
+ move_line3.qty_done = 0
141
+
142
+ response = self.service.dispatch(
143
+ "scan_package_action",
144
+ params={
145
+ "picking_id": picking.id,
146
+ "selected_line_ids": selected_lines.ids,
147
+ # we try to keep the goods in the same package, so we scan the
148
+ # source package but this isn't allowed as it is not a delivery
149
+ # package (i.e. having a delivery packaging set)
150
+ "barcode": pack1.name,
151
+ },
152
+ )
153
+
154
+ self.assertRecordValues(
155
+ move_line1,
156
+ [{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
157
+ )
158
+ self.assertRecordValues(
159
+ move_line2,
160
+ [{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
161
+ )
162
+ self.assertRecordValues(
163
+ move_line3,
164
+ # qty_done was zero so it hasn't been done anyway
165
+ [{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
166
+ )
167
+ self.assert_response(
168
+ response,
169
+ # go pack to the screen to select lines to put in packages
170
+ next_state="select_package",
171
+ data={
172
+ "picking": self.data.picking(picking),
173
+ "selected_move_lines": self.data.move_lines(selected_lines),
174
+ "packing_info": self.service._data_for_packing_info(picking),
175
+ "no_package_enabled": not self.service.options.get(
176
+ "checkout__disable_no_package"
177
+ ),
178
+ },
179
+ message=self.service.msg_store.dest_package_not_valid(pack1),
180
+ )
181
+
182
+ def test_scan_package_action_scan_package_error_invalid(self):
183
+ picking = self._create_picking(lines=[(self.product_a, 10)])
184
+ move = picking.move_ids
185
+ self._fill_stock_for_moves(move, in_package=True)
186
+ picking.action_assign()
187
+
188
+ selected_line = move.move_line_ids
189
+ other_package = self.env["stock.quant.package"].create({})
190
+
191
+ response = self.service.dispatch(
192
+ "scan_package_action",
193
+ params={
194
+ "picking_id": picking.id,
195
+ "selected_line_ids": selected_line.ids,
196
+ "barcode": other_package.name,
197
+ },
198
+ )
199
+
200
+ self.assertRecordValues(
201
+ selected_line,
202
+ [
203
+ {
204
+ # the result package must remain identical, so equal to the
205
+ # source package
206
+ "result_package_id": selected_line.package_id.id,
207
+ "shopfloor_checkout_done": False,
208
+ }
209
+ ],
210
+ )
211
+ self._assert_selected_response(
212
+ response,
213
+ selected_line,
214
+ message=self.service.msg_store.dest_package_not_valid(other_package),
215
+ )
216
+
217
+ def test_scan_package_action_scan_package_use_existing_package_ok(self):
218
+ picking = self._create_picking(
219
+ lines=[
220
+ (self.product_a, 10),
221
+ (self.product_b, 10),
222
+ (self.product_c, 10),
223
+ (self.product_d, 10),
224
+ ]
225
+ )
226
+ pack1_moves = picking.move_ids[:3]
227
+ pack2_moves = picking.move_ids[3:]
228
+ # put in 2 packs, for this test, we'll work on pack1
229
+ self._fill_stock_for_moves(pack1_moves, in_package=True)
230
+ self._fill_stock_for_moves(pack2_moves, in_package=True)
231
+ picking.action_assign()
232
+
233
+ package = self.env["stock.quant.package"].create(
234
+ {"package_type_id": self.delivery_packaging.id}
235
+ )
236
+
237
+ # assume that product d was already put in a package,
238
+ # we must be able to put the lines of pack1 inside the same
239
+ pack2_moves.move_line_ids.write(
240
+ {"result_package_id": package.id, "shopfloor_checkout_done": True}
241
+ )
242
+
243
+ selected_lines = pack1_moves.move_line_ids
244
+ # they are all selected
245
+ selected_lines.write({"qty_done": 10.0})
246
+
247
+ response = self.service.dispatch(
248
+ "scan_package_action",
249
+ params={
250
+ "picking_id": picking.id,
251
+ "selected_line_ids": selected_lines.ids,
252
+ # use the package that was used for product D
253
+ "barcode": package.name,
254
+ },
255
+ )
256
+
257
+ self.assertRecordValues(
258
+ selected_lines,
259
+ [
260
+ {"result_package_id": package.id, "shopfloor_checkout_done": True},
261
+ {"result_package_id": package.id, "shopfloor_checkout_done": True},
262
+ {"result_package_id": package.id, "shopfloor_checkout_done": True},
263
+ ],
264
+ )
265
+
266
+ self.assert_response(
267
+ response,
268
+ # all the lines are packed, so we expect to go the summary screen
269
+ next_state="summary",
270
+ data={
271
+ "picking": self._stock_picking_data(picking, done=True),
272
+ "all_processed": True,
273
+ },
274
+ message=self.msg_store.goods_packed_in(package),
275
+ )
276
+
277
+ def test_scan_package_action_scan_packaging_ok(self):
278
+ picking = self._create_picking(
279
+ lines=[
280
+ (self.product_a, 10),
281
+ (self.product_b, 10),
282
+ (self.product_c, 10),
283
+ (self.product_d, 10),
284
+ ]
285
+ )
286
+ pack1_moves = picking.move_ids[:3]
287
+ pack2_moves = picking.move_ids[3:]
288
+ # put in 2 packs, for this test, we'll work on pack1
289
+ self._fill_stock_for_moves(pack1_moves, in_package=True)
290
+ self._fill_stock_for_moves(pack2_moves, in_package=True)
291
+ picking.action_assign()
292
+
293
+ selected_lines = pack1_moves.move_line_ids
294
+ pack1 = pack1_moves.move_line_ids.package_id
295
+
296
+ move_line1, move_line2, move_line3 = selected_lines
297
+ # we'll put only the first 2 lines (product A and B) in the new package
298
+ move_line1.qty_done = move_line1.reserved_uom_qty
299
+ move_line2.qty_done = move_line2.reserved_uom_qty
300
+ move_line3.qty_done = 0
301
+
302
+ packaging = (
303
+ self.env["stock.package.type"]
304
+ .sudo()
305
+ .create(
306
+ {
307
+ "name": "Pallet",
308
+ "barcode": "PPP",
309
+ "height": 12,
310
+ "width": 13,
311
+ "packaging_length": 14,
312
+ }
313
+ )
314
+ )
315
+
316
+ response = self.service.dispatch(
317
+ "scan_package_action",
318
+ params={
319
+ "picking_id": picking.id,
320
+ "selected_line_ids": selected_lines.ids,
321
+ # create a new package using this packaging
322
+ "barcode": packaging.barcode,
323
+ },
324
+ )
325
+
326
+ new_package = move_line1.result_package_id
327
+ self.assertNotEqual(pack1, new_package)
328
+
329
+ self.assertRecordValues(
330
+ new_package,
331
+ [
332
+ {
333
+ "package_type_id": packaging.id,
334
+ "pack_length": packaging.packaging_length,
335
+ "width": packaging.width,
336
+ "height": packaging.height,
337
+ }
338
+ ],
339
+ )
340
+
341
+ self.assertRecordValues(
342
+ move_line1,
343
+ [{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
344
+ )
345
+ self.assertRecordValues(
346
+ move_line2,
347
+ [{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
348
+ )
349
+ self.assertRecordValues(
350
+ move_line3,
351
+ # qty_done was zero so we don't set it as packed and it remains in
352
+ # the same package
353
+ [{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
354
+ )
355
+ self.assert_response(
356
+ response,
357
+ next_state="select_line",
358
+ data=self._data_for_select_line(picking),
359
+ message=self.msg_store.goods_packed_in(new_package),
360
+ )
361
+
362
+ def test_scan_package_action_scan_packaging_bad_carrier(self):
363
+ picking = self._create_picking(lines=[(self.product_a, 10)])
364
+ picking.carrier_id = picking.carrier_id.search([], limit=1)
365
+ pack1_moves = picking.move_ids
366
+ # put in 2 packs, for this test, we'll work on pack1
367
+ self._fill_stock_for_moves(pack1_moves, in_package=True)
368
+ picking.action_assign()
369
+ selected_lines = pack1_moves.move_line_ids
370
+ selected_lines.qty_done = selected_lines.reserved_uom_qty
371
+
372
+ packaging = (
373
+ self.env["stock.package.type"]
374
+ .sudo()
375
+ .create(
376
+ {
377
+ "name": "DeliverX",
378
+ "package_carrier_type": "none",
379
+ "barcode": "XXX",
380
+ }
381
+ )
382
+ )
383
+ # Delivery type and package_carrier_type values
384
+ # depend on specific implementations that we don't have as dependency.
385
+ # What is important here is to simulate their value when mismatching.
386
+ mock1 = mock.patch.object(
387
+ type(packaging),
388
+ "package_carrier_type",
389
+ new_callable=mock.PropertyMock,
390
+ )
391
+ mock2 = mock.patch.object(
392
+ type(picking.carrier_id),
393
+ "delivery_type",
394
+ new_callable=mock.PropertyMock,
395
+ )
396
+ with mock1 as mocked_package_carrier_type, mock2 as mocked_delivery_type:
397
+ # Not matching at all -> bad
398
+ mocked_package_carrier_type.return_value = "DHL"
399
+ mocked_delivery_type.return_value = "UPS"
400
+ response = self.service.dispatch(
401
+ "scan_package_action",
402
+ params={
403
+ "picking_id": picking.id,
404
+ "selected_line_ids": selected_lines.ids,
405
+ # create a new package using this packaging
406
+ "barcode": packaging.barcode,
407
+ },
408
+ )
409
+ self._assert_selected_response(
410
+ response,
411
+ selected_lines,
412
+ message=self.msg_store.packaging_invalid_for_carrier(
413
+ packaging, picking.carrier_id
414
+ ),
415
+ )
416
+ # No carrier type set on the packaging -> good
417
+ mocked_package_carrier_type.return_value = "none"
418
+ response = self.service.dispatch(
419
+ "scan_package_action",
420
+ params={
421
+ "picking_id": picking.id,
422
+ "selected_line_ids": selected_lines.ids,
423
+ # create a new package using this packaging
424
+ "barcode": packaging.barcode,
425
+ },
426
+ )
427
+ self.assertEqual(
428
+ response["message"],
429
+ self.msg_store.goods_packed_in(selected_lines.result_package_id),
430
+ )
431
+
432
+ def test_scan_package_action_scan_not_found(self):
433
+ picking = self._create_picking(lines=[(self.product_a, 10)])
434
+ move = picking.move_ids
435
+ self._fill_stock_for_moves(move, in_package=True)
436
+ picking.action_assign()
437
+ selected_line = move.move_line_ids
438
+ response = self.service.dispatch(
439
+ "scan_package_action",
440
+ params={
441
+ "picking_id": picking.id,
442
+ "selected_line_ids": selected_line.ids,
443
+ # create a new package using this packaging
444
+ "barcode": "BARCODE NOT FOUND",
445
+ },
446
+ )
447
+ self._assert_selected_response(
448
+ response,
449
+ selected_line,
450
+ message={"message_type": "error", "body": "Barcode not found"},
451
+ )
@@ -0,0 +1,107 @@
1
+ # Copyright 2022 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ from .test_checkout_base import CheckoutCommonCase
5
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
6
+
7
+ # pylint: disable=missing-return
8
+
9
+
10
+ class CheckoutScanPackageActionCaseNoPrefillQty(
11
+ CheckoutCommonCase, CheckoutSelectPackageMixin
12
+ ):
13
+ @classmethod
14
+ def setUpClass(cls):
15
+ super().setUpClass()
16
+ cls.menu.sudo().no_prefill_qty = True
17
+
18
+ def test_scan_package_action_scan_product_to_increment_qty(self):
19
+ """ """
20
+ picking = self._create_picking(lines=[(self.product_a, 3)])
21
+ self._fill_stock_for_moves(picking.move_ids, in_package=False)
22
+ picking.action_assign()
23
+ move_line = picking.move_line_ids
24
+ origin_qty_done = move_line.qty_done = 2
25
+ response = self.service.dispatch(
26
+ "scan_package_action",
27
+ params={
28
+ "picking_id": picking.id,
29
+ "selected_line_ids": move_line.ids,
30
+ "barcode": move_line.product_id.barcode,
31
+ },
32
+ )
33
+ self._assert_selected_qties(
34
+ response,
35
+ move_line,
36
+ {move_line: origin_qty_done + 1},
37
+ )
38
+
39
+ def test_scan_package_action_scan_product2_to_increment_qty(self):
40
+ """Scan a product which is present in two lines.
41
+
42
+ Only one line should have its quantity incremented.
43
+
44
+ """
45
+ picking = self._create_picking(
46
+ lines=[(self.product_a, 3), (self.product_a, 1)], confirm=False
47
+ )
48
+ picking.move_ids._action_confirm(merge=False)
49
+ picking.action_confirm()
50
+ self._fill_stock_for_moves(picking.move_ids, in_package=False)
51
+ picking.action_assign()
52
+ move_lines = picking.move_line_ids
53
+ response = self.service.dispatch(
54
+ "scan_package_action",
55
+ params={
56
+ "picking_id": picking.id,
57
+ "selected_line_ids": move_lines.ids,
58
+ "barcode": self.product_a.barcode,
59
+ },
60
+ )
61
+ self._assert_selected_qties(
62
+ response,
63
+ move_lines,
64
+ {move_lines[0]: 1, move_lines[1]: 0},
65
+ )
66
+
67
+ def test_scan_package_action_scan_lot_to_increment_qty(self):
68
+ """ """
69
+ picking = self._create_picking(lines=[(self.product_a, 3)])
70
+ self._fill_stock_for_moves(picking.move_ids, in_lot=True)
71
+ picking.action_assign()
72
+ move_line = picking.move_line_ids
73
+ origin_qty_done = move_line.qty_done = 2
74
+ response = self.service.dispatch(
75
+ "scan_package_action",
76
+ params={
77
+ "picking_id": picking.id,
78
+ "selected_line_ids": move_line.ids,
79
+ "barcode": move_line.lot_id.name,
80
+ },
81
+ )
82
+ self._assert_selected_qties(
83
+ response,
84
+ move_line,
85
+ {move_line: origin_qty_done + 1},
86
+ )
87
+
88
+ def test_scan_package_action_scan_packaging_to_increment_qty(self):
89
+ """ """
90
+ picking = self._create_picking(lines=[(self.product_a, 3)])
91
+ self._fill_stock_for_moves(picking.move_ids, in_package=True, in_lot=False)
92
+ picking.action_assign()
93
+ move_line = picking.move_line_ids
94
+ origin_qty_done = move_line.qty_done = 0
95
+ response = self.service.dispatch(
96
+ "scan_package_action",
97
+ params={
98
+ "picking_id": picking.id,
99
+ "selected_line_ids": move_line.ids,
100
+ "barcode": self.product_a_packaging.barcode,
101
+ },
102
+ )
103
+ self._assert_selected_qties(
104
+ response,
105
+ move_line,
106
+ {move_line: origin_qty_done + self.product_a_packaging.qty},
107
+ )
@@ -0,0 +1,74 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from .test_checkout_base import CheckoutCommonCase
4
+
5
+ # pylint: disable=missing-return
6
+
7
+
8
+ class CheckoutListStockPickingCase(CheckoutCommonCase):
9
+ def test_list_stock_picking(self):
10
+ picking1 = self._create_picking()
11
+ picking2 = self._create_picking()
12
+ # should not be in the list because another type:
13
+ picking3 = self._create_picking(picking_type=self.wh.pick_type_id)
14
+ # should not be in list because not assigned:
15
+ self._create_picking()
16
+ to_assign = picking1 | picking2 | picking3
17
+ self._fill_stock_for_moves(to_assign.move_ids, in_package=True)
18
+ to_assign.action_assign()
19
+ response = self.service.dispatch("list_stock_picking", params={})
20
+ expected = {
21
+ "pickings": [
22
+ self._picking_summary_data(picking1),
23
+ self._picking_summary_data(picking2),
24
+ ]
25
+ }
26
+
27
+ self.assert_response(response, next_state="manual_selection", data=expected)
28
+
29
+
30
+ class CheckoutSelectCase(CheckoutCommonCase):
31
+ @classmethod
32
+ def setUpClassBaseData(cls):
33
+ super().setUpClassBaseData()
34
+ cls.picking = cls._create_picking()
35
+ cls._fill_stock_for_moves(cls.picking.move_ids, in_package=True)
36
+ cls.picking.action_assign()
37
+
38
+ def test_select_ok(self):
39
+ response = self.service.dispatch(
40
+ "select", params={"picking_id": self.picking.id}
41
+ )
42
+ self.assert_response(
43
+ response,
44
+ next_state="select_line",
45
+ data=self._data_for_select_line(self.picking),
46
+ )
47
+
48
+ def _test_error(self, picking, msg):
49
+ response = self.service.dispatch("select", params={"picking_id": picking.id})
50
+ self.assert_response(
51
+ response,
52
+ next_state="manual_selection",
53
+ message={"message_type": "error", "body": msg},
54
+ data={"pickings": [self._picking_summary_data(self.picking)]},
55
+ )
56
+
57
+ def test_select_error_not_found(self):
58
+ picking = self._create_picking()
59
+ picking.sudo().unlink()
60
+ self._test_error(
61
+ picking, self.service.msg_store.stock_picking_not_found()["body"]
62
+ )
63
+
64
+ def test_select_error_not_available(self):
65
+ picking = self._create_picking()
66
+ self._test_error(
67
+ picking, self.service.msg_store.stock_picking_not_available(picking)["body"]
68
+ )
69
+
70
+ def test_select_error_not_allowed(self):
71
+ picking = self._create_picking(picking_type=self.wh.pick_type_id)
72
+ self._fill_stock_for_moves(picking.move_ids, in_package=True)
73
+ picking.action_assign()
74
+ self._test_error(picking, "You cannot move this using this menu.")