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,130 @@
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
+ from .test_checkout_select_package_base import CheckoutSelectPackageMixin
5
+
6
+
7
+ # pylint: disable=missing-return
8
+ class CheckoutSelectLineCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
9
+ @classmethod
10
+ def setUpClassBaseData(cls):
11
+ super().setUpClassBaseData()
12
+ picking = cls._create_picking(
13
+ lines=[(cls.product_a, 10), (cls.product_b, 10), (cls.product_c, 10)]
14
+ )
15
+ cls.moves_pack = picking.move_ids[:2]
16
+ cls.move_single = picking.move_ids[2:]
17
+ cls._fill_stock_for_moves(cls.moves_pack, in_package=True)
18
+ cls._fill_stock_for_moves(cls.move_single)
19
+ picking.action_assign()
20
+ cls.picking = picking
21
+
22
+ def test_select_line_package_ok(self):
23
+ selected_lines = self.moves_pack.move_line_ids
24
+ # we want to check that when we give the package id, we get
25
+ # all its move lines
26
+ response = self.service.dispatch(
27
+ "select_line",
28
+ params={
29
+ "picking_id": self.picking.id,
30
+ "package_id": selected_lines.package_id.id,
31
+ },
32
+ )
33
+ self._assert_selected(response, selected_lines)
34
+
35
+ def test_select_line_no_package_disabled(self):
36
+ selected_lines = self.moves_pack.move_line_ids
37
+ self.service.work.options = {"checkout__disable_no_package": True}
38
+ response = self.service.dispatch(
39
+ "select_line",
40
+ params={
41
+ "picking_id": self.picking.id,
42
+ "package_id": selected_lines.package_id.id,
43
+ },
44
+ )
45
+ self._assert_selected(response, selected_lines, no_package_enabled=False)
46
+
47
+ def test_select_line_move_line_package_ok(self):
48
+ selected_lines = self.moves_pack.move_line_ids
49
+ # When we select a single line but the line is part of a package,
50
+ # we select all the lines. Note: not really supposed to happen as
51
+ # the client application should use send a package id when there is
52
+ # a package and use the move line id only for lines without package
53
+ response = self.service.dispatch(
54
+ "select_line",
55
+ params={
56
+ "picking_id": self.picking.id,
57
+ "move_line_id": selected_lines[0].id,
58
+ },
59
+ )
60
+ self._assert_selected(response, selected_lines)
61
+
62
+ def test_select_line_move_line_ok(self):
63
+ selected_lines = self.move_single.move_line_ids
64
+ response = self.service.dispatch(
65
+ "select_line",
66
+ params={
67
+ "picking_id": self.picking.id,
68
+ "move_line_id": selected_lines[0].id,
69
+ },
70
+ )
71
+ self._assert_selected(response, selected_lines)
72
+
73
+ def _test_select_line_error(self, params, message):
74
+ """Test errors for /select_line
75
+
76
+ :param params: params sent to /select_line
77
+ :param message: the dict of expected error message
78
+ """
79
+ response = self.service.dispatch("select_line", params=params)
80
+ self.assert_response(
81
+ response,
82
+ next_state="select_line",
83
+ data=self._data_for_select_line(self.picking),
84
+ message=message,
85
+ )
86
+
87
+ def test_select_line_package_error_not_found(self):
88
+ selected_lines = self.move_single.move_line_ids
89
+ selected_lines.unlink()
90
+ self._test_select_line_error(
91
+ {"picking_id": self.picking.id, "package_id": selected_lines[0].id},
92
+ {
93
+ "message_type": "error",
94
+ "body": "The record you were working on does not exist anymore.",
95
+ },
96
+ )
97
+
98
+ def test_select_line_move_line_error_not_found(self):
99
+ selected_lines = self.move_single.move_line_ids
100
+ selected_lines.unlink()
101
+ self._test_select_line_error(
102
+ {"picking_id": self.picking.id, "move_line_id": selected_lines[0].id},
103
+ {
104
+ "message_type": "error",
105
+ "body": "The record you were working on does not exist anymore.",
106
+ },
107
+ )
108
+
109
+ def test_select_line_all_lines_done(self):
110
+ # set all lines as done
111
+ self.picking.move_line_ids.write(
112
+ {"qty_done": 10.0, "shopfloor_checkout_done": True}
113
+ )
114
+ response = self.service.dispatch(
115
+ "select_line",
116
+ params={
117
+ "picking_id": self.picking.id,
118
+ # doesn't matter as all lines are done, we should be
119
+ # redirected to the summary
120
+ "package_id": self.picking.move_line_ids[0].package_id.id,
121
+ },
122
+ )
123
+ self.assert_response(
124
+ response,
125
+ next_state="summary",
126
+ data={
127
+ "picking": self._stock_picking_data(self.picking, done=True),
128
+ "all_processed": True,
129
+ },
130
+ )
@@ -0,0 +1,64 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+
5
+ class CheckoutSelectPackageMixin:
6
+ def _assert_selected_response(
7
+ self,
8
+ response,
9
+ selected_lines,
10
+ message=None,
11
+ packing_info="",
12
+ no_package_enabled=True,
13
+ ):
14
+ picking = selected_lines.mapped("picking_id")
15
+ self.assert_response(
16
+ response,
17
+ next_state="select_package",
18
+ data={
19
+ "selected_move_lines": [
20
+ self._move_line_data(ml) for ml in selected_lines.sorted()
21
+ ],
22
+ "picking": self._picking_summary_data(picking),
23
+ "packing_info": packing_info,
24
+ "no_package_enabled": no_package_enabled,
25
+ },
26
+ message=message,
27
+ )
28
+
29
+ def _assert_selected_qties(
30
+ self,
31
+ response,
32
+ selected_lines,
33
+ lines_quantities,
34
+ message=None,
35
+ packing_info="",
36
+ ):
37
+ picking = selected_lines.mapped("picking_id")
38
+ deselected_lines = picking.move_line_ids - selected_lines
39
+ self.assertEqual(
40
+ sorted(selected_lines.ids), sorted([line.id for line in lines_quantities])
41
+ )
42
+ for line, quantity in lines_quantities.items():
43
+ self.assertEqual(line.qty_done, quantity)
44
+ for line in deselected_lines:
45
+ self.assertEqual(line.qty_done, 0, "Lines deselected must have no qty done")
46
+ self._assert_selected_response(
47
+ response, selected_lines, message=message, packing_info=packing_info
48
+ )
49
+
50
+ def _assert_selected(
51
+ self, response, selected_lines, related_lines=None, message=None, **kw
52
+ ):
53
+ related_lines = related_lines or self.env["stock.move.line"]
54
+ picking = selected_lines.mapped("picking_id")
55
+ unselected_lines = picking.move_line_ids - selected_lines
56
+ for line in selected_lines - related_lines:
57
+ self.assertEqual(
58
+ line.qty_done,
59
+ line.reserved_uom_qty,
60
+ "Scanned lines must have their qty done set to the reserved quantity",
61
+ )
62
+ for line in unselected_lines + related_lines:
63
+ self.assertEqual(line.qty_done, 0)
64
+ self._assert_selected_response(response, selected_lines, message=message, **kw)
@@ -0,0 +1,257 @@
1
+ # Copyright 2020 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 CheckoutSetQtyCommonCase(CheckoutCommonCase, CheckoutSelectPackageMixin):
11
+ @classmethod
12
+ def setUpClassBaseData(cls):
13
+ super().setUpClassBaseData()
14
+ picking = cls._create_picking(
15
+ lines=[(cls.product_a, 10), (cls.product_b, 10), (cls.product_c, 10)]
16
+ )
17
+ cls.moves_pack1 = picking.move_ids[:2]
18
+ cls.moves_pack2 = picking.move_ids[2:]
19
+ cls._fill_stock_for_moves(cls.moves_pack1, in_package=True)
20
+ cls._fill_stock_for_moves(cls.moves_pack2, in_package=True)
21
+ picking.action_assign()
22
+ cls.picking = picking
23
+
24
+ def setUp(self):
25
+ super().setUp()
26
+ # we assume we have called /select_line on pack one, so by default, we
27
+ # expect the lines for product a and b to have their qty_done set to
28
+ # their reserved_uom_qty at the start of the tests
29
+ self.selected_lines = self.moves_pack1.move_line_ids
30
+ self.deselected_lines = self.moves_pack2.move_line_ids
31
+ self.service._select_lines(self.selected_lines)
32
+ self.assertTrue(
33
+ all(line.qty_done == line.reserved_uom_qty for line in self.selected_lines)
34
+ )
35
+ self.assertTrue(all(line.qty_done == 0 for line in self.deselected_lines))
36
+
37
+
38
+ class CheckoutResetLineQtyCase(CheckoutSetQtyCommonCase):
39
+ def test_reset_line_qty_ok(self):
40
+ selected_lines = self.moves_pack1.move_line_ids
41
+ line_to_reset = selected_lines[0]
42
+ line_with_qty = selected_lines[1]
43
+ # we want to check that when we give the package id, we get
44
+ # all its move lines
45
+ response = self.service.dispatch(
46
+ "reset_line_qty",
47
+ params={
48
+ "picking_id": self.picking.id,
49
+ "selected_line_ids": selected_lines.ids,
50
+ "move_line_id": line_to_reset.id,
51
+ },
52
+ )
53
+ self._assert_selected_qties(
54
+ response,
55
+ selected_lines,
56
+ {line_to_reset: 0, line_with_qty: line_with_qty.reserved_uom_qty},
57
+ )
58
+
59
+ def test_reset_line_qty_not_found(self):
60
+ selected_lines = self.moves_pack1.move_line_ids
61
+ response = self.service.dispatch(
62
+ "reset_line_qty",
63
+ params={
64
+ "picking_id": self.picking.id,
65
+ "selected_line_ids": selected_lines.ids,
66
+ "move_line_id": 0,
67
+ },
68
+ )
69
+ # if the move line is not found, ignore and return to the
70
+ # screen
71
+ self._assert_selected_qties(
72
+ response,
73
+ selected_lines,
74
+ {line: line.reserved_uom_qty for line in selected_lines},
75
+ message={
76
+ "body": "The record you were working on does not exist anymore.",
77
+ "message_type": "error",
78
+ },
79
+ )
80
+
81
+
82
+ class CheckoutSetLineQtyCase(CheckoutSetQtyCommonCase):
83
+ def test_set_line_qty_ok(self):
84
+ selected_lines = self.moves_pack1.move_line_ids
85
+ # do as if the user removed the qties of the 2 selected lines
86
+ selected_lines.qty_done = 0
87
+ line_to_set = selected_lines[0]
88
+ line_no_qty = selected_lines[1]
89
+ # we want to check that when we give the package id, we get
90
+ # all its move lines
91
+ response = self.service.dispatch(
92
+ "set_line_qty",
93
+ params={
94
+ "picking_id": self.picking.id,
95
+ "selected_line_ids": selected_lines.ids,
96
+ "move_line_id": line_to_set.id,
97
+ },
98
+ )
99
+ self.assertEqual(line_to_set.qty_done, line_to_set.reserved_uom_qty)
100
+ self.assertEqual(line_no_qty.qty_done, 0)
101
+ self._assert_selected_qties(
102
+ response,
103
+ selected_lines,
104
+ {line_to_set: line_to_set.reserved_uom_qty, line_no_qty: 0},
105
+ )
106
+
107
+ def test_set_line_qty_not_found(self):
108
+ selected_lines = self.moves_pack1.move_line_ids
109
+ response = self.service.dispatch(
110
+ "set_line_qty",
111
+ params={
112
+ "picking_id": self.picking.id,
113
+ "selected_line_ids": selected_lines.ids,
114
+ "move_line_id": 0,
115
+ },
116
+ )
117
+ # if the move line is not found, ignore and return to the
118
+ # screen
119
+ self._assert_selected_qties(
120
+ response,
121
+ selected_lines,
122
+ {line: line.reserved_uom_qty for line in selected_lines},
123
+ message={
124
+ "body": "The record you were working on does not exist anymore.",
125
+ "message_type": "error",
126
+ },
127
+ )
128
+
129
+
130
+ class CheckoutSetCustomQtyCase(CheckoutSetQtyCommonCase):
131
+ def test_set_custom_qty_ok(self):
132
+ selected_lines = self.moves_pack1.move_line_ids
133
+ line_to_change = selected_lines[0]
134
+ line_keep_qty = selected_lines[1]
135
+ # Process full qty
136
+ new_qty = line_to_change.reserved_uom_qty
137
+ # we want to check that when we give the package id, we get
138
+ # all its move lines
139
+ response = self.service.dispatch(
140
+ "set_custom_qty",
141
+ params={
142
+ "picking_id": self.picking.id,
143
+ "selected_line_ids": selected_lines.ids,
144
+ "move_line_id": line_to_change.id,
145
+ "qty_done": new_qty,
146
+ },
147
+ )
148
+ self.assertEqual(line_to_change.qty_done, new_qty)
149
+ self.assertEqual(line_keep_qty.qty_done, line_keep_qty.reserved_uom_qty)
150
+ self._assert_selected_qties(
151
+ response,
152
+ selected_lines,
153
+ {line_to_change: new_qty, line_keep_qty: line_keep_qty.reserved_uom_qty},
154
+ )
155
+
156
+ def test_set_custom_qty_not_found(self):
157
+ selected_lines = self.moves_pack1.move_line_ids
158
+ response = self.service.dispatch(
159
+ "set_custom_qty",
160
+ params={
161
+ "picking_id": self.picking.id,
162
+ "selected_line_ids": selected_lines.ids,
163
+ "move_line_id": 0,
164
+ "qty_done": 3,
165
+ },
166
+ )
167
+ # if the move line is not found, ignore and return to the
168
+ # screen
169
+ self._assert_selected_qties(
170
+ response,
171
+ selected_lines,
172
+ {line: line.reserved_uom_qty for line in selected_lines},
173
+ message={
174
+ "body": "The record you were working on does not exist anymore.",
175
+ "message_type": "error",
176
+ },
177
+ )
178
+
179
+ def test_set_custom_qty_above(self):
180
+ selected_lines = self.moves_pack1.move_line_ids
181
+ line1 = selected_lines[0]
182
+ # modify so we can check that a too high quantity set the max
183
+ line1.qty_done = 1
184
+ line2 = selected_lines[1]
185
+ response = self.service.dispatch(
186
+ "set_custom_qty",
187
+ params={
188
+ "picking_id": self.picking.id,
189
+ "selected_line_ids": selected_lines.ids,
190
+ "move_line_id": line1.id,
191
+ "qty_done": line1.reserved_uom_qty + 1,
192
+ },
193
+ )
194
+ self._assert_selected_qties(
195
+ response,
196
+ selected_lines,
197
+ {line1: line1.reserved_uom_qty + 1, line2: line2.reserved_uom_qty},
198
+ message={
199
+ "body": "Please note that the scanned quantity "
200
+ "is higher than the maximum allowed.",
201
+ "message_type": "warning",
202
+ },
203
+ )
204
+
205
+ def test_set_custom_qty_negative(self):
206
+ selected_lines = self.moves_pack1.move_line_ids
207
+ line1 = selected_lines[0]
208
+ line2 = selected_lines[1]
209
+ response = self.service.dispatch(
210
+ "set_custom_qty",
211
+ params={
212
+ "picking_id": self.picking.id,
213
+ "selected_line_ids": selected_lines.ids,
214
+ "move_line_id": line1.id,
215
+ "qty_done": -1,
216
+ },
217
+ )
218
+ self._assert_selected_qties(
219
+ response,
220
+ selected_lines,
221
+ {line1: line1.reserved_uom_qty, line2: line2.reserved_uom_qty},
222
+ message={
223
+ "body": "Negative quantity not allowed.",
224
+ "message_type": "error",
225
+ },
226
+ )
227
+
228
+ def test_set_custom_qty_partial(self):
229
+ selected_lines = self.moves_pack1.move_line_ids
230
+ line_to_change = selected_lines[0]
231
+ line_keep_qty = selected_lines[1]
232
+ # split 1 qty
233
+ new_qty = line_to_change.reserved_uom_qty - 1
234
+ response = self.service.dispatch(
235
+ "set_custom_qty",
236
+ params={
237
+ "picking_id": self.picking.id,
238
+ "selected_line_ids": selected_lines.ids,
239
+ "move_line_id": line_to_change.id,
240
+ "qty_done": new_qty,
241
+ },
242
+ )
243
+ self.assertEqual(line_to_change.qty_done, new_qty)
244
+ self.assertEqual(line_keep_qty.qty_done, line_keep_qty.reserved_uom_qty)
245
+ new_lines = [
246
+ x for x in self.moves_pack1.move_line_ids if x not in selected_lines
247
+ ]
248
+ # Lines are not being split anymore
249
+ self.assertFalse(new_lines)
250
+ self._assert_selected_qties(
251
+ response,
252
+ self.moves_pack1.move_line_ids,
253
+ {
254
+ line_to_change: new_qty,
255
+ line_keep_qty: line_keep_qty.reserved_uom_qty,
256
+ },
257
+ )
@@ -0,0 +1,69 @@
1
+ # Copyright 2020 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
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class CheckoutSummaryCase(CheckoutCommonCase):
10
+ @classmethod
11
+ def setUpClassBaseData(cls):
12
+ super().setUpClassBaseData()
13
+ cls.picking = cls._create_picking(
14
+ lines=[
15
+ (cls.product_a, 10),
16
+ (cls.product_b, 10),
17
+ (cls.product_c, 10),
18
+ (cls.product_d, 10),
19
+ ]
20
+ )
21
+
22
+ def test_summary_picking_not_ready(self):
23
+ response = self.service.dispatch(
24
+ "summary", params={"picking_id": self.picking.id}
25
+ )
26
+ self.assert_response(
27
+ response,
28
+ next_state="select_document",
29
+ data={},
30
+ message=self.service.msg_store.stock_picking_not_available(self.picking),
31
+ )
32
+
33
+ def test_summary_not_fully_processed(self):
34
+ self._fill_stock_for_moves(self.picking.move_ids, in_package=True)
35
+ self.picking.action_assign()
36
+ # satisfy only few lines
37
+ for ml in self.picking.move_line_ids[:2]:
38
+ ml.qty_done = ml.reserved_uom_qty
39
+ ml.shopfloor_checkout_done = True
40
+ response = self.service.dispatch(
41
+ "summary", params={"picking_id": self.picking.id}
42
+ )
43
+ self.assert_response(
44
+ response,
45
+ next_state="summary",
46
+ data={
47
+ "picking": self._stock_picking_data(self.picking, done=True),
48
+ "all_processed": False,
49
+ },
50
+ )
51
+
52
+ def test_summary_fully_processed(self):
53
+ self._fill_stock_for_moves(self.picking.move_ids, in_package=True)
54
+ self.picking.action_assign()
55
+ # satisfy only all lines
56
+ for ml in self.picking.move_line_ids:
57
+ ml.qty_done = ml.reserved_uom_qty
58
+ ml.shopfloor_checkout_done = True
59
+ response = self.service.dispatch(
60
+ "summary", params={"picking_id": self.picking.id}
61
+ )
62
+ self.assert_response(
63
+ response,
64
+ next_state="summary",
65
+ data={
66
+ "picking": self._stock_picking_data(self.picking, done=True),
67
+ "all_processed": True,
68
+ },
69
+ )
@@ -0,0 +1,83 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ from .common import CommonCase, PickingBatchMixin
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class ClusterPickingCommonCase(CommonCase, PickingBatchMixin):
10
+ @classmethod
11
+ def setUpClassVars(cls, *args, **kwargs):
12
+ super().setUpClassVars(*args, **kwargs)
13
+ cls.menu = cls.env.ref("shopfloor.shopfloor_menu_demo_cluster_picking")
14
+ cls.profile = cls.env.ref("shopfloor_base.profile_demo_1")
15
+ cls.picking_type = cls.menu.picking_type_ids
16
+ cls.wh = cls.picking_type.warehouse_id
17
+
18
+ @classmethod
19
+ def setUpClassBaseData(cls, *args, **kwargs):
20
+ super().setUpClassBaseData(*args, **kwargs)
21
+ cls.wh.sudo().delivery_steps = "pick_pack_ship"
22
+
23
+ def setUp(self):
24
+ super().setUp()
25
+ self.service = self.get_service(
26
+ "cluster_picking", menu=self.menu, profile=self.profile
27
+ )
28
+
29
+ def _line_data(self, move_line, qty=None, package_dest=False, qty_done=None, **kw):
30
+ picking = move_line.picking_id
31
+ # A package exists on the move line, because the quant created
32
+ # by ``_simulate_batch_selected`` has a package.
33
+ data = self.data.move_line(move_line)
34
+ if not package_dest:
35
+ data["package_dest"] = None
36
+ if qty:
37
+ data["quantity"] = qty
38
+ if qty_done:
39
+ data["qty_done"] = qty_done
40
+ data.update(
41
+ {
42
+ "batch": self.data.picking_batch(picking.batch_id),
43
+ "picking": self.data.picking(picking),
44
+ "scan_location_or_pack_first": False,
45
+ }
46
+ )
47
+ data.update(kw)
48
+ return data
49
+
50
+ @classmethod
51
+ def _set_dest_package_and_done(cls, move_lines, dest_package):
52
+ """Simulate what would have been done in the previous steps"""
53
+ for line in move_lines:
54
+ line.write(
55
+ {
56
+ "qty_done": line.reserved_uom_qty,
57
+ "result_package_id": dest_package.id,
58
+ }
59
+ )
60
+
61
+ def _data_for_batch(self, batch, location, pack=None):
62
+ data = self.data.picking_batch(batch)
63
+ data["location_dest"] = self.data.location(location)
64
+ if pack:
65
+ data["package"] = self.data.package(pack)
66
+ return data
67
+
68
+
69
+ class ClusterPickingLineCommonCase(ClusterPickingCommonCase):
70
+ @classmethod
71
+ def setUpClassBaseData(cls, *args, **kwargs):
72
+ super().setUpClassBaseData(*args, **kwargs)
73
+ # quants already existing are from demo data
74
+ cls.env["stock.quant"].sudo().search(
75
+ [("location_id", "=", cls.stock_location.id)]
76
+ ).unlink()
77
+ cls.batch = cls._create_picking_batch(
78
+ [[cls.BatchProduct(product=cls.product_a, quantity=1)]]
79
+ )
80
+
81
+ def _line_data(self, move_line, qty=1.0, **kw):
82
+ # just force qty to 1.0
83
+ return super()._line_data(move_line, qty=qty, **kw)