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,327 @@
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 odoo import fields
4
+
5
+ from .test_checkout_base import CheckoutCommonCase
6
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
7
+
8
+ # pylint: disable=missing-return
9
+
10
+
11
+ class SelectDestPackageMixin:
12
+ def _assert_response_select_dest_package(
13
+ self, response, picking, selected_lines, packages, message=None
14
+ ):
15
+ picking_data = self.data.picking(picking)
16
+ picking_data.update(
17
+ {
18
+ "note": None,
19
+ "origin": None,
20
+ "weight": 110.0,
21
+ "move_line_count": len(picking.move_line_ids),
22
+ }
23
+ )
24
+ self.assert_response(
25
+ response,
26
+ next_state="select_dest_package",
27
+ data={
28
+ "picking": picking_data,
29
+ "packages": [
30
+ self._package_data(
31
+ package.with_context(picking_id=picking.id), picking
32
+ )
33
+ for package in packages
34
+ ],
35
+ "selected_move_lines": [
36
+ self._move_line_data(ml) for ml in selected_lines.sorted()
37
+ ],
38
+ },
39
+ message=message,
40
+ )
41
+
42
+
43
+ class CheckoutListDestPackageCase(
44
+ CheckoutCommonCase, CheckoutSelectPackageMixin, SelectDestPackageMixin
45
+ ):
46
+ def test_list_dest_package_ok(self):
47
+ picking = self._create_picking(
48
+ lines=[
49
+ (self.product_a, 10),
50
+ (self.product_b, 10),
51
+ (self.product_c, 10),
52
+ (self.product_d, 10),
53
+ ]
54
+ )
55
+ self._fill_stock_for_moves(picking.move_ids[:2], in_package=True)
56
+ self._fill_stock_for_moves(picking.move_ids[2], in_package=True)
57
+ self._fill_stock_for_moves(picking.move_ids[3], in_package=True)
58
+ picking.action_assign()
59
+ delivery_packaging = self.env.ref(
60
+ "stock_storage_type.product_product_9_packaging_single_bag"
61
+ )
62
+ delivery_package = self.env["stock.quant.package"].create(
63
+ {"package_type_id": delivery_packaging.id}
64
+ )
65
+ picking.move_ids[1].move_line_ids.result_package_id = delivery_package
66
+ response = self.service.dispatch(
67
+ "list_dest_package",
68
+ params={
69
+ "picking_id": picking.id,
70
+ "selected_line_ids": picking.move_line_ids.ids,
71
+ },
72
+ )
73
+ self._assert_response_select_dest_package(
74
+ response, picking, picking.move_line_ids, delivery_package
75
+ )
76
+
77
+ def test_list_dest_package_error_no_package(self):
78
+ picking = self._create_picking(
79
+ lines=[
80
+ (self.product_a, 10),
81
+ (self.product_b, 10),
82
+ (self.product_c, 10),
83
+ (self.product_d, 10),
84
+ ]
85
+ )
86
+ self._fill_stock_for_moves(picking.move_ids)
87
+ picking.action_assign()
88
+ self.assertEqual(picking.state, "assigned")
89
+ response = self.service.dispatch(
90
+ "list_dest_package",
91
+ params={
92
+ "picking_id": picking.id,
93
+ "selected_line_ids": picking.move_line_ids.ids,
94
+ },
95
+ )
96
+ self._assert_selected_response(
97
+ response,
98
+ picking.move_line_ids,
99
+ message={"message_type": "warning", "body": "No valid package to select."},
100
+ )
101
+
102
+
103
+ class CheckoutScanSetDestPackageCase(CheckoutCommonCase, SelectDestPackageMixin):
104
+ @classmethod
105
+ def setUpClassBaseData(cls):
106
+ super().setUpClassBaseData()
107
+ picking = cls._create_picking(
108
+ lines=[
109
+ (cls.product_a, 10),
110
+ (cls.product_b, 10),
111
+ (cls.product_c, 10),
112
+ (cls.product_d, 10),
113
+ ]
114
+ )
115
+ pack1_moves = picking.move_ids[:3]
116
+ pack2_moves = picking.move_ids[3:]
117
+ # put in 2 packs, for this test, we'll work on pack1
118
+ cls._fill_stock_for_moves(pack1_moves, in_package=True)
119
+ cls._fill_stock_for_moves(pack2_moves, in_package=True)
120
+ picking.action_assign()
121
+
122
+ cls.selected_lines = pack1_moves.move_line_ids
123
+ cls.pack1 = pack1_moves.move_line_ids.package_id
124
+ cls.delivery_packaging = cls.env.ref(
125
+ "stock_storage_type.product_product_9_packaging_single_bag"
126
+ )
127
+ cls.delivery_package = cls.env["stock.quant.package"].create(
128
+ {"package_type_id": cls.delivery_packaging.id}
129
+ )
130
+ cls.move_line1, cls.move_line2, cls.move_line3 = cls.selected_lines
131
+ # The 'scan_dest_package' and 'set_dest_package' methods can not be
132
+ # used at all if there is no valid delivery package on the picking
133
+ # (the user is redirected to the 'select_package' step in that case),
134
+ # so we need at least to set one to pass this check in order to test
135
+ # them
136
+ cls.move_line1.result_package_id = cls.delivery_package
137
+ # We'll put only product A and B in the destination package
138
+ cls.move_line1.qty_done = cls.move_line1.reserved_uom_qty
139
+ cls.move_line2.qty_done = cls.move_line2.reserved_uom_qty
140
+ cls.move_line3.qty_done = 0
141
+
142
+ cls.picking = picking
143
+
144
+ def _get_allowed_packages(self, picking):
145
+ return (
146
+ picking.mapped("move_line_ids.package_id")
147
+ | picking.mapped("move_line_ids.result_package_id")
148
+ ).filtered("package_type_id")
149
+
150
+ def _assert_package_set(self, response):
151
+ self.assertRecordValues(
152
+ self.move_line1 + self.move_line2 + self.move_line3,
153
+ [
154
+ {
155
+ "result_package_id": self.delivery_package.id,
156
+ "shopfloor_checkout_done": True,
157
+ },
158
+ {
159
+ "result_package_id": self.delivery_package.id,
160
+ "shopfloor_checkout_done": True,
161
+ },
162
+ # qty_done was zero so we don't set it as packed
163
+ {"result_package_id": self.pack1.id, "shopfloor_checkout_done": False},
164
+ ],
165
+ )
166
+ self.assert_response(
167
+ response,
168
+ # go pack to the screen to select lines to put in packages
169
+ next_state="select_line",
170
+ data=self._data_for_select_line(self.picking),
171
+ message=self.msg_store.goods_packed_in(self.delivery_package),
172
+ )
173
+
174
+ def test_scan_dest_package_ok(self):
175
+ response = self.service.dispatch(
176
+ "scan_dest_package",
177
+ params={
178
+ "picking_id": self.picking.id,
179
+ "selected_line_ids": self.selected_lines.ids,
180
+ # we keep the goods in the same package, so we scan the source package
181
+ "barcode": self.delivery_package.name,
182
+ },
183
+ )
184
+ self._assert_package_set(response)
185
+
186
+ def test_scan_dest_package_error_not_found(self):
187
+ barcode = "NO BARCODE"
188
+ response = self.service.dispatch(
189
+ "scan_dest_package",
190
+ params={
191
+ "picking_id": self.picking.id,
192
+ "selected_line_ids": self.selected_lines.ids,
193
+ "barcode": barcode,
194
+ },
195
+ )
196
+ self._assert_response_select_dest_package(
197
+ response,
198
+ self.picking,
199
+ self.selected_lines,
200
+ self._get_allowed_packages(self.picking),
201
+ message=self.service.msg_store.package_not_found_for_barcode(barcode),
202
+ )
203
+
204
+ def test_scan_dest_package_error_not_allowed(self):
205
+ package = self.env["stock.quant.package"].create({})
206
+ response = self.service.dispatch(
207
+ "scan_dest_package",
208
+ params={
209
+ "picking_id": self.picking.id,
210
+ "selected_line_ids": self.selected_lines.ids,
211
+ "barcode": package.name,
212
+ },
213
+ )
214
+ self._assert_response_select_dest_package(
215
+ response,
216
+ self.picking,
217
+ self.selected_lines,
218
+ self._get_allowed_packages(self.picking),
219
+ message=self.service.msg_store.dest_package_not_valid(package),
220
+ )
221
+
222
+ def test_set_dest_package_ok(self):
223
+ response = self.service.dispatch(
224
+ "set_dest_package",
225
+ params={
226
+ "picking_id": self.picking.id,
227
+ "selected_line_ids": self.selected_lines.ids,
228
+ "package_id": self.delivery_package.id,
229
+ },
230
+ )
231
+ self._assert_package_set(response)
232
+
233
+ def test_set_dest_package_ok_on_partial_qty_done(self):
234
+ # Partially process line three 3 quantiy out of 10
235
+ self.move_line3.qty_done = 3
236
+ response = self.service.dispatch(
237
+ "set_dest_package",
238
+ params={
239
+ "picking_id": self.picking.id,
240
+ "selected_line_ids": self.selected_lines.ids,
241
+ "package_id": self.delivery_package.id,
242
+ },
243
+ )
244
+ # self._assert_package_set(response)
245
+ self.assertRecordValues(
246
+ self.move_line1 + self.move_line2 + self.move_line3,
247
+ [
248
+ {
249
+ "result_package_id": self.delivery_package.id,
250
+ "shopfloor_checkout_done": True,
251
+ },
252
+ {
253
+ "result_package_id": self.delivery_package.id,
254
+ "shopfloor_checkout_done": True,
255
+ },
256
+ # Line 3 has been split
257
+ {
258
+ "result_package_id": self.delivery_package.id,
259
+ "shopfloor_checkout_done": True,
260
+ "product_uom_qty": 3,
261
+ "qty_done": 3,
262
+ },
263
+ ],
264
+ )
265
+ # Left quantity to do from line 3
266
+ new_move_line = self.picking.move_line_ids.filtered(
267
+ lambda line: line.qty_done == 0 and line.reserved_uom_qty == 7
268
+ )
269
+ self.assertTrue(new_move_line)
270
+ self.assertFalse(new_move_line.shopfloor_checkout_done)
271
+ self.assert_response(
272
+ response,
273
+ # go pack to the screen to select lines to put in packages
274
+ next_state="select_line",
275
+ data=self._data_for_select_line(self.picking),
276
+ message=self.msg_store.goods_packed_in(self.delivery_package),
277
+ )
278
+
279
+ def test_set_dest_package_error_not_found(self):
280
+ response = self.service.dispatch(
281
+ "set_dest_package",
282
+ params={
283
+ "picking_id": self.picking.id,
284
+ "selected_line_ids": self.selected_lines.ids,
285
+ "package_id": 0,
286
+ },
287
+ )
288
+ self._assert_response_select_dest_package(
289
+ response,
290
+ self.picking,
291
+ self.selected_lines,
292
+ self._get_allowed_packages(self.picking),
293
+ message=self.service.msg_store.record_not_found(),
294
+ )
295
+
296
+ def test_set_dest_package_error_not_allowed(self):
297
+ package = self.env["stock.quant.package"].create({})
298
+ response = self.service.dispatch(
299
+ "set_dest_package",
300
+ params={
301
+ "picking_id": self.picking.id,
302
+ "selected_line_ids": self.selected_lines.ids,
303
+ "package_id": package.id,
304
+ },
305
+ )
306
+ self._assert_response_select_dest_package(
307
+ response,
308
+ self.picking,
309
+ self.selected_lines,
310
+ self._get_allowed_packages(self.picking),
311
+ message=self.service.msg_store.dest_package_not_valid(package),
312
+ )
313
+
314
+ def test_set_dest_package_error_qty_done_above(self):
315
+ # If the qty_done of a selected line goes beyond
316
+ # the maximum allowed, a message should be displayed
317
+ # and the user shouldn't be allowed to select a package.
318
+ line = fields.first(self.picking.move_line_ids)
319
+ line.qty_done = line.reserved_uom_qty + 1
320
+ response = self.service.dispatch(
321
+ "list_dest_package",
322
+ params={
323
+ "picking_id": self.picking.id,
324
+ "selected_line_ids": self.picking.move_line_ids.ids,
325
+ },
326
+ )
327
+ self._assert_select_package_qty_above(response, self.picking)
@@ -0,0 +1,88 @@
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 odoo import fields
4
+
5
+ from .test_checkout_base import CheckoutCommonCase
6
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
7
+
8
+
9
+ class CheckoutNewPackageCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
10
+ def test_new_package_ok(self):
11
+ picking = self._create_picking(
12
+ lines=[
13
+ (self.product_a, 10),
14
+ (self.product_b, 10),
15
+ (self.product_c, 10),
16
+ (self.product_d, 10),
17
+ ]
18
+ )
19
+ pack1_moves = picking.move_ids[:3]
20
+ pack2_moves = picking.move_ids[3:]
21
+ # put in 2 packs, for this test, we'll work on pack1
22
+ self._fill_stock_for_moves(pack1_moves, in_package=True)
23
+ self._fill_stock_for_moves(pack2_moves, in_package=True)
24
+ picking.action_assign()
25
+
26
+ selected_lines = pack1_moves.move_line_ids
27
+ pack1 = pack1_moves.move_line_ids.package_id
28
+
29
+ move_line1, move_line2, move_line3 = selected_lines
30
+ # we'll put only the first 2 lines (product A and B) in the new package
31
+ move_line1.qty_done = move_line1.reserved_uom_qty
32
+ move_line2.qty_done = move_line2.reserved_uom_qty
33
+ move_line3.qty_done = 0
34
+
35
+ response = self.service.dispatch(
36
+ "new_package",
37
+ params={"picking_id": picking.id, "selected_line_ids": selected_lines.ids},
38
+ )
39
+
40
+ new_package = move_line1.result_package_id
41
+ self.assertNotEqual(pack1, new_package)
42
+
43
+ self.assertRecordValues(
44
+ move_line1,
45
+ [{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
46
+ )
47
+ self.assertRecordValues(
48
+ move_line2,
49
+ [{"result_package_id": new_package.id, "shopfloor_checkout_done": True}],
50
+ )
51
+ self.assertRecordValues(
52
+ move_line3,
53
+ # qty_done was zero so we don't set it as packed and it remains in
54
+ # the same package
55
+ [{"result_package_id": pack1.id, "shopfloor_checkout_done": False}],
56
+ )
57
+ self.assert_response(
58
+ response,
59
+ # go pack to the screen to select lines to put in packages
60
+ next_state="select_line",
61
+ data=self._data_for_select_line(picking),
62
+ message=self.msg_store.goods_packed_in(new_package),
63
+ )
64
+
65
+ def test_set_dest_package_error_qty_done_above(self):
66
+ picking = self._create_picking(
67
+ lines=[
68
+ (self.product_a, 10),
69
+ (self.product_b, 10),
70
+ ]
71
+ )
72
+ moves = picking.move_ids
73
+ self._fill_stock_for_moves(moves, in_package=True)
74
+ picking.action_assign()
75
+ # If the qty_done of a selected line goes beyond
76
+ # the maximum allowed, a message should be displayed
77
+ # and the user shouldn't be allowed to select a package.
78
+ selected_lines = moves.move_line_ids
79
+ line = fields.first(selected_lines)
80
+ line.qty_done = line.reserved_uom_qty + 1
81
+ response = self.service.dispatch(
82
+ "list_dest_package",
83
+ params={
84
+ "picking_id": picking.id,
85
+ "selected_line_ids": picking.move_line_ids.ids,
86
+ },
87
+ )
88
+ self._assert_select_package_qty_above(response, picking)
@@ -0,0 +1,95 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ import werkzeug
4
+
5
+ from odoo import fields
6
+
7
+ from .test_checkout_base import CheckoutCommonCase
8
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
9
+
10
+
11
+ # pylint: disable=missing-return
12
+ class CheckoutNoPackageCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
13
+ @classmethod
14
+ def setUpClassBaseData(cls):
15
+ super().setUpClassBaseData()
16
+ cls.picking = picking = cls._create_picking(
17
+ lines=[
18
+ (cls.product_a, 10),
19
+ (cls.product_b, 10),
20
+ (cls.product_c, 10),
21
+ (cls.product_d, 10),
22
+ ]
23
+ )
24
+ cls.pack1_moves = pack1_moves = picking.move_ids[:3]
25
+ cls.pack2_moves = pack2_moves = picking.move_ids[3:]
26
+ # put in 2 packs, for this test, we'll work on pack1
27
+ cls._fill_stock_for_moves(pack1_moves)
28
+ cls._fill_stock_for_moves(pack2_moves)
29
+ picking.action_assign()
30
+
31
+ def test_no_package_ok(self):
32
+ move_line1, move_line2, move_line3 = self.pack1_moves.move_line_ids
33
+ selected_lines = move_line1 + move_line2
34
+
35
+ # we'll put only the first 2 lines (product A and B) w/ no package
36
+ move_line1.qty_done = move_line1.reserved_uom_qty
37
+ move_line2.qty_done = move_line2.reserved_uom_qty
38
+ move_line3.qty_done = 0
39
+ response = self.service.dispatch(
40
+ "no_package",
41
+ params={
42
+ "picking_id": self.picking.id,
43
+ "selected_line_ids": selected_lines.ids,
44
+ },
45
+ )
46
+
47
+ self.assertRecordValues(
48
+ move_line1,
49
+ [{"result_package_id": False, "shopfloor_checkout_done": True}],
50
+ )
51
+ self.assertRecordValues(
52
+ move_line2,
53
+ [{"result_package_id": False, "shopfloor_checkout_done": True}],
54
+ )
55
+ self.assertRecordValues(
56
+ move_line3,
57
+ [{"result_package_id": False, "shopfloor_checkout_done": False}],
58
+ )
59
+ self.assert_response(
60
+ response,
61
+ # go pack to the screen to select lines to put in packages
62
+ next_state="select_line",
63
+ data=self._data_for_select_line(self.picking),
64
+ message={
65
+ "message_type": "success",
66
+ "body": "Product(s) processed as raw product(s)",
67
+ },
68
+ )
69
+
70
+ def test_no_package_disabled(self):
71
+ self.service.work.options = {"checkout__disable_no_package": True}
72
+ with self.assertRaises(werkzeug.exceptions.BadRequest) as err:
73
+ self.service.dispatch(
74
+ "no_package",
75
+ params={
76
+ "picking_id": self.picking.id,
77
+ "selected_line_ids": self.pack1_moves.move_line_ids.ids,
78
+ },
79
+ )
80
+ self.assertEqual(repr(err), "`checkout.no_package` endpoint is not enabled")
81
+
82
+ def test_set_dest_package_error_qty_done_above(self):
83
+ # If the qty_done of a selected line goes beyond
84
+ # the maximum allowed, a message should be displayed
85
+ # and the user shouldn't be allowed to select a package.
86
+ line = fields.first(self.picking.move_line_ids)
87
+ line.qty_done = line.reserved_uom_qty + 1
88
+ response = self.service.dispatch(
89
+ "list_dest_package",
90
+ params={
91
+ "picking_id": self.picking.id,
92
+ "selected_line_ids": self.picking.move_line_ids.ids,
93
+ },
94
+ )
95
+ self._assert_select_package_qty_above(response, self.picking)
@@ -0,0 +1,174 @@
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
+
6
+ class CheckoutScanCase(CheckoutCommonCase):
7
+ def _test_scan_ok(self, barcode_func, in_package=True):
8
+ picking = self._create_picking()
9
+ self._fill_stock_for_moves(picking.move_ids, in_package=in_package)
10
+ picking.action_assign()
11
+ barcode = barcode_func(picking)
12
+ response = self.service.dispatch("scan_document", params={"barcode": barcode})
13
+ self.assert_response(
14
+ response,
15
+ next_state="select_line",
16
+ data=self._data_for_select_line(picking),
17
+ )
18
+
19
+ def test_scan_document_stock_picking_ok(self):
20
+ self._test_scan_ok(lambda picking: picking.name)
21
+
22
+ def test_scan_document_location_ok(self):
23
+ self._test_scan_ok(lambda picking: picking.move_line_ids.location_id.barcode)
24
+
25
+ def test_scan_document_package_ok(self):
26
+ self._test_scan_ok(lambda picking: picking.move_line_ids.package_id.name)
27
+
28
+ def test_scan_document_product_ok(self):
29
+ self._test_scan_ok(
30
+ lambda picking: picking.move_line_ids.product_id[0].barcode,
31
+ in_package=False,
32
+ )
33
+
34
+ def test_scan_document_packaging_ok(self):
35
+ self._test_scan_ok(
36
+ lambda picking: picking.move_line_ids.product_id[0].packaging_ids.barcode,
37
+ in_package=False,
38
+ )
39
+
40
+ def test_scan_document_error_not_found(self):
41
+ response = self.service.dispatch("scan_document", params={"barcode": "NOPE"})
42
+ self.assert_response(
43
+ response,
44
+ next_state="select_document",
45
+ message={"message_type": "error", "body": "Barcode not found"},
46
+ )
47
+
48
+ def _test_scan_document_error_not_available(self, barcode_func):
49
+ picking = self._create_picking()
50
+ # in this test, we want the picking not to be available, but
51
+ # if we leave the shipping policy to direct, a single move assigned
52
+ # would make the picking available
53
+ picking.move_type = "one"
54
+ # the picking will have one line available, so the endpoint can find
55
+ # something from a location or package but should reject the picking as
56
+ # it is not entirely available
57
+ self._fill_stock_for_moves(picking.move_ids[0], in_package=True)
58
+ picking.action_assign()
59
+ barcode = barcode_func(picking)
60
+ response = self.service.dispatch("scan_document", params={"barcode": barcode})
61
+ self.assert_response(
62
+ response,
63
+ next_state="select_document",
64
+ message={
65
+ "message_type": "error",
66
+ "body": "Transfer {} is not available.".format(picking.name),
67
+ },
68
+ )
69
+
70
+ def test_scan_document_error_not_available_picking(self):
71
+ self._test_scan_document_error_not_available(lambda picking: picking.name)
72
+
73
+ def test_scan_document_error_not_available_location(self):
74
+ self._test_scan_document_error_not_available(
75
+ lambda picking: picking.move_line_ids.location_id.barcode
76
+ )
77
+
78
+ def test_scan_document_error_not_available_package(self):
79
+ self._test_scan_document_error_not_available(
80
+ lambda picking: picking.move_line_ids.package_id.name
81
+ )
82
+
83
+ def test_scan_document_error_location_not_child_of_type(self):
84
+ picking = self._create_picking()
85
+ picking.location_id = self.dispatch_location
86
+ self._fill_stock_for_moves(picking.move_ids, in_package=True)
87
+ picking.action_assign()
88
+ response = self.service.dispatch(
89
+ "scan_document", params={"barcode": picking.location_id.barcode}
90
+ )
91
+ self.assert_response(
92
+ response,
93
+ next_state="select_document",
94
+ message={"message_type": "error", "body": "Location not allowed here."},
95
+ )
96
+
97
+ def _test_scan_document_error_different_picking_type(self, barcode_func):
98
+ picking = self._create_picking(picking_type=self.wh.pick_type_id)
99
+ self._fill_stock_for_moves(picking.move_ids, in_package=True)
100
+ picking.action_assign()
101
+ barcode = barcode_func(picking)
102
+ response = self.service.dispatch("scan_document", params={"barcode": barcode})
103
+ self.assert_response(
104
+ response,
105
+ next_state="select_document",
106
+ message={
107
+ "message_type": "error",
108
+ "body": "You cannot move this using this menu.",
109
+ },
110
+ )
111
+
112
+ def test_scan_document_error_different_picking_type_picking(self):
113
+ self._test_scan_document_error_different_picking_type(
114
+ lambda picking: picking.name
115
+ )
116
+
117
+ def test_scan_document_error_different_picking_type_package(self):
118
+ self._test_scan_document_error_different_picking_type(
119
+ lambda picking: picking.move_line_ids.package_id.name
120
+ )
121
+
122
+ def test_scan_document_error_location_several_pickings(self):
123
+ picking = self._create_picking()
124
+ # create a second picking at the same place so we don't
125
+ # know which picking to use
126
+ picking2 = self._create_picking()
127
+ pickings = picking | picking2
128
+ self._fill_stock_for_moves(pickings.move_ids, in_package=True)
129
+ pickings.action_assign()
130
+ response = self.service.dispatch(
131
+ "scan_document",
132
+ params={"barcode": picking.move_line_ids.location_id.barcode},
133
+ )
134
+ self.assert_response(
135
+ response,
136
+ next_state="select_document",
137
+ message={
138
+ "message_type": "error",
139
+ "body": "Several transfers found, please scan a package"
140
+ " or select a transfer manually.",
141
+ },
142
+ )
143
+
144
+ def test_scan_document_recover(self):
145
+ """If the user starts to process a line, and for whatever reason he
146
+ stops there and restarts the scenario from the beginning, he should
147
+ still be able to find the previous line.
148
+ """
149
+ picking = self._create_picking()
150
+ self._fill_stock_for_moves(picking.move_ids, in_package=True)
151
+ picking.action_assign()
152
+ package = picking.move_line_ids.package_id
153
+ # The user selects a line, then stops working in the middle of the process
154
+ response = self.service.dispatch(
155
+ "scan_document", params={"barcode": package.name}
156
+ )
157
+ data = response["data"]["select_line"]
158
+ self.assertEqual(data["picking"]["move_line_count"], 2)
159
+ self.assertEqual(len(data["picking"]["move_lines"]), 2)
160
+ self.assertFalse(picking.move_line_ids.shopfloor_user_id)
161
+ response = self.service.dispatch(
162
+ "select_line",
163
+ params={"picking_id": picking.id, "package_id": package.id},
164
+ )
165
+ self.assertTrue(all(m.qty_done for m in picking.move_line_ids))
166
+ self.assertEqual(picking.move_line_ids.shopfloor_user_id, self.env.user)
167
+ # He restarts the scenario and try to select again the previous line
168
+ # to continue its job
169
+ response = self.service.dispatch(
170
+ "scan_document", params={"barcode": package.name}
171
+ )
172
+ data = response["data"]["select_line"]
173
+ self.assertEqual(data["picking"]["move_line_count"], 2)
174
+ self.assertEqual(len(data["picking"]["move_lines"]), 2) # Lines found