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,12 @@
1
+ from . import priority_postpone_mixin
2
+ from . import shopfloor_menu
3
+ from . import shopfloor_app
4
+ from . import stock_picking_type
5
+ from . import stock_location
6
+ from . import stock_move
7
+ from . import stock_move_line
8
+ from . import stock_package_level
9
+ from . import stock_picking
10
+ from . import stock_picking_batch
11
+ from . import stock_quant
12
+ from . import stock_quant_package
@@ -0,0 +1,41 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from odoo import fields, models
4
+
5
+
6
+ class PriorityPostponeMixin(models.AbstractModel):
7
+ _name = "shopfloor.priority.postpone.mixin"
8
+ _description = "Adds shopfloor priority/postpone fields"
9
+
10
+ _SF_PRIORITY_DEFAULT = 10
11
+
12
+ shopfloor_priority = fields.Integer(
13
+ default=lambda self: self._SF_PRIORITY_DEFAULT,
14
+ copy=False,
15
+ help="Technical field. Overrides operation priority in barcode scenario.",
16
+ )
17
+ shopfloor_postponed = fields.Boolean(
18
+ copy=False,
19
+ help="Technical field. "
20
+ "Indicates if the operation has been postponed in a barcode scenario.",
21
+ )
22
+
23
+ def _get_max_shopfloor_priority(self, records):
24
+ self.ensure_one()
25
+ if not records:
26
+ return 0
27
+ return max(rec.shopfloor_priority for rec in records)
28
+
29
+ def shopfloor_postpone(self, *recordsets):
30
+ """Postpone the record and update its priority based on other records.
31
+
32
+ The method accepts several recordsets as parameter (to be able to get
33
+ the current max priority from different types of records).
34
+ """
35
+ self.ensure_one()
36
+ # Set the max priority from sibling records + 1
37
+ max_priority = max(
38
+ self._get_max_shopfloor_priority(records) for records in recordsets
39
+ )
40
+ self.shopfloor_priority = max_priority + 1
41
+ self.shopfloor_postponed = True
@@ -0,0 +1,9 @@
1
+ # Copyright 2021 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from odoo import fields, models
4
+
5
+
6
+ class ShopfloorApp(models.Model):
7
+ _inherit = "shopfloor.app"
8
+
9
+ category = fields.Selection(selection_add=[("wms", "WMS")])
@@ -0,0 +1,436 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from odoo import _, api, exceptions, fields, models
4
+
5
+ PICK_PACK_SAME_TIME_HELP = """
6
+ If you tick this box, while picking goods from a location
7
+ (eg: zone picking) set destination will work as follow:
8
+
9
+ * if a location is scanned, a new delivery package is created;
10
+ * if a package is scanned, the package is validated against the carrier
11
+ * in both cases, if the picking has no carrier the operation fails.",
12
+ """
13
+
14
+ UNLOAD_PACK_AT_DEST_HELP = """
15
+ With this option, the lines you process by putting on a package during the
16
+ picking process will be put as bulk products at the final destination location.
17
+
18
+ This is useful if your picking device is emptied at the destination location or
19
+ if you want to provide bulk products to the next operation.
20
+
21
+ Incompatible with: "Pick and pack at the same time"
22
+ """
23
+
24
+ MULTIPLE_MOVE_SINGLE_PACK_HELP = """
25
+ When picking a move,
26
+ allow to set a destination package that was already used for the other lines.
27
+ """
28
+
29
+ NO_PREFILL_QTY_HELP = """
30
+ We assume the picker will take the suggested quantities.
31
+ With this option, the operator will have to enter the quantity manually or
32
+ by scanning a product or product packaging EAN to increase the quantity
33
+ (i.e. +1 Unit or +1 Box)
34
+ """
35
+
36
+ AUTO_POST_LINE = """
37
+ When setting result pack & destination,
38
+ automatically post the corresponding line
39
+ if this option is checked.
40
+ """
41
+
42
+ RETURN_HELP = """
43
+ When enabled, you can receive unplanned products that are returned
44
+ from an existing delivery matched on the origin (SO name).
45
+ A new move will be added as a return of the delivery,
46
+ decreasing the delivered quantity of the related SO line.
47
+ """
48
+
49
+
50
+ class ShopfloorMenu(models.Model):
51
+ _inherit = "shopfloor.menu"
52
+
53
+ picking_type_ids = fields.Many2many(
54
+ comodel_name="stock.picking.type", string="Operation Types", required=True
55
+ )
56
+ move_create_is_possible = fields.Boolean(compute="_compute_move_create_is_possible")
57
+ # only available for some scenarios, move_create_is_possible defines if the option
58
+ # can be used or not
59
+ allow_move_create = fields.Boolean(
60
+ string="Allow Move Creation",
61
+ default=False,
62
+ help="Some scenario may create move(s) when a product or package is"
63
+ " scanned and no move already exists. Any new move is created in the"
64
+ " selected operation type, so it can be active only when one type is selected.",
65
+ )
66
+ unreserve_other_moves_is_possible = fields.Boolean(
67
+ compute="_compute_unreserve_other_moves_is_possible"
68
+ )
69
+ allow_unreserve_other_moves = fields.Boolean(
70
+ string="Allow to process reserved quantities",
71
+ default=False,
72
+ help="If you tick this box, this scenario will allow operator to move"
73
+ " goods even if a reservation is made by a different operation type.",
74
+ )
75
+ ignore_no_putaway_available_is_possible = fields.Boolean(
76
+ compute="_compute_ignore_no_putaway_available_is_possible"
77
+ )
78
+ ignore_no_putaway_available = fields.Boolean(
79
+ string="Ignore transfers when no put-away is available",
80
+ default=False,
81
+ help="If you tick this box, the transfer is reserved only "
82
+ "if the put-away can find a sublocation (when putaway destination "
83
+ "is different from the operation type's destination).",
84
+ )
85
+ prepackaged_product_is_possible = fields.Boolean(
86
+ compute="_compute_prepackaged_product_is_possible"
87
+ )
88
+ allow_prepackaged_product = fields.Boolean(
89
+ string="Process as pre-packaged",
90
+ default=False,
91
+ help=(
92
+ "When active, what you scan (typically a product packaging EAN) "
93
+ "will be ship 'as-is' and the operation will be validated "
94
+ "triggering a backorder creation with the remaining lines."
95
+ ),
96
+ )
97
+ # TODO: refactor handling of these options.
98
+ # Possible solution:
99
+ # * field should stay on the scenario and get stored in options
100
+ # * field should use `sf_scenario` (eg: sf_scenario=("zone_picking", ))
101
+ # to control for which scenario it will be available
102
+ # * on the menu form, display a button to edit configurations
103
+ # and display a summary
104
+ pick_pack_same_time = fields.Boolean(
105
+ string="Pick and pack at the same time",
106
+ default=False,
107
+ help=PICK_PACK_SAME_TIME_HELP,
108
+ )
109
+ pick_pack_same_time_is_possible = fields.Boolean(
110
+ compute="_compute_pick_pack_same_time_is_possible"
111
+ )
112
+ multiple_move_single_pack_is_possible = fields.Boolean(
113
+ compute="_compute_multiple_move_single_pack_is_possible"
114
+ )
115
+ multiple_move_single_pack = fields.Boolean(
116
+ string="Collect multiple moves on a same destination package",
117
+ default=False,
118
+ help=MULTIPLE_MOVE_SINGLE_PACK_HELP,
119
+ )
120
+ unload_package_at_destination_is_possible = fields.Boolean(
121
+ compute="_compute_unload_package_at_dest_is_possible"
122
+ )
123
+ unload_package_at_destination = fields.Boolean(
124
+ string="Unload package at destination",
125
+ default=False,
126
+ help=UNLOAD_PACK_AT_DEST_HELP,
127
+ )
128
+
129
+ disable_full_bin_action_is_possible = fields.Boolean(
130
+ compute="_compute_disable_full_bin_action_is_possible"
131
+ )
132
+ disable_full_bin_action = fields.Boolean(
133
+ string="Disable full bin action",
134
+ default=False,
135
+ # TODO: improve this desc w/ usecases.
136
+ help=("When picking, prevent unloading the whole bin when full."),
137
+ )
138
+
139
+ allow_force_reservation = fields.Boolean(
140
+ string="Force stock reservation",
141
+ default=False,
142
+ )
143
+ allow_force_reservation_is_possible = fields.Boolean(
144
+ compute="_compute_allow_force_reservation_is_possible"
145
+ )
146
+
147
+ allow_get_work = fields.Boolean(
148
+ string="Show Get Work on start",
149
+ default=False,
150
+ help=(
151
+ "When enabled the user will have the option to ask "
152
+ "for a task to work on."
153
+ ),
154
+ )
155
+ allow_get_work_is_possible = fields.Boolean(
156
+ compute="_compute_allow_get_work_is_possible"
157
+ )
158
+ no_prefill_qty = fields.Boolean(
159
+ string="Do not pre-fill quantity to pick",
160
+ help=NO_PREFILL_QTY_HELP,
161
+ default=False,
162
+ )
163
+ no_prefill_qty_is_possible = fields.Boolean(
164
+ compute="_compute_no_prefill_qty_is_possible"
165
+ )
166
+ show_oneline_package_content = fields.Boolean(
167
+ string="Show one-line package content",
168
+ help="Display the content of package if it contains 1 line only",
169
+ default=False,
170
+ )
171
+ show_oneline_package_content_is_possible = fields.Boolean(
172
+ compute="_compute_show_oneline_package_content_is_possible"
173
+ )
174
+ scan_location_or_pack_first = fields.Boolean(
175
+ string="Scan first location or pack",
176
+ help=(
177
+ "When selecting work, force the user to first scan a location or pack,"
178
+ "then the product or lot."
179
+ ),
180
+ )
181
+ scan_location_or_pack_first_is_possible = fields.Boolean(
182
+ compute="_compute_scan_location_or_pack_first_is_possible"
183
+ )
184
+ allow_alternative_destination = fields.Boolean(
185
+ string="Allow to scan alternative destination locations",
186
+ help=(
187
+ "When enabled the user will have the option to scan "
188
+ "destination locations other than the expected ones "
189
+ "(ask for confirmation)."
190
+ ),
191
+ default=False,
192
+ )
193
+ allow_alternative_destination_is_possible = fields.Boolean(
194
+ compute="_compute_allow_alternative_destination_is_possible"
195
+ )
196
+ allow_return_is_possible = fields.Boolean(
197
+ compute="_compute_allow_return_is_possible"
198
+ )
199
+ allow_return = fields.Boolean(
200
+ string="Allow create returns",
201
+ default=False,
202
+ help=RETURN_HELP,
203
+ )
204
+
205
+ auto_post_line = fields.Boolean(
206
+ string="Automatically post line",
207
+ default=False,
208
+ help=AUTO_POST_LINE,
209
+ )
210
+ auto_post_line_is_possible = fields.Boolean(
211
+ compute="_compute_auto_post_line_is_possible"
212
+ )
213
+
214
+ @api.onchange("unload_package_at_destination")
215
+ def _onchange_unload_package_at_destination(self):
216
+ # Uncheck pick_pack_same_time when unload_package_at_destination is set to True
217
+ for record in self:
218
+ if record.unload_package_at_destination:
219
+ record.pick_pack_same_time = False
220
+
221
+ @api.onchange("pick_pack_same_time")
222
+ def _onchange_pick_pack_same_time(self):
223
+ # pick_pack_same_time is incompatible with multiple_move_single_pack and
224
+ # multiple_move_single_pack
225
+ for record in self:
226
+ if record.pick_pack_same_time:
227
+ record.unload_package_at_destination = False
228
+ record.multiple_move_single_pack = False
229
+
230
+ @api.onchange("multiple_move_single_pack")
231
+ def _onchange_multiple_move_single_pack(self):
232
+ # multiple_move_single_pack is incompatible with pick_pack_same_time,
233
+ for record in self:
234
+ if record.multiple_move_single_pack:
235
+ record.pick_pack_same_time = False
236
+
237
+ @api.constrains(
238
+ "unload_package_at_destination",
239
+ "pick_pack_same_time",
240
+ "multiple_move_single_pack",
241
+ )
242
+ def _check_options(self):
243
+ if self.pick_pack_same_time and self.unload_package_at_destination:
244
+ raise exceptions.UserError(
245
+ _(
246
+ "'Pick and pack at the same time' is incompatible with "
247
+ "'Unload package at destination'."
248
+ )
249
+ )
250
+ elif self.pick_pack_same_time and self.multiple_move_single_pack:
251
+ raise exceptions.UserError(
252
+ _(
253
+ "'Pick and pack at the same time' is incompatible with "
254
+ "'Multiple moves same destination package'."
255
+ )
256
+ )
257
+
258
+ @api.depends("scenario_id", "picking_type_ids")
259
+ def _compute_move_create_is_possible(self):
260
+ for menu in self:
261
+ menu.move_create_is_possible = bool(
262
+ menu.scenario_id.has_option("allow_create_moves")
263
+ and len(menu.picking_type_ids) == 1
264
+ )
265
+
266
+ @api.onchange("move_create_is_possible")
267
+ def onchange_move_create_is_possible(self):
268
+ self.allow_move_create = self.move_create_is_possible
269
+
270
+ @api.constrains("scenario_id", "picking_type_ids", "allow_move_create")
271
+ def _check_allow_move_create(self):
272
+ for menu in self:
273
+ if menu.allow_move_create and not menu.move_create_is_possible:
274
+ raise exceptions.ValidationError(
275
+ _("Creation of moves is not allowed for menu {}.").format(menu.name)
276
+ )
277
+
278
+ @api.depends("scenario_id")
279
+ def _compute_unreserve_other_moves_is_possible(self):
280
+ for menu in self:
281
+ menu.unreserve_other_moves_is_possible = menu.scenario_id.has_option(
282
+ "allow_unreserve_other_moves"
283
+ )
284
+
285
+ @api.depends("scenario_id")
286
+ def _compute_pick_pack_same_time_is_possible(self):
287
+ for menu in self:
288
+ menu.pick_pack_same_time_is_possible = menu.scenario_id.has_option(
289
+ "pick_pack_same_time"
290
+ )
291
+
292
+ @api.depends("scenario_id")
293
+ def _compute_unload_package_at_dest_is_possible(self):
294
+ for menu in self:
295
+ menu.unload_package_at_destination_is_possible = (
296
+ menu.scenario_id.has_option("unload_package_at_destination")
297
+ )
298
+
299
+ @api.depends("scenario_id")
300
+ def _compute_multiple_move_single_pack_is_possible(self):
301
+ for menu in self:
302
+ menu.multiple_move_single_pack_is_possible = menu.scenario_id.has_option(
303
+ "multiple_move_single_pack"
304
+ )
305
+
306
+ @api.onchange("unreserve_other_moves_is_possible")
307
+ def onchange_unreserve_other_moves_is_possible(self):
308
+ self.allow_unreserve_other_moves = self.unreserve_other_moves_is_possible
309
+
310
+ @api.depends("scenario_id")
311
+ def _compute_disable_full_bin_action_is_possible(self):
312
+ for menu in self:
313
+ menu.disable_full_bin_action_is_possible = menu.scenario_id.has_option(
314
+ "disable_full_bin_action"
315
+ )
316
+
317
+ @api.depends("scenario_id")
318
+ def _compute_ignore_no_putaway_available_is_possible(self):
319
+ for menu in self:
320
+ menu.ignore_no_putaway_available_is_possible = menu.scenario_id.has_option(
321
+ "allow_ignore_no_putaway_available"
322
+ )
323
+
324
+ @api.onchange("ignore_no_putaway_available_is_possible")
325
+ def onchange_ignore_no_putaway_available_is_possible(self):
326
+ self.ignore_no_putaway_available = self.ignore_no_putaway_available_is_possible
327
+
328
+ @api.depends("scenario_id")
329
+ def _compute_prepackaged_product_is_possible(self):
330
+ for menu in self:
331
+ menu.prepackaged_product_is_possible = menu.scenario_id.has_option(
332
+ "allow_prepackaged_product"
333
+ )
334
+
335
+ @api.constrains("scenario_id", "picking_type_ids", "ignore_no_putaway_available")
336
+ def _check_ignore_no_putaway_available(self):
337
+ for menu in self:
338
+ if (
339
+ menu.ignore_no_putaway_available
340
+ and not menu.ignore_no_putaway_available_is_possible
341
+ ):
342
+ raise exceptions.ValidationError(
343
+ _("Ignoring not found putaway is not allowed for menu {}.").format(
344
+ menu.name
345
+ )
346
+ )
347
+
348
+ @api.constrains("scenario_id", "picking_type_ids", "allow_unreserve_other_moves")
349
+ def _check_allow_unreserve_other_moves(self):
350
+ for menu in self:
351
+ if (
352
+ menu.allow_unreserve_other_moves
353
+ and not menu.unreserve_other_moves_is_possible
354
+ ):
355
+ raise exceptions.ValidationError(
356
+ _(
357
+ "Processing reserved quantities is" " not allowed for menu {}."
358
+ ).format(menu.name)
359
+ )
360
+
361
+ @api.constrains("scenario_id", "picking_type_ids")
362
+ def _check_move_entire_packages(self):
363
+ for menu in self:
364
+ # TODO: these kind of checks should be provided by the scenario itself.
365
+ bad_picking_types = [
366
+ x.name for x in menu.picking_type_ids if not x.show_entire_packs
367
+ ]
368
+ if (
369
+ menu.scenario_id.has_option("must_move_entire_pack")
370
+ and bad_picking_types
371
+ ):
372
+ scenario_name = menu.scenario_id.name
373
+ raise exceptions.ValidationError(
374
+ _(
375
+ "Scenario `%(scenario_name)s` require(s) "
376
+ "'Move Entire Packages' to be enabled.\n"
377
+ "These type(s) do not satisfy this constraint: "
378
+ "\n%(bad_picking_types)s.\n"
379
+ "Please, adjust your configuration.",
380
+ scenario_name=scenario_name,
381
+ bad_picking_types="\n- ".join(bad_picking_types),
382
+ )
383
+ )
384
+
385
+ @api.depends("scenario_id")
386
+ def _compute_allow_force_reservation_is_possible(self):
387
+ for menu in self:
388
+ menu.allow_force_reservation_is_possible = menu.scenario_id.has_option(
389
+ "allow_force_reservation"
390
+ )
391
+
392
+ @api.depends("scenario_id")
393
+ def _compute_allow_get_work_is_possible(self):
394
+ for menu in self:
395
+ menu.allow_get_work_is_possible = menu.scenario_id.has_option(
396
+ "allow_get_work"
397
+ )
398
+
399
+ @api.depends("scenario_id")
400
+ def _compute_no_prefill_qty_is_possible(self):
401
+ for menu in self:
402
+ menu.no_prefill_qty_is_possible = menu.scenario_id.has_option(
403
+ "no_prefill_qty"
404
+ )
405
+
406
+ @api.depends("scenario_id")
407
+ def _compute_show_oneline_package_content_is_possible(self):
408
+ for menu in self:
409
+ menu.show_oneline_package_content_is_possible = menu.scenario_id.has_option(
410
+ "show_oneline_package_content"
411
+ )
412
+
413
+ @api.depends("scenario_id")
414
+ def _compute_scan_location_or_pack_first_is_possible(self):
415
+ for menu in self:
416
+ menu.scan_location_or_pack_first_is_possible = menu.scenario_id.has_option(
417
+ "scan_location_or_pack_first"
418
+ )
419
+
420
+ @api.depends("scenario_id")
421
+ def _compute_auto_post_line_is_possible(self):
422
+ for menu in self:
423
+ menu.auto_post_line_is_possible = menu.scenario_id.has_option(
424
+ "auto_post_line"
425
+ )
426
+
427
+ def _compute_allow_alternative_destination_is_possible(self):
428
+ for menu in self:
429
+ menu.allow_alternative_destination_is_possible = (
430
+ menu.scenario_id.has_option("allow_alternative_destination")
431
+ )
432
+
433
+ @api.depends("scenario_id")
434
+ def _compute_allow_return_is_possible(self):
435
+ for menu in self:
436
+ menu.allow_return_is_possible = menu.scenario_id.has_option("allow_return")
@@ -0,0 +1,76 @@
1
+ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+ from odoo import fields, models
4
+ from odoo.tools.float_utils import float_compare
5
+
6
+
7
+ class StockLocation(models.Model):
8
+ _inherit = "stock.location"
9
+
10
+ shopfloor_picking_sequence = fields.Char(
11
+ help="The picking done in Shopfloor scenarios will respect this order. "
12
+ "The sequence is a char so it can be composed of fields such as "
13
+ "'corridor-rack-side-level'. Pay attention to the padding "
14
+ "('09' is before '19', '9' is not). It is recommended to use an"
15
+ " Export then an Import to populate this field using a spreadsheet.",
16
+ )
17
+ source_move_line_ids = fields.One2many(
18
+ comodel_name="stock.move.line", inverse_name="location_id", readonly=True
19
+ )
20
+ reserved_move_line_ids = fields.One2many(
21
+ comodel_name="stock.move.line",
22
+ compute="_compute_reserved_move_lines",
23
+ )
24
+
25
+ def _get_reserved_move_lines(self):
26
+ return self.env["stock.move.line"].search(
27
+ [
28
+ ("location_id", "child_of", self.id),
29
+ ("reserved_uom_qty", ">", 0),
30
+ ("state", "not in", ("done", "cancel")),
31
+ ]
32
+ )
33
+
34
+ def _compute_reserved_move_lines(self):
35
+ for rec in self:
36
+ rec.update({"reserved_move_line_ids": rec._get_reserved_move_lines()})
37
+
38
+ def planned_qty_in_location_is_empty(self, move_lines=None):
39
+ """Return if a location will be empty when move lines will be confirmed
40
+
41
+ Used for the "zero check". We need to know if a location is empty, but since
42
+ we set the move lines to "done" only at the end of the unload workflow, we
43
+ have to look at the qty_done of the move lines from this location.
44
+
45
+ With `move_lines` we can force the use of the given move lines for the check.
46
+ This allows to know that the location will be empty if we process only
47
+ these move lines.
48
+ """
49
+ self.ensure_one()
50
+ quants = self.env["stock.quant"].search(
51
+ [("quantity", ">", 0), ("location_id", "=", self.id)]
52
+ )
53
+ remaining = sum(quants.mapped("quantity"))
54
+ move_line_qty_field = "qty_done"
55
+ if move_lines:
56
+ move_lines = move_lines.filtered(
57
+ lambda m: m.state not in ("cancel", "done")
58
+ )
59
+ move_line_qty_field = "reserved_uom_qty"
60
+ else:
61
+ move_lines = self.env["stock.move.line"].search(
62
+ [
63
+ ("state", "not in", ("cancel", "done")),
64
+ ("location_id", "=", self.id),
65
+ ("qty_done", ">", 0),
66
+ ]
67
+ )
68
+ planned = remaining - sum(move_lines.mapped(move_line_qty_field))
69
+ compare = float_compare(planned, 0, precision_rounding=0.01)
70
+ return compare <= 0
71
+
72
+ def should_bypass_reservation(self):
73
+ self.ensure_one()
74
+ if self.env.context.get("force_reservation"):
75
+ return False
76
+ return super().should_bypass_reservation()