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,32 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # Copyright 2020 Akretion (http://www.akretion.com)
3
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
4
+ from .common import CommonCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class SinglePackTransferCommonBase(CommonCase):
10
+ @classmethod
11
+ def setUpClassVars(cls, *args, **kwargs):
12
+ super().setUpClassVars(*args, **kwargs)
13
+ cls.menu = cls.env.ref("shopfloor.shopfloor_menu_demo_single_pallet_transfer")
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
+ # we activate the move creation in tests when needed
22
+ cls.menu.sudo().allow_move_create = False
23
+
24
+ # disable the completion on the picking type, we'll have specific test(s)
25
+ # to check the behavior of this screen
26
+ cls.picking_type.sudo().display_completion_info = False
27
+
28
+ def setUp(self):
29
+ super().setUp()
30
+ self.service = self.get_service(
31
+ "single_pack_transfer", menu=self.menu, profile=self.profile
32
+ )
@@ -0,0 +1,104 @@
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_single_pack_transfer_base import SinglePackTransferCommonBase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class TestSinglePackTransferPutaway(SinglePackTransferCommonBase):
10
+ @classmethod
11
+ def setUpClassVars(cls, *args, **kwargs):
12
+ super().setUpClassVars(*args, **kwargs)
13
+ cls.pallets_storage_type = cls.env.ref(
14
+ "stock_storage_type.package_storage_type_pallets"
15
+ )
16
+ cls.main_pallets_location = cls.env.ref(
17
+ "stock_storage_type.stock_location_pallets"
18
+ )
19
+ cls.reserve_pallets_locations = cls.env.ref(
20
+ "stock_storage_type.stock_location_pallets_reserve"
21
+ )
22
+ cls.all_pallets_locations = (
23
+ cls.main_pallets_location.leaf_location_ids
24
+ | cls.reserve_pallets_locations.leaf_location_ids
25
+ )
26
+
27
+ @classmethod
28
+ def setUpClassBaseData(cls, *args, **kwargs):
29
+ super().setUpClassBaseData(*args, **kwargs)
30
+ cls.package = cls.env["stock.quant.package"].create(
31
+ {
32
+ # this will parameterize the putaway to use pallet locations,
33
+ # and if not, it will stay on the picking type's default dest.
34
+ "package_type_id": cls.pallets_storage_type.id,
35
+ }
36
+ )
37
+ cls._update_qty_in_location(cls.shelf1, cls.product_a, 10, package=cls.package)
38
+ cls.menu.sudo().ignore_no_putaway_available = True
39
+ cls.menu.sudo().allow_move_create = True
40
+
41
+ def test_normal_putaway(self):
42
+ """Ensure putaway is applied on moves"""
43
+ response = self.service.dispatch(
44
+ "start", params={"barcode": self.shelf1.barcode}
45
+ )
46
+ self.assert_response(
47
+ response,
48
+ next_state="scan_location",
49
+ data=self.ANY,
50
+ )
51
+ package_level_id = response["data"]["scan_location"]["id"]
52
+ package_level = self.env["stock.package_level"].browse(package_level_id)
53
+ self.assertIn(package_level.location_dest_id, self.all_pallets_locations)
54
+
55
+ def test_ignore_no_putaway_available(self):
56
+ """Ignore no putaway available is activated on the menu
57
+
58
+ In this case, when no putaway is possible, the changes
59
+ are rollbacked and an error is returned.
60
+ """
61
+ for location in self.all_pallets_locations:
62
+ package = self.env["stock.quant.package"].create(
63
+ {"package_type_id": self.pallets_storage_type.id}
64
+ )
65
+ self._update_qty_in_location(location, self.product_a, 10, package=package)
66
+
67
+ response = self.service.dispatch(
68
+ "start", params={"barcode": self.shelf1.barcode}
69
+ )
70
+ self.assert_response(
71
+ response,
72
+ next_state="start",
73
+ message=self.service.msg_store.no_putaway_destination_available(),
74
+ )
75
+
76
+ package_levels = self.env["stock.package_level"].search(
77
+ [("package_id", "=", self.package.id)]
78
+ )
79
+ # no package level created to move the package
80
+ self.assertFalse(package_levels)
81
+
82
+ def test_putaway_move_dest_not_child_of_picking_dest(self):
83
+ """Putaway is applied on move but the destination location is not a
84
+ child of the default picking type destination location.
85
+ Case where the picking is created by scanning a package level. Then the
86
+ move destination is according to the putaway and valid.
87
+ """
88
+ # Change the default destination location of the picking type
89
+ # to get it outside of the putaway destination
90
+ self.picking_type.sudo().default_location_dest_id = self.main_pallets_location
91
+ # Create a standard putaway to move the package from pallet storage
92
+ # to a unrelated one (outside of the pallet storage tree)
93
+ self.env["stock.putaway.rule"].sudo().create(
94
+ {
95
+ "product_id": self.product_a.id,
96
+ "location_in_id": self.picking_type.default_location_dest_id.id,
97
+ "location_out_id": self.env.ref("stock.location_refrigerator_small").id,
98
+ }
99
+ )
100
+ # Check the result
101
+ response = self.service.dispatch(
102
+ "start", params={"barcode": self.shelf1.barcode}
103
+ )
104
+ self.assert_response(response, next_state="scan_location", data=self.ANY)
@@ -0,0 +1,204 @@
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.tests import tagged
4
+ from odoo.tests.common import TransactionCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ @tagged("post_install", "-at_install")
10
+ class TestStockSplit(TransactionCase):
11
+ @classmethod
12
+ def setUpClass(cls):
13
+ super(TestStockSplit, cls).setUpClass()
14
+ cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
15
+ cls.warehouse = cls.env.ref("stock.warehouse0")
16
+ cls.warehouse.delivery_steps = "pick_pack_ship"
17
+ cls.customer_location = cls.env.ref("stock.stock_location_customers")
18
+ cls.pack_location = cls.warehouse.wh_pack_stock_loc_id
19
+ cls.ship_location = cls.warehouse.wh_output_stock_loc_id
20
+ cls.stock_location = cls.env.ref("stock.stock_location_stock")
21
+ # Create products
22
+ cls.product_a = (
23
+ cls.env["product.product"]
24
+ .sudo()
25
+ .create(
26
+ {
27
+ "name": "Product A",
28
+ "type": "product",
29
+ "default_code": "A",
30
+ "barcode": "A",
31
+ "weight": 2,
32
+ }
33
+ )
34
+ )
35
+ cls.product_a_packaging = (
36
+ cls.env["product.packaging"]
37
+ .sudo()
38
+ .create(
39
+ {
40
+ "name": "Box",
41
+ "product_id": cls.product_a.id,
42
+ "barcode": "ProductABox",
43
+ }
44
+ )
45
+ )
46
+ cls.product_b = (
47
+ cls.env["product.product"]
48
+ .sudo()
49
+ .create(
50
+ {
51
+ "name": "Product B",
52
+ "type": "product",
53
+ "default_code": "B",
54
+ "barcode": "B",
55
+ "weight": 2,
56
+ }
57
+ )
58
+ )
59
+ cls.product_b_packaging = (
60
+ cls.env["product.packaging"]
61
+ .sudo()
62
+ .create(
63
+ {
64
+ "name": "Box",
65
+ "product_id": cls.product_b.id,
66
+ "barcode": "ProductBBox",
67
+ }
68
+ )
69
+ )
70
+ # Put product_a quantities in different packages to get several move lines
71
+ cls.package_1 = cls.env["stock.quant.package"].create({"name": "PACKAGE_1"})
72
+ cls.package_2 = cls.env["stock.quant.package"].create({"name": "PACKAGE_2"})
73
+ cls.package_3 = cls.env["stock.quant.package"].create({"name": "PACKAGE_3"})
74
+ cls.package_4 = cls.env["stock.quant.package"].create({"name": "PACKAGE_4"})
75
+ cls._update_qty_in_location(
76
+ cls.stock_location, cls.product_a, 6, package=cls.package_1
77
+ )
78
+ cls._update_qty_in_location(
79
+ cls.stock_location, cls.product_a, 4, package=cls.package_2
80
+ )
81
+ cls._update_qty_in_location(
82
+ cls.stock_location, cls.product_a, 5, package=cls.package_3
83
+ )
84
+ # Put product_b quantities in stock
85
+ cls._update_qty_in_location(cls.stock_location, cls.product_b, 10)
86
+ # Create the pick/pack/ship transfer
87
+ cls.ship_move_a = cls.env["stock.move"].create(
88
+ {
89
+ "name": cls.product_a.display_name,
90
+ "product_id": cls.product_a.id,
91
+ "product_uom_qty": 15.0,
92
+ "product_uom": cls.product_a.uom_id.id,
93
+ "location_id": cls.ship_location.id,
94
+ "location_dest_id": cls.customer_location.id,
95
+ "warehouse_id": cls.warehouse.id,
96
+ "picking_type_id": cls.warehouse.out_type_id.id,
97
+ "procure_method": "make_to_order",
98
+ "state": "draft",
99
+ }
100
+ )
101
+ cls.ship_move_b = cls.env["stock.move"].create(
102
+ {
103
+ "name": cls.product_b.display_name,
104
+ "product_id": cls.product_b.id,
105
+ "product_uom_qty": 4,
106
+ "product_uom": cls.product_b.uom_id.id,
107
+ "location_id": cls.ship_location.id,
108
+ "location_dest_id": cls.customer_location.id,
109
+ "warehouse_id": cls.warehouse.id,
110
+ "picking_type_id": cls.warehouse.out_type_id.id,
111
+ "procure_method": "make_to_order",
112
+ "state": "draft",
113
+ }
114
+ )
115
+ (cls.ship_move_a | cls.ship_move_b)._assign_picking()
116
+ (cls.ship_move_a | cls.ship_move_b)._action_confirm(merge=False)
117
+ cls.pack_move_a = cls.ship_move_a.move_orig_ids[0]
118
+ cls.pick_move_a = cls.pack_move_a.move_orig_ids[0]
119
+ cls.pack_move_b = cls.ship_move_b.move_orig_ids[0]
120
+ cls.pick_move_b = cls.pack_move_b.move_orig_ids[0]
121
+ cls.picking = cls.pick_move_a.picking_id
122
+ cls.packing = cls.pack_move_a.picking_id
123
+ cls.picking.action_assign()
124
+
125
+ @classmethod
126
+ def _update_qty_in_location(
127
+ cls, location, product, quantity, package=None, lot=None
128
+ ):
129
+ quants = cls.env["stock.quant"]._gather(
130
+ product, location, lot_id=lot, package_id=package, strict=True
131
+ )
132
+ # this method adds the quantity to the current quantity, so remove it
133
+ quantity -= sum(quants.mapped("quantity"))
134
+ cls.env["stock.quant"]._update_available_quantity(
135
+ product, location, quantity, package_id=package, lot_id=lot
136
+ )
137
+
138
+ def test_split_pickings_from_source_location(self):
139
+ dest_location = self.pick_move_a.location_dest_id.sudo().copy(
140
+ {
141
+ "name": self.pick_move_a.location_dest_id.name + "_2",
142
+ "barcode": self.pick_move_a.location_dest_id.barcode + "_2",
143
+ "location_id": self.pick_move_a.location_dest_id.id,
144
+ }
145
+ )
146
+ # Pick goods from stock and move some of them to a different destination
147
+ self.assertEqual(self.pick_move_a.state, "assigned")
148
+ for i, move_line in enumerate(self.pick_move_a.move_line_ids):
149
+ move_line.qty_done = move_line.reserved_uom_qty
150
+ if i % 2:
151
+ move_line.location_dest_id = dest_location
152
+ self.pick_move_a.extract_and_action_done()
153
+ self.assertEqual(self.pick_move_a.state, "done")
154
+ # Pack step, we want to split move lines from common source location
155
+ self.assertEqual(self.pack_move_a.state, "assigned")
156
+ move_lines_to_process = self.pack_move_a.move_line_ids.filtered(
157
+ lambda ml: ml.location_id == dest_location
158
+ )
159
+ self.assertEqual(len(self.pack_move_a.move_line_ids), 3)
160
+ self.assertEqual(len(self.packing.package_level_ids), 3)
161
+ self.assertEqual(len(move_lines_to_process), 1)
162
+ new_packing = move_lines_to_process._split_pickings_from_source_location()
163
+ self.assertEqual(len(self.packing.package_level_ids), 2)
164
+ self.assertEqual(len(new_packing.package_level_ids), 1)
165
+ self.assertEqual(len(new_packing.move_line_ids), 1)
166
+ self.assertTrue(new_packing != self.packing)
167
+ self.assertEqual(new_packing.backorder_id, self.packing)
168
+ self.assertEqual(
169
+ self.pick_move_a.move_dest_ids.picking_id, self.packing | new_packing
170
+ )
171
+ self.assertEqual(move_lines_to_process.state, "assigned")
172
+ self.assertEqual(
173
+ set(self.pack_move_a.move_line_ids.mapped("state")), {"assigned"}
174
+ )
175
+
176
+ def test_extract_and_action_done_one_assigned_move(self):
177
+ self.assertFalse(self.picking.backorder_ids)
178
+ self.assertEqual(self.picking.state, "assigned")
179
+ for move_line in self.pick_move_b.move_line_ids:
180
+ move_line.qty_done = move_line.reserved_uom_qty
181
+ self.pick_move_b.extract_and_action_done()
182
+ new_picking = self.picking.backorder_ids
183
+ self.assertTrue(new_picking)
184
+ # Check move lines repartition
185
+ self.assertNotIn(self.pick_move_b, self.picking.move_ids)
186
+ self.assertEqual(new_picking.move_ids, self.pick_move_b)
187
+ # Check states
188
+ self.assertEqual(self.picking.state, "assigned")
189
+ self.assertEqual(self.pick_move_b.state, "done")
190
+ self.assertEqual(new_picking.state, "done")
191
+
192
+ def test_extract_and_action_done_multiple_assigned_moves(self):
193
+ self.assertFalse(self.picking.backorder_ids)
194
+ self.assertEqual(self.picking.state, "assigned")
195
+ for move_line in self.picking.move_line_ids:
196
+ move_line.qty_done = move_line.reserved_uom_qty
197
+ self.picking.move_ids.extract_and_action_done()
198
+ # No backorder as all moves of the picking have been validated
199
+ new_picking = self.picking.backorder_ids
200
+ self.assertFalse(new_picking)
201
+ # Check move lines repartition
202
+ self.assertEqual(len(self.picking.move_ids), 2)
203
+ # Check states
204
+ self.assertEqual(self.picking.state, "done")
@@ -0,0 +1,42 @@
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_menu_base import CommonMenuCase
5
+
6
+ # pylint: disable=missing-return
7
+
8
+
9
+ class UserCase(CommonMenuCase):
10
+ @classmethod
11
+ def setUpClassVars(cls, *args, **kwargs):
12
+ super().setUpClassVars(*args, **kwargs)
13
+ ref = cls.env.ref
14
+ cls.wms_profile = ref("shopfloor.profile_demo_1")
15
+ cls.wms_profile2 = ref("shopfloor.profile_demo_2")
16
+
17
+ def test_menu_by_profile(self):
18
+ """Request /user/menu w/ a specific profile but no picking types"""
19
+
20
+ service = self.get_service("user", profile=self.wms_profile)
21
+ menus = (
22
+ self.env["shopfloor.menu"]
23
+ .sudo()
24
+ .search([("profile_id", "=", self.wms_profile.id)])
25
+ )
26
+ response = service.dispatch("menu")
27
+ self.assert_response(
28
+ response,
29
+ data={"menus": [self._data_for_menu_item(menu) for menu in menus]},
30
+ )
31
+
32
+ service = self.get_service("user", profile=self.wms_profile2)
33
+ menus = (
34
+ self.env["shopfloor.menu"]
35
+ .sudo()
36
+ .search([("profile_id", "=", self.wms_profile2.id)])
37
+ )
38
+ response = service.dispatch("menu")
39
+ self.assert_response(
40
+ response,
41
+ data={"menus": [self._data_for_menu_item(menu) for menu in menus]},
42
+ )