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,324 @@
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 collections import namedtuple
4
+
5
+ from odoo import models
6
+ from odoo.tests.common import Form
7
+
8
+ from odoo.addons.shopfloor_base.tests.common import CommonCase as BaseCommonCase
9
+
10
+
11
+ # pylint: disable=missing-return
12
+ class CommonCase(BaseCommonCase):
13
+ @classmethod
14
+ def setUpShopfloorApp(cls):
15
+ cls.shopfloor_app = cls.env.ref("shopfloor.app_demo").with_user(
16
+ cls.shopfloor_user
17
+ )
18
+
19
+ @classmethod
20
+ def setUpClassVars(cls):
21
+ super().setUpClassVars()
22
+ stock_location = cls.env.ref("stock.stock_location_stock")
23
+ cls.stock_location = stock_location
24
+ cls.customer_location = cls.env.ref("stock.stock_location_customers")
25
+ cls.dispatch_location = cls.env.ref("stock.location_dispatch_zone")
26
+ cls.packing_location = cls.env.ref("stock.location_pack_zone")
27
+ cls.input_location = cls.env.ref("stock.stock_location_company")
28
+ cls.shelf1 = cls.env.ref("stock.stock_location_components")
29
+ cls.shelf2 = cls.env.ref("stock.stock_location_14")
30
+
31
+ @classmethod
32
+ def _shopfloor_user_values(cls):
33
+ vals = super()._shopfloor_user_values()
34
+ vals["groups_id"] = [
35
+ (
36
+ 6,
37
+ 0,
38
+ [
39
+ cls.env.ref("stock.group_stock_user").id,
40
+ cls.env.ref("stock.group_stock_multi_locations").id,
41
+ ],
42
+ )
43
+ ]
44
+ return vals
45
+
46
+ @classmethod
47
+ def setUpClassBaseData(cls):
48
+ super().setUpClassBaseData()
49
+ cls.customer = cls.env["res.partner"].sudo().create({"name": "Customer"})
50
+
51
+ cls.customer_location.sudo().barcode = "CUSTOMERS"
52
+ cls.dispatch_location.sudo().barcode = "DISPATCH"
53
+ cls.packing_location.sudo().barcode = "PACKING"
54
+ cls.input_location.sudo().barcode = "INPUT"
55
+ cls.shelf1.sudo().barcode = "SHELF1"
56
+ cls.shelf2.sudo().barcode = "SHELF2"
57
+
58
+ cls.product_a = (
59
+ cls.env["product.product"]
60
+ .sudo()
61
+ .create(
62
+ {
63
+ "name": "Product A",
64
+ "type": "product",
65
+ "default_code": "A",
66
+ "barcode": "A",
67
+ "weight": 2,
68
+ }
69
+ )
70
+ )
71
+ cls.product_a_packaging = (
72
+ cls.env["product.packaging"]
73
+ .sudo()
74
+ .create(
75
+ {
76
+ "name": "Box",
77
+ "product_id": cls.product_a.id,
78
+ "barcode": "ProductABox",
79
+ "qty": 3.0,
80
+ }
81
+ )
82
+ )
83
+ cls.product_b = (
84
+ cls.env["product.product"]
85
+ .sudo()
86
+ .create(
87
+ {
88
+ "name": "Product B",
89
+ "type": "product",
90
+ "default_code": "B",
91
+ "barcode": "B",
92
+ "weight": 3,
93
+ }
94
+ )
95
+ )
96
+ cls.product_b_packaging = (
97
+ cls.env["product.packaging"]
98
+ .sudo()
99
+ .create(
100
+ {
101
+ "name": "Box",
102
+ "product_id": cls.product_b.id,
103
+ "barcode": "ProductBBox",
104
+ }
105
+ )
106
+ )
107
+ cls.product_c = (
108
+ cls.env["product.product"]
109
+ .sudo()
110
+ .create(
111
+ {
112
+ "name": "Product C",
113
+ "type": "product",
114
+ "default_code": "C",
115
+ "barcode": "C",
116
+ "weight": 3,
117
+ }
118
+ )
119
+ )
120
+ cls.product_c_packaging = (
121
+ cls.env["product.packaging"]
122
+ .sudo()
123
+ .create(
124
+ {
125
+ "name": "Box",
126
+ "product_id": cls.product_c.id,
127
+ "barcode": "ProductCBox",
128
+ "qty": 3,
129
+ }
130
+ )
131
+ )
132
+ cls.product_d = (
133
+ cls.env["product.product"]
134
+ .sudo()
135
+ .create(
136
+ {
137
+ "name": "Product D",
138
+ "type": "product",
139
+ "default_code": "D",
140
+ "barcode": "D",
141
+ "weight": 3,
142
+ }
143
+ )
144
+ )
145
+ cls.product_d_packaging = (
146
+ cls.env["product.packaging"]
147
+ .sudo()
148
+ .create(
149
+ {
150
+ "name": "Box",
151
+ "product_id": cls.product_d.id,
152
+ "barcode": "ProductDBox",
153
+ }
154
+ )
155
+ )
156
+
157
+ @classmethod
158
+ def _create_picking(cls, picking_type=None, lines=None, confirm=True, **kw):
159
+ picking_form = Form(cls.env["stock.picking"])
160
+ picking_form.picking_type_id = picking_type or cls.picking_type
161
+ picking_form.partner_id = cls.customer
162
+ if lines is None:
163
+ lines = [(cls.product_a, 10), (cls.product_b, 10)]
164
+ for product, qty in lines:
165
+ with picking_form.move_ids_without_package.new() as move:
166
+ move.product_id = product
167
+ move.product_uom_qty = qty
168
+ for k, v in kw.items():
169
+ setattr(picking_form, k, v)
170
+ picking = picking_form.save()
171
+ if confirm:
172
+ picking.action_confirm()
173
+ return picking
174
+
175
+ @classmethod
176
+ def _update_qty_in_location(
177
+ cls, location, product, quantity, package=None, lot=None
178
+ ):
179
+ quants = cls.env["stock.quant"]._gather(
180
+ product, location, lot_id=lot, package_id=package, strict=True
181
+ )
182
+ # this method adds the quantity to the current quantity, so remove it
183
+ quantity -= sum(quants.mapped("quantity"))
184
+ cls.env["stock.quant"]._update_available_quantity(
185
+ product, location, quantity, package_id=package, lot_id=lot
186
+ )
187
+
188
+ @classmethod
189
+ def _fill_stock_for_moves(
190
+ cls, moves, in_package=False, same_package=True, in_lot=False, location=False
191
+ ):
192
+ """Satisfy stock for given moves.
193
+
194
+ :param moves: stock.move recordset
195
+ :param in_package: stock.quant.package record or simple boolean
196
+ If a package record is given, it will be used as package.
197
+ If a boolean true is given, a new package will be created for each move.
198
+ :param same_package:
199
+ modify the behavior of `in_package` to use the same package for all moves.
200
+ :param in_lot: stock.lot record or simple boolean
201
+ If a lot record is given, it will be used as lot.
202
+ If a boolean true is given, a new lot will be created.
203
+ """
204
+ product_packages = {}
205
+ product_locations = {}
206
+ package = None
207
+ if in_package:
208
+ if isinstance(in_package, models.BaseModel):
209
+ package = in_package
210
+ else:
211
+ package = cls.env["stock.quant.package"].create({})
212
+ for move in moves:
213
+ key = (move.product_id, location or move.location_id)
214
+ product_locations.setdefault(key, 0)
215
+ product_locations[key] += move.product_qty
216
+ if in_package:
217
+ if isinstance(in_package, models.BaseModel):
218
+ package = in_package
219
+ if not package or package and not same_package:
220
+ package = cls.env["stock.quant.package"].create({})
221
+ product_packages[key] = package
222
+ for (product, location), qty in product_locations.items():
223
+ lot = None
224
+ if in_lot:
225
+ if isinstance(in_lot, models.BaseModel):
226
+ lot = in_lot
227
+ else:
228
+ lot = cls.env["stock.lot"].create(
229
+ {"product_id": product.id, "company_id": cls.env.company.id}
230
+ )
231
+ if not (in_lot or in_package):
232
+ # always add more quantity in stock to avoid to trigger the
233
+ # "zero checks" in tests, not for lots which must have a qty
234
+ # of 1 and not for packages because we need the strict number
235
+ # of units to pick a package
236
+ qty *= 2
237
+ cls._update_qty_in_location(
238
+ location, product, qty, package=package, lot=lot
239
+ )
240
+
241
+ # used by _create_package_in_location
242
+ PackageContent = namedtuple(
243
+ "PackageContent",
244
+ # recordset of the product,
245
+ # quantity in float
246
+ # recordset of the lot (optional)
247
+ "product quantity lot",
248
+ )
249
+
250
+ def _create_package_in_location(self, location, content):
251
+ """Create a package and quants in a location
252
+
253
+ content is a list of PackageContent
254
+ """
255
+ package = self.env["stock.quant.package"].create({})
256
+ for product, quantity, lot in content:
257
+ self._update_qty_in_location(
258
+ location, product, quantity, package=package, lot=lot
259
+ )
260
+ return package
261
+
262
+ def _create_lot(self, product):
263
+ return self.env["stock.lot"].create(
264
+ {"product_id": product.id, "company_id": self.env.company.id}
265
+ )
266
+
267
+
268
+ class PickingBatchMixin:
269
+
270
+ BatchProduct = namedtuple(
271
+ "BatchProduct",
272
+ # browse record of the product,
273
+ # quantity in float
274
+ "product quantity",
275
+ )
276
+
277
+ @classmethod
278
+ def _create_picking_batch(cls, products):
279
+ """Create a picking batch
280
+
281
+ :param products: list of list of BatchProduct. The outer list creates
282
+ pickings and the innerr list creates moves in these pickings
283
+ """
284
+ batch_form = Form(cls.env["stock.picking.batch"].sudo())
285
+ for transfer in products:
286
+ picking_form = Form(cls.env["stock.picking"].sudo())
287
+ picking_form.picking_type_id = cls.picking_type
288
+ picking_form.location_id = cls.stock_location
289
+ picking_form.origin = "test"
290
+ picking_form.partner_id = cls.customer
291
+ for batch_product in transfer:
292
+ product = batch_product.product
293
+ quantity = batch_product.quantity
294
+ with picking_form.move_ids_without_package.new() as move:
295
+ move.product_id = product
296
+ move.product_uom_qty = quantity
297
+ picking = picking_form.save()
298
+ batch_form.picking_ids.add(picking)
299
+
300
+ batch = batch_form.save()
301
+ batch.picking_ids.action_confirm()
302
+ batch.picking_ids.action_assign()
303
+ return batch
304
+
305
+ @classmethod
306
+ def _simulate_batch_selected(
307
+ cls, batches, in_package=False, in_lot=False, fill_stock=True
308
+ ):
309
+ """Create a state as if a batch was selected by the user
310
+
311
+ * The picking batch is in progress
312
+ * It is assigned to the current user
313
+ * All the move lines are available
314
+
315
+ Note: currently, this method create a source package that contains
316
+ all the products of the batch. It is enough for the current tests.
317
+ """
318
+ pickings = batches.mapped("picking_ids")
319
+ if fill_stock:
320
+ cls._fill_stock_for_moves(
321
+ pickings.mapped("move_ids"), in_package=in_package, in_lot=in_lot
322
+ )
323
+ pickings.action_assign()
324
+ batches.write({"state": "in_progress", "user_id": cls.env.uid})
@@ -0,0 +1,29 @@
1
+ # Copyright 2021 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3
+ """Test data models to get a mapping between carrier and delivery packaging.
4
+
5
+ Add a new "test" value for 'delivery_type' field of carrier and
6
+ 'package_carrier_type' field of packaging for test purpose because
7
+ Shopfloor do not depend on 'delivery_*' modules adding the different
8
+ delivery types.
9
+ """
10
+ from odoo import fields, models
11
+
12
+
13
+ class DeliveryCarrierTest(models.Model):
14
+ _inherit = "delivery.carrier"
15
+
16
+ delivery_type = fields.Selection(
17
+ selection_add=[("test", "TEST")], ondelete={"test": "set default"}
18
+ )
19
+ test_default_packaging_id = fields.Many2one(
20
+ "stock.package.type", string="Default Package Type"
21
+ )
22
+
23
+
24
+ class StockPackageType(models.Model):
25
+ _inherit = "stock.package.type"
26
+
27
+ package_carrier_type = fields.Selection(
28
+ selection_add=[("test", "TEST")], ondelete={"test": "set default"}
29
+ )