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,643 @@
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_zone_picking_base import ZonePickingCommonCase
4
+
5
+ # pylint: disable=missing-return
6
+
7
+
8
+ class ZonePickingSetLineDestinationCase(ZonePickingCommonCase):
9
+ """Tests for endpoint used from set_line_destination
10
+
11
+ * /set_destination
12
+
13
+ """
14
+
15
+ def setUp(self):
16
+ super().setUp()
17
+ self.service.work.current_picking_type = self.picking1.picking_type_id
18
+
19
+ def test_set_destination_wrong_parameters(self):
20
+ move_line = self.picking1.move_line_ids[0]
21
+ response = self.service.dispatch(
22
+ "set_destination",
23
+ params={
24
+ "move_line_id": 1234567890,
25
+ "barcode": self.packing_location.barcode,
26
+ "quantity": move_line.reserved_uom_qty,
27
+ "confirmation": False,
28
+ },
29
+ )
30
+ self.assert_response_start(
31
+ response,
32
+ message=self.service.msg_store.record_not_found(),
33
+ )
34
+
35
+ def test_set_destination_location_confirm(self):
36
+ """Scanned barcode is the destination location but needs confirmation
37
+ as it is outside the current move line destination but is still
38
+ allowed by the picking type's default destination.
39
+ """
40
+ zone_location = self.zone_location
41
+ picking_type = self.picking1.picking_type_id
42
+ move_line = self.picking1.move_line_ids
43
+ move_line.location_dest_id = self.shelf1
44
+ quantity_done = move_line.reserved_uom_qty
45
+ response = self.service.dispatch(
46
+ "set_destination",
47
+ params={
48
+ "move_line_id": move_line.id,
49
+ "barcode": self.packing_location.barcode,
50
+ "quantity": quantity_done,
51
+ "confirmation": False,
52
+ },
53
+ )
54
+ # Check response
55
+ self.assert_response_set_line_destination(
56
+ response,
57
+ zone_location,
58
+ picking_type,
59
+ move_line,
60
+ message=self.service.msg_store.confirm_location_changed(
61
+ move_line.location_dest_id, self.packing_location
62
+ ),
63
+ confirmation_required=True,
64
+ qty_done=quantity_done,
65
+ )
66
+ # Confirm the destination with a wrong destination (should not happen)
67
+ response = self.service.dispatch(
68
+ "set_destination",
69
+ params={
70
+ "move_line_id": move_line.id,
71
+ "barcode": self.customer_location.barcode,
72
+ "quantity": move_line.reserved_uom_qty,
73
+ "confirmation": True,
74
+ },
75
+ )
76
+ # Check response
77
+ self.assert_response_set_line_destination(
78
+ response,
79
+ zone_location,
80
+ picking_type,
81
+ move_line,
82
+ message=self.service.msg_store.dest_location_not_allowed(),
83
+ qty_done=quantity_done,
84
+ )
85
+ # Confirm the destination with the right destination this time
86
+ response = self.service.dispatch(
87
+ "set_destination",
88
+ params={
89
+ "move_line_id": move_line.id,
90
+ "barcode": self.packing_location.barcode,
91
+ "quantity": move_line.reserved_uom_qty,
92
+ "confirmation": True,
93
+ },
94
+ )
95
+ # Check response
96
+ move_lines = self.service._find_location_move_lines()
97
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
98
+ self.assert_response_select_line(
99
+ response,
100
+ zone_location,
101
+ picking_type,
102
+ move_lines,
103
+ message=self.service.msg_store.confirm_pack_moved(),
104
+ )
105
+
106
+ def test_set_destination_location_move_invalid_location(self):
107
+ # Confirm the destination with a wrong destination, outside of picking
108
+ # and move's move line (should not happen)
109
+ zone_location = self.zone_location
110
+ picking_type = self.picking1.picking_type_id
111
+ move_line = self.picking1.move_line_ids
112
+ move_line.move_id.location_dest_id = self.packing_sublocation_a
113
+ move_line.picking_id.location_dest_id = self.packing_sublocation_a
114
+ quantity_done = move_line.reserved_uom_qty
115
+ response = self.service.dispatch(
116
+ "set_destination",
117
+ params={
118
+ "move_line_id": move_line.id,
119
+ "barcode": self.packing_sublocation_b.barcode,
120
+ "quantity": quantity_done,
121
+ "confirmation": True,
122
+ },
123
+ )
124
+ # Check response
125
+ self.assert_response_set_line_destination(
126
+ response,
127
+ zone_location,
128
+ picking_type,
129
+ move_line,
130
+ message=self.service.msg_store.dest_location_not_allowed(),
131
+ qty_done=quantity_done,
132
+ )
133
+
134
+ def test_set_destination_location_no_other_move_line_full_qty(self):
135
+ """Scanned barcode is the destination location.
136
+
137
+ The move line is the only one in the move, and we move the whole qty.
138
+
139
+ Initial data:
140
+
141
+ move qty 10 (assigned):
142
+ -> move_line qty 10 from location X
143
+
144
+ Then the operator move the 10 qty, we get:
145
+
146
+ move qty 10 (done):
147
+ -> move_line qty 10 from location X
148
+ """
149
+ zone_location = self.zone_location
150
+ picking_type = self.picking1.picking_type_id
151
+ moves_before = self.picking1.move_ids
152
+ self.assertEqual(len(moves_before), 1)
153
+ self.assertEqual(len(moves_before.move_line_ids), 1)
154
+ move_line = moves_before.move_line_ids
155
+ response = self.service.dispatch(
156
+ "set_destination",
157
+ params={
158
+ "move_line_id": move_line.id,
159
+ "barcode": self.packing_location.barcode,
160
+ "quantity": move_line.reserved_uom_qty,
161
+ "confirmation": False,
162
+ },
163
+ )
164
+ self.assertEqual(move_line.state, "done")
165
+ # Check picking data
166
+ moves_after = self.picking1.move_ids
167
+ self.assertEqual(moves_before, moves_after)
168
+ self.assertEqual(move_line.qty_done, 10)
169
+ # Check response
170
+ move_lines = self.service._find_location_move_lines()
171
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
172
+ self.assert_response_select_line(
173
+ response,
174
+ zone_location,
175
+ picking_type,
176
+ move_lines,
177
+ message=self.service.msg_store.confirm_pack_moved(),
178
+ )
179
+
180
+ def test_set_destination_location_no_other_move_line_partial_qty(self):
181
+ """Scanned barcode is the destination location.
182
+
183
+ The move line is the only one in the move, and we move some of the qty.
184
+
185
+ Initial data:
186
+
187
+ move qty 10 (assigned):
188
+ -> move_line qty 10 from location X
189
+
190
+ Then the operator move 6 qty on 10, we get:
191
+
192
+ an error because we can move only full qty by location
193
+ and only a package barcode is allowed on scan.
194
+ """
195
+ zone_location = self.zone_location
196
+ picking_type = self.picking3.picking_type_id
197
+ barcode = self.packing_location.barcode
198
+ moves_before = self.picking3.move_ids
199
+ self.assertEqual(len(moves_before), 1)
200
+ self.assertEqual(len(moves_before.move_line_ids), 1)
201
+ move_line = moves_before.move_line_ids
202
+ # we need a destination package if we want to scan a destination location
203
+ move_line.result_package_id = self.free_package
204
+ response = self.service.dispatch(
205
+ "set_destination",
206
+ params={
207
+ "move_line_id": move_line.id,
208
+ "barcode": barcode,
209
+ "quantity": 6,
210
+ "confirmation": False,
211
+ },
212
+ )
213
+ self.assert_response_set_line_destination(
214
+ response,
215
+ zone_location,
216
+ picking_type,
217
+ move_line,
218
+ qty_done=6,
219
+ message=self.service.msg_store.package_not_found_for_barcode(barcode),
220
+ )
221
+
222
+ def test_set_destination_location_several_move_line_full_qty(self):
223
+ """Scanned barcode is the destination location.
224
+
225
+ The move line has siblings in the move, and we move the whole qty:
226
+ the processed move line will then get its own move (split from original one)
227
+
228
+ Initial data:
229
+
230
+ move qty 10 (assigned):
231
+ -> move_line qty 6 from location X
232
+ -> move_line qty 4 from location Y
233
+
234
+ Then the operator move 6 qty (from the first move line), we get:
235
+
236
+ move qty 6 (done):
237
+ -> move_line qty 4 from location X
238
+ move qty 4 (assigned):
239
+ -> move_line qty 4 from location Y (untouched)
240
+ """
241
+ zone_location = self.zone_location
242
+ picking_type = self.picking4.picking_type_id
243
+ moves_before = self.picking4.move_ids
244
+ self.assertEqual(len(moves_before), 1)
245
+ self.assertEqual(len(moves_before.move_line_ids), 2)
246
+ move_line = moves_before.move_line_ids[0]
247
+ # we need a destination package if we want to scan a destination location
248
+ move_line.result_package_id = self.free_package
249
+ other_move_line = moves_before.move_line_ids[1]
250
+ response = self.service.dispatch(
251
+ "set_destination",
252
+ params={
253
+ "move_line_id": move_line.id,
254
+ "barcode": self.packing_location.barcode,
255
+ "quantity": move_line.reserved_uom_qty, # 6 qty
256
+ "confirmation": False,
257
+ },
258
+ )
259
+ self.assertEqual(move_line.state, "done")
260
+ # Check picking data (move has been split in two, 6 done and 4 remaining)
261
+
262
+ done_picking = self.picking4.backorder_ids
263
+ self.assertEqual(done_picking.state, "done")
264
+ self.assertEqual(self.picking4.state, "assigned")
265
+ move_after = self.picking4.move_ids
266
+ self.assertEqual(len(move_after), 1)
267
+ self.assertEqual(move_line.move_id.product_uom_qty, 6)
268
+ self.assertEqual(move_line.move_id.state, "done")
269
+ self.assertEqual(move_line.move_id.move_line_ids.reserved_uom_qty, 0)
270
+ self.assertEqual(move_after.product_uom_qty, 4)
271
+ self.assertEqual(move_after.state, "assigned")
272
+ self.assertEqual(move_after.move_line_ids.reserved_uom_qty, 4)
273
+ self.assertEqual(move_line.qty_done, 6)
274
+ self.assertNotEqual(move_line.move_id, other_move_line.move_id)
275
+ # Check response
276
+ move_lines = self.service._find_location_move_lines()
277
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
278
+ self.assert_response_select_line(
279
+ response,
280
+ zone_location,
281
+ picking_type,
282
+ move_lines,
283
+ message=self.service.msg_store.confirm_pack_moved(),
284
+ )
285
+
286
+ def test_set_destination_location_several_move_line_partial_qty(self):
287
+ """Scanned barcode is the destination location.
288
+
289
+ The move line has siblings in the move, and we move some of the qty:
290
+ the processed move line will then get its own move (split from original one)
291
+
292
+ Initial data:
293
+
294
+ move qty 10 (assigned):
295
+ -> move_line qty 6 from location X
296
+ -> move_line qty 4 from location Y
297
+
298
+ Then the operator move 4 qty on 6 (from the first move line), we get:
299
+
300
+ an error because we can move only full qty by location
301
+ and only a package barcode is allowed on scan.
302
+ """
303
+ zone_location = self.zone_location
304
+ picking_type = self.picking4.picking_type_id
305
+ barcode = self.packing_location.barcode
306
+ moves_before = self.picking4.move_ids
307
+ self.assertEqual(len(moves_before), 1) # 10 qty
308
+ self.assertEqual(len(moves_before.move_line_ids), 2) # 6+4 qty
309
+ move_line = moves_before.move_line_ids[0]
310
+ # we need a destination package if we want to scan a destination location
311
+ move_line.result_package_id = self.free_package
312
+ response = self.service.dispatch(
313
+ "set_destination",
314
+ params={
315
+ "move_line_id": move_line.id,
316
+ "barcode": barcode,
317
+ "quantity": 4, # 4/6 qty
318
+ "confirmation": False,
319
+ },
320
+ )
321
+ self.assert_response_set_line_destination(
322
+ response,
323
+ zone_location,
324
+ picking_type,
325
+ move_line,
326
+ qty_done=4,
327
+ message=self.service.msg_store.package_not_found_for_barcode(barcode),
328
+ )
329
+
330
+ def test_set_destination_location_zero_check(self):
331
+ """Scanned barcode is the destination location.
332
+
333
+ The move line is the only one in the source location, as such the
334
+ 'zero_check' step is triggered.
335
+ """
336
+ zone_location = self.zone_location
337
+ picking_type = self.picking1.picking_type_id
338
+ picking_type.sudo().shopfloor_zero_check = True
339
+ self.assertEqual(len(self.picking1.move_line_ids), 1)
340
+ move_line = self.picking1.move_line_ids
341
+ location_is_empty = move_line.location_id.planned_qty_in_location_is_empty
342
+ self.assertFalse(location_is_empty())
343
+ response = self.service.dispatch(
344
+ "set_destination",
345
+ params={
346
+ "move_line_id": move_line.id,
347
+ "barcode": self.packing_location.barcode,
348
+ "quantity": move_line.reserved_uom_qty,
349
+ "confirmation": False,
350
+ },
351
+ )
352
+ self.assertTrue(location_is_empty())
353
+ # Check response
354
+ self.assert_response_zero_check(
355
+ response, zone_location, picking_type, move_line
356
+ )
357
+
358
+ def test_set_destination_package_full_qty(self):
359
+ """Scanned barcode is the destination package.
360
+
361
+ Initial data:
362
+
363
+ move qty 10 (assigned):
364
+ -> move_line qty 10 from location X
365
+
366
+ Then the operator move the 10 qty, we get:
367
+
368
+ move qty 10 (done):
369
+ -> move_line qty 10 from location X with the scanned package
370
+ """
371
+ zone_location = self.zone_location
372
+ picking_type = self.picking1.picking_type_id
373
+ moves_before = self.picking1.move_ids
374
+ self.assertEqual(len(moves_before), 1)
375
+ self.assertEqual(len(moves_before.move_line_ids), 1)
376
+ move_line = moves_before.move_line_ids
377
+ response = self.service.dispatch(
378
+ "set_destination",
379
+ params={
380
+ "move_line_id": move_line.id,
381
+ "barcode": self.free_package.name,
382
+ "quantity": move_line.reserved_uom_qty,
383
+ "confirmation": False,
384
+ },
385
+ )
386
+ # Check picking data
387
+ moves_after = self.picking1.move_ids
388
+ self.assertEqual(moves_before, moves_after)
389
+ self.assertRecordValues(
390
+ move_line,
391
+ [
392
+ {
393
+ "result_package_id": self.free_package.id,
394
+ "reserved_uom_qty": 10,
395
+ "qty_done": 10,
396
+ "shopfloor_user_id": self.env.user.id,
397
+ },
398
+ ],
399
+ )
400
+ # Check response
401
+ move_lines = self.service._find_location_move_lines()
402
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
403
+ self.assert_response_select_line(
404
+ response,
405
+ zone_location,
406
+ picking_type,
407
+ move_lines,
408
+ message=self.service.msg_store.confirm_pack_moved(),
409
+ )
410
+
411
+ def test_set_destination_package_partial_qty(self):
412
+ """Scanned barcode is the destination package.
413
+
414
+ Initial data:
415
+
416
+ move qty 10 (assigned):
417
+ -> move_line qty 10 from location X
418
+
419
+ Then the operator move the 6 on 10 qty, we get:
420
+
421
+ move qty 6 (assigned):
422
+ -> move_line qty 6 from location X with the scanned package (buffer)
423
+ -> move_line qty 4 from location X (remaining)
424
+ """
425
+ zone_location = self.zone_location
426
+ picking_type = self.picking1.picking_type_id
427
+ moves_before = self.picking1.move_ids
428
+ self.assertEqual(len(moves_before), 1)
429
+ self.assertEqual(len(moves_before.move_line_ids), 1)
430
+ move_line = moves_before.move_line_ids
431
+ response = self.service.dispatch(
432
+ "set_destination",
433
+ params={
434
+ "move_line_id": move_line.id,
435
+ "barcode": self.free_package.name,
436
+ "quantity": 6,
437
+ "confirmation": False,
438
+ },
439
+ )
440
+ # Check picking data
441
+ moves_after = self.picking1.move_ids
442
+ new_move_line = self.picking1.move_line_ids.filtered(
443
+ lambda line: line != move_line
444
+ )
445
+ self.assertTrue(move_line != new_move_line)
446
+ self.assertEqual(moves_before, moves_after)
447
+ self.assertRecordValues(
448
+ move_line,
449
+ [
450
+ {
451
+ "result_package_id": self.free_package.id,
452
+ "reserved_uom_qty": 6,
453
+ "qty_done": 6,
454
+ "shopfloor_user_id": self.env.user.id,
455
+ },
456
+ ],
457
+ )
458
+ self.assertRecordValues(
459
+ new_move_line,
460
+ [
461
+ {
462
+ "result_package_id": new_move_line.package_id.id, # Unchanged
463
+ "reserved_uom_qty": 4,
464
+ "qty_done": 0,
465
+ "shopfloor_user_id": False,
466
+ },
467
+ ],
468
+ )
469
+ # Check response
470
+ move_lines = self.service._find_location_move_lines()
471
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
472
+ self.assert_response_select_line(
473
+ response,
474
+ zone_location,
475
+ picking_type,
476
+ move_lines,
477
+ message=self.service.msg_store.confirm_pack_moved(),
478
+ )
479
+
480
+ def test_set_destination_package_zero_check(self):
481
+ """Scanned barcode is the destination package.
482
+
483
+ The move line is the only one in the source location, as such the
484
+ 'zero_check' step is triggered.
485
+ """
486
+ zone_location = self.zone_location
487
+ picking_type = self.picking1.picking_type_id
488
+ picking_type.sudo().shopfloor_zero_check = True
489
+ self.assertEqual(len(self.picking1.move_line_ids), 1)
490
+ move_line = self.picking1.move_line_ids
491
+ location_is_empty = move_line.location_id.planned_qty_in_location_is_empty
492
+ self.assertFalse(location_is_empty())
493
+ response = self.service.dispatch(
494
+ "set_destination",
495
+ params={
496
+ "move_line_id": move_line.id,
497
+ "barcode": self.free_package.name,
498
+ "quantity": move_line.reserved_uom_qty,
499
+ "confirmation": False,
500
+ },
501
+ )
502
+ self.assertTrue(location_is_empty())
503
+ # Check response
504
+ self.assert_response_zero_check(
505
+ response,
506
+ zone_location,
507
+ picking_type,
508
+ move_line,
509
+ )
510
+
511
+ def test_set_same_destination_package_multiple_moves(self):
512
+ """Scanned barcode is the destination package."""
513
+ zone_location = self.zone_location
514
+ picking_type = self.picking1.picking_type_id
515
+ # picking_type.sudo().shopfloor_zero_check = True
516
+ self.assertEqual(len(self.picking1.move_line_ids), 1)
517
+ move_line = self.picking1.move_line_ids
518
+ location_is_empty = move_line.location_id.planned_qty_in_location_is_empty
519
+ self.assertFalse(location_is_empty())
520
+ response = self.service.dispatch(
521
+ "set_destination",
522
+ params={
523
+ "move_line_id": move_line.id,
524
+ "barcode": self.free_package.name,
525
+ "quantity": move_line.reserved_uom_qty,
526
+ "confirmation": False,
527
+ },
528
+ )
529
+ self.assertTrue(location_is_empty())
530
+ # Check response
531
+ move_lines = self.service._find_location_move_lines()
532
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
533
+ self.assert_response_select_line(
534
+ response,
535
+ zone_location,
536
+ picking_type,
537
+ move_lines,
538
+ message=self.service.msg_store.confirm_pack_moved(),
539
+ )
540
+ # Now, try to add more goods in the same package
541
+ move_line = self.picking3.move_line_ids
542
+ response = self.service.dispatch(
543
+ "set_destination",
544
+ params={
545
+ "move_line_id": move_line.id,
546
+ "barcode": self.free_package.name,
547
+ "quantity": move_line.reserved_uom_qty,
548
+ "confirmation": False,
549
+ },
550
+ )
551
+ self.assertEqual(
552
+ response["message"],
553
+ {
554
+ "body": "Package FREE_PACKAGE is already used.",
555
+ "message_type": "warning",
556
+ },
557
+ )
558
+ # Now enable `multiple_move_single_pack` and try again
559
+ self.menu.sudo().write(
560
+ {
561
+ "multiple_move_single_pack": True,
562
+ "unload_package_at_destination": True,
563
+ }
564
+ )
565
+ response = self.service.dispatch(
566
+ "set_destination",
567
+ params={
568
+ "move_line_id": move_line.id,
569
+ "barcode": self.free_package.name,
570
+ "quantity": move_line.reserved_uom_qty,
571
+ "confirmation": False,
572
+ },
573
+ )
574
+ # We now have no error in the response
575
+ move_lines = self.service._find_location_move_lines()
576
+ move_lines = move_lines.sorted(lambda l: l.move_id.priority, reverse=True)
577
+ self.assert_response_select_line(
578
+ response,
579
+ zone_location,
580
+ picking_type,
581
+ move_lines,
582
+ message=self.service.msg_store.confirm_pack_moved(),
583
+ )
584
+
585
+ def test_set_destination_location_zero_quantity(self):
586
+ """Scanned barcode is the destination location.
587
+
588
+ Quantity to move is zero -> error raised, user can try again.
589
+
590
+ """
591
+ zone_location = self.zone_location
592
+ picking_type = self.picking1.picking_type_id
593
+ move_line = self.picking1.move_ids.move_line_ids
594
+ response = self.service.dispatch(
595
+ "set_destination",
596
+ params={
597
+ "move_line_id": move_line.id,
598
+ "barcode": self.packing_location.barcode,
599
+ "quantity": 0,
600
+ },
601
+ )
602
+ # Check response
603
+ self.assert_response_set_line_destination(
604
+ response,
605
+ zone_location,
606
+ picking_type,
607
+ move_line,
608
+ message=self.service.msg_store.picking_zero_quantity(),
609
+ qty_done=move_line.reserved_uom_qty,
610
+ )
611
+
612
+ def test_set_destination_error_concurent_work(self):
613
+ """Scanned barcode is the destination package.
614
+
615
+ Move line is already being worked on by someone else
616
+ """
617
+ zone_location = self.zone_location
618
+ picking_type = self.picking1.picking_type_id
619
+ picking_type.sudo().shopfloor_zero_check = True
620
+ self.assertEqual(len(self.picking1.move_line_ids), 1)
621
+ move_line = self.picking1.move_line_ids
622
+ move_line.picking_id.user_id = self.shopfloor_manager
623
+ response = self.service.dispatch(
624
+ "set_destination",
625
+ params={
626
+ "move_line_id": move_line.id,
627
+ "barcode": self.free_package.name,
628
+ "quantity": move_line.reserved_uom_qty,
629
+ "confirmation": False,
630
+ },
631
+ )
632
+ # Check response
633
+ self.assert_response_set_line_destination(
634
+ response,
635
+ zone_location,
636
+ picking_type,
637
+ move_line,
638
+ message={
639
+ "message_type": "error",
640
+ "body": "Someone is already working on these transfers",
641
+ },
642
+ qty_done=move_line.reserved_uom_qty,
643
+ )