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,135 @@
1
+ # Copyright 2020 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3
+
4
+ from .test_delivery_base import DeliveryCommonCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class DeliverySetQtyDonePackCase(DeliveryCommonCase):
10
+ """Tests for /set_qty_done_pack"""
11
+
12
+ @classmethod
13
+ def setUpClassBaseData(cls):
14
+ super().setUpClassBaseData()
15
+ cls.picking = picking = cls._create_picking(
16
+ lines=[
17
+ # we'll put A and B in a single package
18
+ (cls.product_a, 10),
19
+ (cls.product_b, 10),
20
+ # C alone in a package
21
+ (cls.product_c, 10),
22
+ # D in two different packages
23
+ (cls.product_d, 10),
24
+ ]
25
+ )
26
+ cls.pack1_moves = picking.move_ids[:2]
27
+ cls.pack2_move = picking.move_ids[2]
28
+ cls.pack3_move = picking.move_ids[3]
29
+ cls._fill_stock_for_moves(cls.pack1_moves, in_package=True)
30
+ cls._fill_stock_for_moves(cls.pack2_move, in_package=True)
31
+ # Fill stock for D moves (two packages)
32
+ for __ in range(2):
33
+ product_d_pkg = cls.env["stock.quant.package"].create({})
34
+ cls._update_qty_in_location(
35
+ cls.pack3_move.location_id,
36
+ cls.pack3_move.product_id,
37
+ 5,
38
+ package=product_d_pkg,
39
+ )
40
+ picking.action_assign()
41
+
42
+ def _test_set_qty_done_pack_ok(self, move_lines, package, qties=None):
43
+ response = self.service.dispatch(
44
+ "set_qty_done_pack",
45
+ params={"package_id": package.id, "picking_id": self.picking.id},
46
+ )
47
+ self.assert_qty_done(move_lines, qties=qties)
48
+ self.assert_response_deliver(response, picking=self.picking)
49
+
50
+ def test_set_qty_done_pack_picking_not_found(self):
51
+ move_lines = self.pack1_moves.mapped("move_line_ids")
52
+ package = move_lines.mapped("package_id")
53
+ response = self.service.dispatch(
54
+ "set_qty_done_pack", params={"package_id": package.id, "picking_id": -1}
55
+ )
56
+ self.assert_response_deliver(
57
+ response, message=self.service.msg_store.stock_picking_not_found()
58
+ )
59
+
60
+ def test_set_qty_done_pack_picking_canceled(self):
61
+ move_lines = self.pack1_moves.mapped("move_line_ids")
62
+ package = move_lines.mapped("package_id")
63
+ self.picking.action_cancel()
64
+ response = self.service.dispatch(
65
+ "set_qty_done_pack",
66
+ params={"package_id": package.id, "picking_id": self.picking.id},
67
+ )
68
+ self.assert_response_deliver(
69
+ response,
70
+ message=self.service.msg_store.stock_picking_not_available(self.picking),
71
+ )
72
+
73
+ def test_set_qty_done_pack_package_not_found(self):
74
+ response = self.service.dispatch(
75
+ "set_qty_done_pack",
76
+ params={"package_id": -1, "picking_id": self.picking.id},
77
+ )
78
+ self.assert_response_deliver(
79
+ response,
80
+ picking=self.picking,
81
+ message=self.service.msg_store.package_not_found(),
82
+ )
83
+
84
+ def test_set_qty_done_pack_multiple_product_ok(self):
85
+ move_lines = self.pack1_moves.mapped("move_line_ids")
86
+ package = move_lines.mapped("package_id")
87
+ self._test_set_qty_done_pack_ok(move_lines, package)
88
+
89
+ def test_set_qty_done_pack_one_product_ok(self):
90
+ move_lines = self.pack2_move.mapped("move_line_ids")
91
+ package = move_lines.mapped("package_id")
92
+ self._test_set_qty_done_pack_ok(move_lines, package)
93
+
94
+ def test_set_qty_done_pack_product_in_multiple_packages_ok(self):
95
+ move_lines = self.pack3_move.mapped("move_line_ids")
96
+ first_package = move_lines.mapped("package_id")[0]
97
+ self._test_set_qty_done_pack_ok(
98
+ move_lines,
99
+ # first_package done, not the second
100
+ first_package,
101
+ qties=[5, 0],
102
+ )
103
+
104
+ def test_set_qty_done_pack_picking_done(self):
105
+ pack1_move_lines = self.pack1_moves.mapped("move_line_ids")
106
+ package1 = pack1_move_lines.mapped("package_id")
107
+ pack2_move_lines = self.pack2_move.mapped("move_line_ids")
108
+ package2 = pack2_move_lines.mapped("package_id")
109
+ pack3_move_lines = self.pack3_move.mapped("move_line_ids")
110
+ packages3 = pack3_move_lines.mapped("package_id")
111
+ # process first package
112
+ self.service.dispatch(
113
+ "set_qty_done_pack",
114
+ params={"package_id": package1.id, "picking_id": self.picking.id},
115
+ )
116
+ self.assertEqual(self.picking.state, "assigned")
117
+ # process second package
118
+ self.service.dispatch(
119
+ "set_qty_done_pack",
120
+ params={"package_id": package2.id, "picking_id": self.picking.id},
121
+ )
122
+ self.assertEqual(self.picking.state, "assigned")
123
+ # process third package
124
+ self.service.dispatch(
125
+ "set_qty_done_pack",
126
+ params={"package_id": packages3[0].id, "picking_id": self.picking.id},
127
+ )
128
+ self.assertEqual(self.picking.state, "assigned")
129
+ # process last package
130
+ self.service.dispatch(
131
+ "set_qty_done_pack",
132
+ params={"package_id": packages3[1].id, "picking_id": self.picking.id},
133
+ )
134
+ # picking is done once all its moves have been processed
135
+ self.assertEqual(self.picking.state, "done")
@@ -0,0 +1,180 @@
1
+ # Copyright 2022 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ from .test_delivery_base import DeliveryCommonCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class DeliveryScanSublocationCase(DeliveryCommonCase):
10
+ """Tests sublocation with delivery service."""
11
+
12
+ @classmethod
13
+ def setUpClassBaseData(cls):
14
+ super().setUpClassBaseData()
15
+ cls.product_e.tracking = "lot"
16
+ # Picking for the top location
17
+ cls.picking = picking = cls._create_picking(
18
+ lines=[
19
+ (cls.product_d, 10), # D as raw product
20
+ (cls.product_e, 10), # E as raw product with a lot
21
+ ]
22
+ )
23
+ cls.raw_move = picking.move_ids[0]
24
+ cls.raw_lot_move = picking.move_ids[1]
25
+ cls._fill_stock_for_moves(cls.raw_move)
26
+ cls._fill_stock_for_moves(cls.raw_lot_move, in_lot=True)
27
+ picking.action_assign()
28
+ cls.lot = cls.raw_lot_move.move_line_ids.lot_id
29
+ # Create a sublocation
30
+ cls.sublocation = (
31
+ cls.env["stock.location"]
32
+ .sudo()
33
+ .create(
34
+ {
35
+ "name": "Output 1",
36
+ "location_id": cls.picking_type.default_location_src_id.id,
37
+ "usage": "internal",
38
+ "barcode": "WH-OUTPUT-1",
39
+ }
40
+ )
41
+ )
42
+ # Picking for the sublocation
43
+ cls.picking_sublocation = cls._create_picking(
44
+ lines=[
45
+ (cls.product_d, 10), # D as raw product
46
+ (cls.product_e, 10), # E as raw product with a lot
47
+ ],
48
+ location_id=cls.sublocation,
49
+ )
50
+ cls.raw_move_sublocation = cls.picking_sublocation.move_ids[0]
51
+ cls.raw_lot_move_sublocation = cls.picking_sublocation.move_ids[1]
52
+ cls._fill_stock_for_moves(cls.raw_move_sublocation, location=cls.sublocation)
53
+ # Use the same lot on product from both picking
54
+ cls._fill_stock_for_moves(
55
+ cls.raw_lot_move_sublocation, in_lot=cls.lot, location=cls.sublocation
56
+ )
57
+ cls.picking_sublocation.action_assign()
58
+
59
+ def test_scan_sublocation_exists(self):
60
+ """Check scanning a sublocation sets it as sublocation."""
61
+ response = self.service.dispatch(
62
+ "scan_deliver",
63
+ params={
64
+ "barcode": self.sublocation.barcode,
65
+ "picking_id": None,
66
+ "location_id": None,
67
+ },
68
+ )
69
+ self.assert_response_deliver(
70
+ response,
71
+ picking=None,
72
+ location=self.sublocation,
73
+ message=self.service.msg_store.location_src_set_to_sublocation(
74
+ self.sublocation
75
+ ),
76
+ )
77
+
78
+ def test_scan_invalid_barcode_in_sublocation(self):
79
+ response = self.service.dispatch(
80
+ "scan_deliver",
81
+ params={
82
+ "barcode": "NO VALID BARCODE",
83
+ "picking_id": None,
84
+ "location_id": self.sublocation.id,
85
+ },
86
+ )
87
+ self.assert_response_deliver(
88
+ response,
89
+ location=self.sublocation,
90
+ message=self.service.msg_store.barcode_not_found(),
91
+ )
92
+
93
+ def test_scan_barcode_in_sublocation(self):
94
+ """Scan product barcode that exists in sublocation."""
95
+
96
+ response = self.service.dispatch(
97
+ "scan_deliver",
98
+ params={
99
+ "barcode": self.product_d.barcode,
100
+ "location_id": self.sublocation.id,
101
+ },
102
+ )
103
+ self.assert_response_deliver(
104
+ response,
105
+ location=self.sublocation,
106
+ picking=self.picking_sublocation,
107
+ )
108
+
109
+ def test_scan_product_not_in_sublocation(self):
110
+ """Scan a product in picking type location but not in sublocation set."""
111
+ response = self.service.dispatch(
112
+ "scan_deliver",
113
+ params={
114
+ "barcode": self.product_c.barcode,
115
+ "picking_id": None,
116
+ "location_id": self.sublocation.id,
117
+ },
118
+ )
119
+ self.assert_response_deliver(
120
+ response,
121
+ location=self.sublocation,
122
+ message=self.service.msg_store.product_not_found_in_pickings(),
123
+ )
124
+
125
+ def test_scan_product_exist_in_multiple_sublocation(self):
126
+ """Check scan of product in multiple location will ask to scan a location."""
127
+ response = self.service.dispatch(
128
+ "scan_deliver",
129
+ params={
130
+ "barcode": self.product_d.barcode,
131
+ "picking_id": None,
132
+ },
133
+ )
134
+ self.assert_response_deliver(
135
+ response,
136
+ message=self.service.msg_store.product_in_multiple_sublocation(
137
+ self.product_d
138
+ ),
139
+ )
140
+
141
+ def test_list_stock_picking_sublocation(self):
142
+ """Check manual selection filter picking in sublocation."""
143
+ response = self.service.dispatch(
144
+ "list_stock_picking", params={"location_id": self.sublocation.id}
145
+ )
146
+ self.assert_response_manual_selection(
147
+ response,
148
+ pickings=self.picking_sublocation,
149
+ )
150
+
151
+ def test_scan_lot_in_sublocation(self):
152
+ """Scan a lot that exists in sublocation."""
153
+ lot = self.raw_lot_move_sublocation.move_line_ids.lot_id
154
+ response = self.service.dispatch(
155
+ "scan_deliver",
156
+ params={
157
+ "barcode": lot.name,
158
+ "picking_id": None,
159
+ "location_id": self.sublocation.id,
160
+ },
161
+ )
162
+ self.assert_response_deliver(
163
+ response,
164
+ location=self.sublocation,
165
+ picking=self.picking_sublocation,
166
+ )
167
+
168
+ def test_scan_lot_exist_in_multiple_sublocation(self):
169
+ """Check scanning lot in multiple location, will ask location scan first."""
170
+ response = self.service.dispatch(
171
+ "scan_deliver",
172
+ params={
173
+ "barcode": self.lot.name,
174
+ "picking_id": None,
175
+ },
176
+ )
177
+ self.assert_response_deliver(
178
+ response,
179
+ message=self.service.msg_store.lot_in_multiple_sublocation(self.lot),
180
+ )
@@ -0,0 +1,136 @@
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
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class LocationContentTransferCommonCase(CommonCase):
10
+ @classmethod
11
+ def setUpClassVars(cls, *args, **kwargs):
12
+ super().setUpClassVars(*args, **kwargs)
13
+ cls.menu = cls.env.ref(
14
+ "shopfloor.shopfloor_menu_demo_location_content_transfer"
15
+ )
16
+ cls.profile = cls.env.ref("shopfloor_base.profile_demo_1")
17
+ cls.picking_type = cls.menu.picking_type_ids
18
+ cls.wh = cls.picking_type.warehouse_id
19
+
20
+ @classmethod
21
+ def setUpClassBaseData(cls, *args, **kwargs):
22
+ super().setUpClassBaseData(*args, **kwargs)
23
+ cls.content_loc = (
24
+ cls.env["stock.location"]
25
+ .sudo()
26
+ .create(
27
+ {
28
+ "name": "Content Location",
29
+ "barcode": "Content",
30
+ "location_id": cls.picking_type.default_location_src_id.id,
31
+ }
32
+ )
33
+ )
34
+
35
+ def setUp(self):
36
+ super().setUp()
37
+ self.service = self.get_service(
38
+ "location_content_transfer", menu=self.menu, profile=self.profile
39
+ )
40
+ self.stock_action = self.service._actions_for("stock")
41
+
42
+ def _simulate_selected_move_line(self, move_line):
43
+ """Mark the move line as picked (as it's done into the scan_location method)"""
44
+ self.stock_action.mark_move_line_as_picked(move_line)
45
+
46
+ @classmethod
47
+ def _simulate_pickings_selected(cls, pickings):
48
+ """Create a state as if pickings has been selected
49
+
50
+ ... during a Location content transfer.
51
+
52
+ It means a user scanned the location with the pickings. They are:
53
+
54
+ * assigned to the user
55
+ * the qty_done of all their move lines is set to they reserved qty
56
+
57
+ """
58
+ pickings.user_id = cls.env.uid
59
+ for line in pickings.mapped("move_line_ids"):
60
+ line.qty_done = line.reserved_uom_qty
61
+
62
+ def assert_response_start(self, response, message=None, popup=None):
63
+ self.assert_response(
64
+ response, next_state="scan_location", message=message, popup=popup
65
+ )
66
+
67
+ def _assert_response_scan_destination_all(
68
+ self, state, response, pickings, message=None, confirmation_required=False
69
+ ):
70
+ # this code is repeated from the implementation, not great, but we
71
+ # mostly want to ensure the selection of pickings is right, and the
72
+ # data methods have their own tests
73
+ move_lines = pickings.move_line_ids
74
+ lines = move_lines.filtered(lambda line: not line.package_level_id)
75
+ package_levels = pickings.package_level_ids
76
+ location = move_lines.location_id
77
+ self.assert_response(
78
+ response,
79
+ next_state=state,
80
+ data={
81
+ "move_lines": self.data.move_lines(lines),
82
+ "package_levels": self.data.package_levels(package_levels),
83
+ "location": self.data.location(location),
84
+ "confirmation_required": confirmation_required,
85
+ },
86
+ message=message,
87
+ )
88
+
89
+ def assert_response_scan_destination_all(
90
+ self, response, pickings, message=None, confirmation_required=False
91
+ ):
92
+ self._assert_response_scan_destination_all(
93
+ "scan_destination_all",
94
+ response,
95
+ pickings,
96
+ message=message,
97
+ confirmation_required=confirmation_required,
98
+ )
99
+
100
+ def assert_response_start_single(
101
+ self, response, pickings, message=None, popup=None
102
+ ):
103
+ sorter = self.service._actions_for("location_content_transfer.sorter")
104
+ sorter.feed_pickings(pickings)
105
+ location = pickings.mapped("location_id")
106
+ self.assert_response(
107
+ response,
108
+ next_state="start_single",
109
+ data=self.service._data_content_line_for_location(location, next(sorter)),
110
+ message=message,
111
+ popup=popup,
112
+ )
113
+
114
+ def _assert_response_scan_destination(
115
+ self, state, response, next_content, message=None, confirmation_required=False
116
+ ):
117
+ location = next_content.location_id
118
+ data = self.service._data_content_line_for_location(location, next_content)
119
+ data["confirmation_required"] = confirmation_required
120
+ self.assert_response(
121
+ response,
122
+ next_state=state,
123
+ data=data,
124
+ message=message,
125
+ )
126
+
127
+ def assert_response_scan_destination(
128
+ self, response, next_content, message=None, confirmation_required=False
129
+ ):
130
+ self._assert_response_scan_destination(
131
+ "scan_destination",
132
+ response,
133
+ next_content,
134
+ message=message,
135
+ confirmation_required=confirmation_required,
136
+ )
@@ -0,0 +1,125 @@
1
+ # Copyright 2022 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
3
+
4
+ from .test_location_content_transfer_base import LocationContentTransferCommonCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class TestLocationContentTransferGetWork(LocationContentTransferCommonCase):
10
+ """Tests for getting work
11
+
12
+ Endpoints:
13
+
14
+ * /find_work
15
+ * /cancel_work
16
+ """
17
+
18
+ @classmethod
19
+ def setUpClassBaseData(cls):
20
+ super().setUpClassBaseData()
21
+ cls.menu.sudo().allow_get_work = True
22
+ cls.pickings = cls.env["stock.picking"].search(
23
+ [("location_id", "=", cls.stock_location.id)]
24
+ )
25
+ cls.move_lines = cls.pickings.move_line_ids.filtered(
26
+ lambda line: line.qty_done == 0
27
+ and line.state in ("assigned", "partially_available")
28
+ and not line.shopfloor_user_id
29
+ )
30
+ products = cls.product_a + cls.product_b + cls.product_c + cls.product_d
31
+ for product in products:
32
+ cls.env["stock.putaway.rule"].sudo().create(
33
+ {
34
+ "product_id": product.id,
35
+ "location_in_id": cls.stock_location.id,
36
+ "location_out_id": cls.shelf1.id,
37
+ }
38
+ )
39
+
40
+ cls.picking1 = picking1 = cls._create_picking(
41
+ lines=[(cls.product_a, 10), (cls.product_b, 10)]
42
+ )
43
+ cls.picking2 = picking2 = cls._create_picking(
44
+ lines=[(cls.product_c, 10), (cls.product_d, 10)]
45
+ )
46
+ cls.pickings = picking1 | picking2
47
+ cls.content_loc2 = (
48
+ cls.env["stock.location"]
49
+ .sudo()
50
+ .create(
51
+ {
52
+ "name": "Content Location 2",
53
+ "barcode": "Content2",
54
+ "location_id": cls.picking_type.default_location_src_id.id,
55
+ }
56
+ )
57
+ )
58
+ cls._fill_stock_for_moves(
59
+ picking1.move_ids, in_package=True, location=cls.content_loc
60
+ )
61
+ cls._fill_stock_for_moves(picking2.move_ids[0], location=cls.content_loc2)
62
+ cls._fill_stock_for_moves(picking2.move_ids[1], location=cls.content_loc)
63
+ cls.pickings.action_assign()
64
+
65
+ def _get_location_lines(self, location):
66
+ return self.env["stock.move.line"].search([("location_id", "=", location.id)])
67
+
68
+ def test_get_work(self):
69
+ """Check the first state is get_work when the option is enabled."""
70
+ response = self.service.dispatch("start_or_recover", params={})
71
+ self.assert_response(
72
+ response,
73
+ next_state="get_work",
74
+ data={},
75
+ )
76
+
77
+ def test_find_work_no_work_found(self):
78
+ """Check the user asked for work but none is found."""
79
+ # Cancel all moves related to the location we work from
80
+ self.pickings.move_ids.filtered(lambda r: r.state != "done")._action_cancel()
81
+ response = self.service.dispatch("find_work", params={})
82
+ self.assert_response(
83
+ response,
84
+ next_state="get_work",
85
+ data={},
86
+ message=self.service.msg_store.no_work_found(),
87
+ )
88
+
89
+ def test_find_work_work_found(self):
90
+ """Check the user is offered a location to work from."""
91
+ next_location = self.service._find_location_to_work_from()
92
+ response = self.service.dispatch("find_work", params={})
93
+ self.assert_response(
94
+ response,
95
+ next_state="scan_location",
96
+ data={
97
+ "location": self.data.location(next_location),
98
+ },
99
+ )
100
+ lines = self._get_location_lines(next_location)
101
+ self.assertEqual(lines.shopfloor_user_id, self.env.user)
102
+ # Confirm the location
103
+ response = self.service.dispatch(
104
+ "scan_location", params={"barcode": next_location.name}
105
+ )
106
+ self.assertEqual(response["next_state"], "scan_destination_all")
107
+
108
+ def test_cancel_work(self):
109
+ next_location = self.service._find_location_to_work_from()
110
+ stock = self.service._actions_for("stock")
111
+ location_lines = self._get_location_lines(next_location)
112
+ stock.mark_move_line_as_picked(location_lines, quantity=0)
113
+ location_lines = self._get_location_lines(next_location)
114
+ self.assertEqual(location_lines.shopfloor_user_id, self.env.user)
115
+ response = self.service.dispatch(
116
+ "cancel_work", params={"location_id": next_location.id}
117
+ )
118
+ self.assert_response(
119
+ response,
120
+ next_state="get_work",
121
+ data={},
122
+ message={},
123
+ )
124
+ lines = self._get_location_lines(next_location)
125
+ self.assertFalse(lines.shopfloor_user_id)