odoo-addon-shopfloor 16.0.2.12.0.1__py3-none-any.whl → 18.0.0.1.1__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.
- odoo/addons/shopfloor/README.rst +79 -52
- odoo/addons/shopfloor/__manifest__.py +11 -4
- odoo/addons/shopfloor/actions/__init__.py +0 -1
- odoo/addons/shopfloor/actions/change_package_lot.py +4 -6
- odoo/addons/shopfloor/actions/data.py +7 -6
- odoo/addons/shopfloor/actions/inventory.py +1 -1
- odoo/addons/shopfloor/actions/message.py +1 -1
- odoo/addons/shopfloor/actions/move_line_search.py +1 -1
- odoo/addons/shopfloor/actions/search.py +8 -32
- odoo/addons/shopfloor/actions/stock.py +8 -7
- odoo/addons/shopfloor/i18n/ca.po +0 -5
- odoo/addons/shopfloor/i18n/de.po +0 -5
- odoo/addons/shopfloor/i18n/es_AR.po +0 -5
- odoo/addons/shopfloor/i18n/it.po +2 -8
- odoo/addons/shopfloor/i18n/pt_BR.po +0 -5
- odoo/addons/shopfloor/i18n/shopfloor.pot +7 -194
- odoo/addons/shopfloor/models/stock_location.py +5 -5
- odoo/addons/shopfloor/models/stock_move.py +24 -7
- odoo/addons/shopfloor/models/stock_move_line.py +29 -82
- odoo/addons/shopfloor/models/stock_picking.py +8 -6
- odoo/addons/shopfloor/models/stock_quant_package.py +2 -2
- odoo/addons/shopfloor/readme/CONTRIBUTORS.md +17 -0
- odoo/addons/shopfloor/readme/CREDITS.md +5 -0
- odoo/addons/shopfloor/readme/DESCRIPTION.md +37 -0
- odoo/addons/shopfloor/readme/{HISTORY.rst → HISTORY.md} +1 -2
- odoo/addons/shopfloor/readme/ROADMAP.md +10 -0
- odoo/addons/shopfloor/readme/USAGE.md +6 -0
- odoo/addons/shopfloor/services/checkout.py +9 -9
- odoo/addons/shopfloor/services/cluster_picking.py +2 -2
- odoo/addons/shopfloor/services/delivery.py +4 -6
- odoo/addons/shopfloor/services/forms/picking_form.py +4 -1
- odoo/addons/shopfloor/services/location_content_transfer.py +1 -1
- odoo/addons/shopfloor/services/single_pack_transfer.py +1 -1
- odoo/addons/shopfloor/services/zone_picking.py +5 -9
- odoo/addons/shopfloor/static/description/index.html +41 -18
- odoo/addons/shopfloor/tests/__init__.py +74 -73
- odoo/addons/shopfloor/tests/common.py +9 -9
- odoo/addons/shopfloor/tests/test_actions_change_package_lot.py +104 -71
- odoo/addons/shopfloor/tests/test_actions_data.py +17 -21
- odoo/addons/shopfloor/tests/test_actions_data_base.py +9 -3
- odoo/addons/shopfloor/tests/test_actions_data_detail.py +17 -19
- odoo/addons/shopfloor/tests/test_actions_search.py +2 -1
- odoo/addons/shopfloor/tests/test_actions_stock.py +4 -1
- odoo/addons/shopfloor/tests/test_checkout_auto_post.py +3 -3
- odoo/addons/shopfloor/tests/test_checkout_base.py +1 -1
- odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +1 -1
- odoo/addons/shopfloor/tests/test_checkout_list_package.py +4 -4
- odoo/addons/shopfloor/tests/test_checkout_new_package.py +3 -3
- odoo/addons/shopfloor/tests/test_checkout_no_package.py +3 -3
- odoo/addons/shopfloor/tests/test_checkout_scan_dest_location.py +1 -1
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +11 -11
- odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +1 -1
- odoo/addons/shopfloor/tests/test_checkout_set_qty.py +17 -17
- odoo/addons/shopfloor/tests/test_checkout_summary.py +2 -2
- odoo/addons/shopfloor/tests/test_cluster_picking_base.py +1 -1
- odoo/addons/shopfloor/tests/test_cluster_picking_batch.py +2 -2
- odoo/addons/shopfloor/tests/test_cluster_picking_change_pack_lot.py +1 -1
- odoo/addons/shopfloor/tests/test_cluster_picking_is_zero.py +1 -1
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination.py +17 -19
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_no_prefill_qty.py +1 -1
- odoo/addons/shopfloor/tests/test_cluster_picking_stock_issue.py +5 -5
- odoo/addons/shopfloor/tests/test_delivery_base.py +4 -6
- odoo/addons/shopfloor/tests/test_delivery_done.py +1 -1
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_line.py +1 -1
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_pack.py +1 -1
- odoo/addons/shopfloor/tests/test_delivery_scan_deliver.py +6 -6
- odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +2 -2
- odoo/addons/shopfloor/tests/test_location_content_transfer_mix.py +8 -8
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +41 -41
- odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +3 -3
- odoo/addons/shopfloor/tests/test_menu_base.py +4 -4
- odoo/addons/shopfloor/tests/test_move_action_assign.py +19 -4
- odoo/addons/shopfloor/tests/test_single_pack_transfer.py +3 -3
- odoo/addons/shopfloor/tests/test_stock_split.py +77 -17
- odoo/addons/shopfloor/tests/test_zone_picking_base.py +5 -5
- odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +2 -2
- odoo/addons/shopfloor/tests/test_zone_picking_complete_mix_pack_flux.py +2 -2
- odoo/addons/shopfloor/tests/test_zone_picking_require_destination_package.py +3 -3
- odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +4 -4
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +29 -29
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_package_not_allowed.py +2 -2
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +12 -12
- odoo/addons/shopfloor/tests/test_zone_picking_start.py +1 -1
- odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +12 -12
- odoo/addons/shopfloor/tests/test_zone_picking_unload_buffer_lines.py +4 -4
- odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +10 -10
- odoo/addons/shopfloor/tests/test_zone_picking_unload_single.py +3 -3
- odoo/addons/shopfloor/views/shopfloor_menu.xml +33 -102
- odoo/addons/shopfloor/views/stock_move_line.xml +2 -2
- odoo/addons/shopfloor/views/stock_picking_type.xml +1 -1
- odoo_addon_shopfloor-18.0.0.1.1.dist-info/METADATA +222 -0
- {odoo_addon_shopfloor-16.0.2.12.0.1.dist-info → odoo_addon_shopfloor-18.0.0.1.1.dist-info}/RECORD +94 -98
- {odoo_addon_shopfloor-16.0.2.12.0.1.dist-info → odoo_addon_shopfloor-18.0.0.1.1.dist-info}/WHEEL +1 -1
- odoo_addon_shopfloor-18.0.0.1.1.dist-info/top_level.txt +1 -0
- odoo/addons/shopfloor/actions/barcode_parser.py +0 -44
- odoo/addons/shopfloor/migrations/16.0.2.0.0/post-migration.py +0 -41
- odoo/addons/shopfloor/migrations/16.0.2.4.2/post-init_search_move_line_options.py +0 -33
- odoo/addons/shopfloor/migrations/16.0.2.4.2/post-migration.py +0 -29
- odoo/addons/shopfloor/readme/CONTRIBUTORS.rst +0 -18
- odoo/addons/shopfloor/readme/CREDITS.rst +0 -5
- odoo/addons/shopfloor/readme/DESCRIPTION.rst +0 -17
- odoo/addons/shopfloor/readme/ROADMAP.rst +0 -4
- odoo/addons/shopfloor/readme/USAGE.rst +0 -6
- odoo_addon_shopfloor-16.0.2.12.0.1.dist-info/METADATA +0 -193
- odoo_addon_shopfloor-16.0.2.12.0.1.dist-info/top_level.txt +0 -1
@@ -26,7 +26,7 @@ class StockLocation(models.Model):
|
|
26
26
|
return self.env["stock.move.line"].search(
|
27
27
|
[
|
28
28
|
("location_id", "child_of", self.id),
|
29
|
-
("
|
29
|
+
("quantity", ">", 0),
|
30
30
|
("state", "not in", ("done", "cancel")),
|
31
31
|
]
|
32
32
|
)
|
@@ -40,7 +40,7 @@ class StockLocation(models.Model):
|
|
40
40
|
|
41
41
|
Used for the "zero check". We need to know if a location is empty, but since
|
42
42
|
we set the move lines to "done" only at the end of the unload workflow, we
|
43
|
-
have to look at the
|
43
|
+
have to look at the picked quantity of the move lines from this location.
|
44
44
|
|
45
45
|
With `move_lines` we can force the use of the given move lines for the check.
|
46
46
|
This allows to know that the location will be empty if we process only
|
@@ -51,18 +51,18 @@ class StockLocation(models.Model):
|
|
51
51
|
[("quantity", ">", 0), ("location_id", "=", self.id)]
|
52
52
|
)
|
53
53
|
remaining = sum(quants.mapped("quantity"))
|
54
|
-
move_line_qty_field = "
|
54
|
+
move_line_qty_field = "qty_picked"
|
55
55
|
if move_lines:
|
56
56
|
move_lines = move_lines.filtered(
|
57
57
|
lambda m: m.state not in ("cancel", "done")
|
58
58
|
)
|
59
|
-
move_line_qty_field = "
|
59
|
+
move_line_qty_field = "quantity"
|
60
60
|
else:
|
61
61
|
move_lines = self.env["stock.move.line"].search(
|
62
62
|
[
|
63
63
|
("state", "not in", ("cancel", "done")),
|
64
64
|
("location_id", "=", self.id),
|
65
|
-
("
|
65
|
+
("picked", "=", True),
|
66
66
|
]
|
67
67
|
)
|
68
68
|
planned = remaining - sum(move_lines.mapped(move_line_qty_field))
|
@@ -1,16 +1,26 @@
|
|
1
1
|
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
2
|
# Copyright 2022 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
|
3
3
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
4
|
-
from odoo import
|
4
|
+
from odoo import api, fields, models
|
5
5
|
from odoo.tools.float_utils import float_compare
|
6
6
|
|
7
7
|
|
8
8
|
class StockMove(models.Model):
|
9
9
|
_inherit = "stock.move"
|
10
10
|
|
11
|
+
quantity_picked = fields.Float(compute="_compute_quantity_picked")
|
12
|
+
|
13
|
+
@api.depends("move_line_ids.picked", "move_line_ids.qty_picked")
|
14
|
+
def _compute_quantity_picked(self):
|
15
|
+
for move in self:
|
16
|
+
move.quantity_picked = sum(
|
17
|
+
ml.qty_picked for ml in move.move_line_ids if ml.picked
|
18
|
+
)
|
19
|
+
|
11
20
|
def _qty_is_satisfied(self):
|
12
21
|
compare = float_compare(
|
13
|
-
|
22
|
+
# TODO: in theory we should use ``quantity`` + ``picked``
|
23
|
+
self.quantity_picked,
|
14
24
|
self.product_uom_qty,
|
15
25
|
precision_rounding=self.product_uom.rounding,
|
16
26
|
)
|
@@ -32,11 +42,18 @@ class StockMove(models.Model):
|
|
32
42
|
to_move = other_move_lines
|
33
43
|
if other_move_lines or self.state == "partially_available":
|
34
44
|
if intersection:
|
35
|
-
qty_to_split = sum(to_move.mapped("
|
45
|
+
qty_to_split = sum(to_move.mapped("quantity"))
|
36
46
|
else:
|
37
|
-
qty_to_split = self.product_uom_qty - sum(
|
38
|
-
|
39
|
-
|
47
|
+
qty_to_split = self.product_uom_qty - sum(move_lines.mapped("quantity"))
|
48
|
+
prec = self.env["decimal.precision"].precision_get(
|
49
|
+
"Product Unit of Measure"
|
50
|
+
)
|
51
|
+
# Do not split if we have full quantity to split
|
52
|
+
if (
|
53
|
+
float_compare(qty_to_split, self.product_uom_qty, precision_digits=prec)
|
54
|
+
== 0
|
55
|
+
):
|
56
|
+
return self.browse()
|
40
57
|
split_move_vals = self._split(qty_to_split)
|
41
58
|
split_move = self.create(split_move_vals)
|
42
59
|
split_move.move_line_ids = to_move
|
@@ -80,7 +97,7 @@ class StockMove(models.Model):
|
|
80
97
|
new_picking.id,
|
81
98
|
new_picking.name,
|
82
99
|
)
|
83
|
-
message = (_("The split order {} has been created.")).format(link)
|
100
|
+
message = (self.env._("The split order {} has been created.")).format(link)
|
84
101
|
picking.message_post(body=message)
|
85
102
|
self.picking_id = new_picking.id
|
86
103
|
self.package_level_id.picking_id = new_picking.id
|
@@ -29,6 +29,8 @@ class StockMoveLine(models.Model):
|
|
29
29
|
# allow domain on picking_id.xxx without too much perf penalty
|
30
30
|
picking_id = fields.Many2one(auto_join=True)
|
31
31
|
|
32
|
+
is_shopfloor_created = fields.Boolean()
|
33
|
+
|
32
34
|
def _split_partial_quantity(self):
|
33
35
|
"""Create new move line for the quantity remaining to do
|
34
36
|
|
@@ -36,23 +38,23 @@ class StockMoveLine(models.Model):
|
|
36
38
|
"""
|
37
39
|
self.ensure_one()
|
38
40
|
rounding = self.product_uom_id.rounding
|
39
|
-
if float_is_zero(self.
|
41
|
+
if float_is_zero(self.qty_picked, precision_rounding=rounding):
|
40
42
|
return self.browse()
|
41
43
|
compare = float_compare(
|
42
|
-
self.
|
44
|
+
self.qty_picked, self.quantity, precision_rounding=rounding
|
43
45
|
)
|
44
46
|
qty_lesser = compare == -1
|
45
47
|
qty_greater = compare == 1
|
46
48
|
assert not qty_greater, "Quantity done cannot exceed quantity to do"
|
47
49
|
if qty_lesser:
|
48
|
-
remaining = self.
|
49
|
-
new_line = self.copy(
|
50
|
+
remaining = self.quantity - self.qty_picked
|
51
|
+
new_line = self.copy(
|
52
|
+
{"quantity": remaining, "qty_picked": 0, "picked": False}
|
53
|
+
)
|
50
54
|
# if we didn't bypass reservation update, the quant reservation
|
51
55
|
# would be reduced as much as the deduced quantity, which is wrong
|
52
56
|
# as we only moved the quantity to a new move line
|
53
|
-
self.with_context(
|
54
|
-
bypass_reservation_update=True
|
55
|
-
).reserved_uom_qty = self.qty_done
|
57
|
+
self.with_context(bypass_reservation_update=True).quantity = self.qty_picked
|
56
58
|
return new_line
|
57
59
|
return self.browse()
|
58
60
|
|
@@ -95,58 +97,6 @@ class StockMoveLine(models.Model):
|
|
95
97
|
default=default, backorder=True
|
96
98
|
)
|
97
99
|
|
98
|
-
def _split_pickings_from_source_location(self):
|
99
|
-
"""Ensure that the related pickings will have the same source location.
|
100
|
-
|
101
|
-
Some pickings related could have other unrelated move lines, as such we
|
102
|
-
have to split them to contain only the move lines related to the expected
|
103
|
-
source location.
|
104
|
-
|
105
|
-
Example:
|
106
|
-
|
107
|
-
Initial data:
|
108
|
-
|
109
|
-
PICK1:
|
110
|
-
- move line with source location LOC1
|
111
|
-
- move line with source location LOC2
|
112
|
-
PICK2:
|
113
|
-
- move line with source location LOC2
|
114
|
-
- move line with source location LOC3
|
115
|
-
|
116
|
-
Then we process move lines related to LOC2 with this method, we get:
|
117
|
-
|
118
|
-
PICK1:
|
119
|
-
- move line with source location LOC1
|
120
|
-
PICK2:
|
121
|
-
- move line with source location LOC3
|
122
|
-
PICK3:
|
123
|
-
- move line with source location LOC2
|
124
|
-
- move line with source location LOC2
|
125
|
-
|
126
|
-
Return the pickings containing the given move lines.
|
127
|
-
"""
|
128
|
-
_logger.warning(
|
129
|
-
"`_split_pickings_from_source_location` is deprecated "
|
130
|
-
"and replaced by `_extract_in_split_order`"
|
131
|
-
)
|
132
|
-
location_src_to_process = self.location_id
|
133
|
-
if location_src_to_process and len(location_src_to_process) != 1:
|
134
|
-
raise UserError(
|
135
|
-
_("Move lines processed have to share the same source location.")
|
136
|
-
)
|
137
|
-
pickings = self.picking_id
|
138
|
-
move_lines_to_process_ids = []
|
139
|
-
for picking in pickings:
|
140
|
-
location_src = picking.move_line_ids.location_id
|
141
|
-
if len(location_src) == 1:
|
142
|
-
continue
|
143
|
-
(picking.move_line_ids & self)._extract_in_split_order()
|
144
|
-
# Get the related move lines among the picking and split them
|
145
|
-
move_lines_to_process_ids.extend(
|
146
|
-
set(picking.move_line_ids.ids) & set(self.ids)
|
147
|
-
)
|
148
|
-
return self.picking_id
|
149
|
-
|
150
100
|
def _split_qty_to_be_done(self, qty_done, split_partial=True, **split_default_vals):
|
151
101
|
"""Check qty to be done for current move line. Split it if needed.
|
152
102
|
|
@@ -154,14 +104,12 @@ class StockMoveLine(models.Model):
|
|
154
104
|
:param split_partial: split if qty is less than expected
|
155
105
|
otherwise rely on a backorder.
|
156
106
|
"""
|
157
|
-
if self.
|
107
|
+
if self.quantity < 0:
|
158
108
|
raise UserError(_("The demand cannot be negative"))
|
159
109
|
# store a new line if we have split our line (not enough qty)
|
160
110
|
new_line = self.env["stock.move.line"]
|
161
111
|
rounding = self.product_uom_id.rounding
|
162
|
-
compare = float_compare(
|
163
|
-
qty_done, self.reserved_uom_qty, precision_rounding=rounding
|
164
|
-
)
|
112
|
+
compare = float_compare(qty_done, self.quantity, precision_rounding=rounding)
|
165
113
|
qty_lesser = compare == -1
|
166
114
|
qty_greater = compare == 1
|
167
115
|
if qty_greater:
|
@@ -180,16 +128,14 @@ class StockMoveLine(models.Model):
|
|
180
128
|
# split the move line which will be processed later (maybe the user
|
181
129
|
# has to pick some goods from another place because the location
|
182
130
|
# contained less items than expected)
|
183
|
-
remaining = self.
|
184
|
-
vals = {"
|
131
|
+
remaining = self.quantity - quantity_done
|
132
|
+
vals = {"quantity": remaining, "picked": False, "qty_picked": 0}
|
185
133
|
vals.update(split_default_vals)
|
186
134
|
new_line = self.copy(vals)
|
187
135
|
# if we didn't bypass reservation update, the quant reservation
|
188
136
|
# would be reduced as much as the deduced quantity, which is wrong
|
189
137
|
# as we only moved the quantity to a new move line
|
190
|
-
self.with_context(
|
191
|
-
bypass_reservation_update=True
|
192
|
-
).reserved_uom_qty = quantity_done
|
138
|
+
self.with_context(bypass_reservation_update=True).quantity = quantity_done
|
193
139
|
return new_line
|
194
140
|
|
195
141
|
def replace_package(self, new_package):
|
@@ -205,9 +151,7 @@ class StockMoveLine(models.Model):
|
|
205
151
|
)
|
206
152
|
|
207
153
|
# we can't change already picked lines
|
208
|
-
unreservable_lines = other_reserved_lines.filtered(
|
209
|
-
lambda line: line.qty_done == 0
|
210
|
-
)
|
154
|
+
unreservable_lines = other_reserved_lines.filtered(lambda line: not line.picked)
|
211
155
|
to_assign_moves = unreservable_lines.move_id
|
212
156
|
|
213
157
|
# if we leave the package level, it will try to reserve the same
|
@@ -266,26 +210,29 @@ class StockMoveLine(models.Model):
|
|
266
210
|
"lot_id": quant.lot_id.id,
|
267
211
|
"owner_id": quant.owner_id.id,
|
268
212
|
"result_package_id": False,
|
213
|
+
# Always set the 'quantity' value in 'values', this ensure quants are
|
214
|
+
# synchronized with the data set on the move line starting from Odoo
|
215
|
+
# 18.0 (see '<stock.move.line.write()' method in 'stock' module).
|
216
|
+
"quantity": self.quantity,
|
269
217
|
}
|
270
218
|
|
271
219
|
available_quantity = quant.quantity - quant.reserved_quantity
|
272
220
|
if is_lesser(
|
273
|
-
available_quantity, self.
|
221
|
+
available_quantity, self.quantity_product_uom, quant.product_uom_id.rounding
|
274
222
|
):
|
275
223
|
new_uom_qty = self.product_id.uom_id._compute_quantity(
|
276
224
|
available_quantity, self.product_uom_id, rounding_method="HALF-UP"
|
277
225
|
)
|
278
|
-
values["
|
226
|
+
values["quantity"] = new_uom_qty
|
279
227
|
|
280
228
|
self.write(values)
|
281
229
|
|
282
|
-
#
|
283
|
-
# recreate a package level if it applies
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
self.move_id._recompute_state()
|
230
|
+
# Try reassign the move in case we had a partial qty, also, it will
|
231
|
+
# recreate a package level if it applies.
|
232
|
+
# When we change the quantity of the move, the state
|
233
|
+
# will still be "assigned" and be skipped by "_action_assign",
|
234
|
+
# recompute the state to be "partially_available"
|
235
|
+
self.move_id._recompute_state()
|
289
236
|
|
290
237
|
# if the new package has less quantities, assign will create new move
|
291
238
|
# lines
|
@@ -312,9 +259,9 @@ class StockMoveLine(models.Model):
|
|
312
259
|
def shopfloor_postpone(self, *recordsets):
|
313
260
|
"""
|
314
261
|
Specific behavior for move lines.
|
315
|
-
As we need to reset
|
262
|
+
As we need to reset the quantity picked.
|
316
263
|
|
317
264
|
"""
|
318
265
|
res = super().shopfloor_postpone(*recordsets)
|
319
|
-
self.
|
266
|
+
self.picked = False
|
320
267
|
return res
|
@@ -26,7 +26,9 @@ class StockPicking(models.Model):
|
|
26
26
|
is_shopfloor_created = fields.Boolean()
|
27
27
|
|
28
28
|
@api.depends(
|
29
|
-
"move_line_ids",
|
29
|
+
"move_line_ids",
|
30
|
+
"move_line_ids.quantity_product_uom",
|
31
|
+
"move_line_ids.product_id.weight",
|
30
32
|
)
|
31
33
|
def _compute_picking_info(self):
|
32
34
|
for item in self:
|
@@ -47,7 +49,7 @@ class StockPicking(models.Model):
|
|
47
49
|
def _calc_weight(self):
|
48
50
|
weight = 0.0
|
49
51
|
for move_line in self.mapped("move_line_ids"):
|
50
|
-
weight += move_line.
|
52
|
+
weight += move_line.quantity_product_uom * move_line.product_id.weight
|
51
53
|
return weight
|
52
54
|
|
53
55
|
def _check_move_lines_map_quant_package(self, package):
|
@@ -55,9 +57,9 @@ class StockPicking(models.Model):
|
|
55
57
|
pack_move_lines = self.move_line_ids.filtered(
|
56
58
|
lambda ml: ml.package_id == package
|
57
59
|
)
|
58
|
-
# if we
|
60
|
+
# if we flag a line as picked, we don't want
|
59
61
|
# to change it in any case, so we ignore the package level
|
60
|
-
if any(pack_move_lines.mapped("
|
62
|
+
if any(pack_move_lines.mapped("picked")):
|
61
63
|
return False
|
62
64
|
# if we already changed the destination package, do not create
|
63
65
|
# a new package level
|
@@ -119,13 +121,13 @@ class StockPicking(models.Model):
|
|
119
121
|
assigned_moves._action_assign()
|
120
122
|
return new_picking.id
|
121
123
|
|
122
|
-
def _put_in_pack(self, move_line_ids
|
124
|
+
def _put_in_pack(self, move_line_ids):
|
123
125
|
"""
|
124
126
|
Marks the corresponding move lines as 'shopfloor_checkout_done'
|
125
127
|
when the package is created in the backend.
|
126
128
|
|
127
129
|
"""
|
128
|
-
new_package = super()._put_in_pack(move_line_ids
|
130
|
+
new_package = super()._put_in_pack(move_line_ids)
|
129
131
|
lines = move_line_ids.filtered(lambda p: p.result_package_id == new_package)
|
130
132
|
lines.write({"shopfloor_checkout_done": True})
|
131
133
|
return new_package
|
@@ -41,11 +41,11 @@ class StockQuantPackage(models.Model):
|
|
41
41
|
for rec in self:
|
42
42
|
rec.update({"reserved_move_line_ids": rec._get_reserved_move_lines()})
|
43
43
|
|
44
|
-
@api.depends("pack_weight", "
|
44
|
+
@api.depends("pack_weight", "weight")
|
45
45
|
@api.depends_context("picking_id")
|
46
46
|
def _compute_shopfloor_weight(self):
|
47
47
|
for rec in self:
|
48
|
-
rec.shopfloor_weight = rec.pack_weight or rec.
|
48
|
+
rec.shopfloor_weight = rec.pack_weight or rec.weight
|
49
49
|
|
50
50
|
# TODO: we should refactor this like
|
51
51
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
- Guewen Baconnier \<<guewen.baconnier@camptocamp.com>\>
|
2
|
+
- Simone Orsi \<<simahawk@gmail.com>\>
|
3
|
+
- Sébastien Alix \<<sebastien.alix@camptocamp.com>\>
|
4
|
+
- Alexandre Fayolle \<<alexandre.fayolle@camptocamp.com>\>
|
5
|
+
- Benoit Guillot \<<benoit.guillot@akretion.com>\>
|
6
|
+
- Thierry Ducrest \<<thierry.ducrest@camptocamp.com>\>
|
7
|
+
- Raphaël Reverdy \<<raphael.reverdy@akretion.com>\>
|
8
|
+
- Jacques-Etienne Baudoux \<<je@bcim.be>\>
|
9
|
+
- Juan Miguel Sánchez Arce \<<juan.sanchez@camptocamp.com>\>
|
10
|
+
- Michael Tietz (MT Software) \<<mtietz@mt-software.de>\>
|
11
|
+
- Souheil Bejaoui \<<souheil.bejaoui@acsone.eu>\>
|
12
|
+
- Laurent Mignon \<<laurent.mignon@acsone.eu>\>
|
13
|
+
|
14
|
+
## Design
|
15
|
+
|
16
|
+
- Joël Grand-Guillaume \<<joel.grandguillaume@camptocamp.com>\>
|
17
|
+
- Jacques-Etienne Baudoux \<<je@bcim.be>\>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Shopfloor is a barcode scanner application for internal warehouse
|
2
|
+
operations.
|
3
|
+
|
4
|
+
The application supports scenarios, to relate to Operation Types:
|
5
|
+
|
6
|
+
- Cluster Picking
|
7
|
+
- Zone Picking
|
8
|
+
- Checkout/Packing
|
9
|
+
- Delivery
|
10
|
+
- Location Content Transfer
|
11
|
+
- Single Pack Transfer
|
12
|
+
|
13
|
+
This module provides REST APIs to support the scenarios. It needs a
|
14
|
+
frontend to consume the backend APIs and provide screens for users on
|
15
|
+
barcode devices. A default front-end application is provided by
|
16
|
+
`shopfloor_mobile`.
|
17
|
+
|
18
|
+
Note: if you want to enable a new scenario on an existing application,
|
19
|
+
you must trigger the registry sync on the shopfloor.app in a
|
20
|
+
post_init_hook or a post-migrate script.
|
21
|
+
See an example
|
22
|
+
[here](https://github.com/OCA/wms/pull/520/commits/bccdfd445a9bc943998c4848f183a076e8459a98).
|
23
|
+
|
24
|
+
|
25
|
+
**WARNING v18**
|
26
|
+
|
27
|
+
Scenario will be migrated one by one.
|
28
|
+
|
29
|
+
Current availability status:
|
30
|
+
|
31
|
+
- [ ] checkout
|
32
|
+
- [ ] cluster picking
|
33
|
+
- [ ] zone picking
|
34
|
+
- [ ] delivery
|
35
|
+
- [ ] location content transfer
|
36
|
+
- [ ] single pack transfer (to be discarded in favor of shopfloor_single_product_transfer)
|
37
|
+
- [ ] zone picking
|
@@ -0,0 +1,10 @@
|
|
1
|
+
- improve documentation
|
2
|
+
- split out scenario components to their own modules
|
3
|
+
- maybe split common stock features to shopfloor_stock_base and move
|
4
|
+
scenario to shopfloor_wms?
|
5
|
+
|
6
|
+
|
7
|
+
**WARNING v18**
|
8
|
+
|
9
|
+
We don't want to split now the scenario to keep it as much as possible in line w/ v16.
|
10
|
+
We'll do it later.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
An API key is created in the Demo data (for development), using the Demo
|
2
|
+
user. The key to use in the HTTP header `API-KEY` is: 72B044F7AC780DAC
|
3
|
+
|
4
|
+
Curl example:
|
5
|
+
|
6
|
+
curl -X POST "http://localhost:8069/shopfloor/user/menu" -H "accept: */*" -H "Content-Type: application/json" -H "API-KEY: 72B044F7AC780DAC"
|
@@ -303,7 +303,7 @@ class Checkout(Component):
|
|
303
303
|
# The picking should have a move line for the product
|
304
304
|
# where qty >= packaging.qty, since it doesn't makes sense
|
305
305
|
# to select a move line which have less qty than the packaging
|
306
|
-
line_domain = [("
|
306
|
+
line_domain = [("quantity", ">=", packaging.qty)]
|
307
307
|
return self._select_document_from_product(product, line_domain=line_domain)
|
308
308
|
|
309
309
|
def _select_document_from_none(self, *args, barcode=None, **kwargs):
|
@@ -428,7 +428,7 @@ class Checkout(Component):
|
|
428
428
|
# For prefill quantity we only want to increment one line
|
429
429
|
line.qty_done += prefill_qty
|
430
430
|
elif not self.work.menu.no_prefill_qty:
|
431
|
-
line.qty_done = line.
|
431
|
+
line.qty_done = line.quantity
|
432
432
|
line.shopfloor_user_id = self.env.user
|
433
433
|
|
434
434
|
picking = lines.mapped("picking_id")
|
@@ -845,7 +845,7 @@ class Checkout(Component):
|
|
845
845
|
move_line.qty_done = qty_done
|
846
846
|
if new_line:
|
847
847
|
selected_line_ids.append(new_line.id)
|
848
|
-
if qty_done > move_line.
|
848
|
+
if qty_done > move_line.quantity:
|
849
849
|
return self._response_for_select_package(
|
850
850
|
picking,
|
851
851
|
self.env["stock.move.line"].browse(selected_line_ids).exists(),
|
@@ -884,7 +884,7 @@ class Checkout(Component):
|
|
884
884
|
as selected
|
885
885
|
"""
|
886
886
|
return self._change_line_qty(
|
887
|
-
picking_id, selected_line_ids, [move_line_id], lambda x: x.
|
887
|
+
picking_id, selected_line_ids, [move_line_id], lambda x: x.quantity
|
888
888
|
)
|
889
889
|
|
890
890
|
def set_custom_qty(self, picking_id, selected_line_ids, move_line_id, qty_done):
|
@@ -917,7 +917,7 @@ class Checkout(Component):
|
|
917
917
|
picking.id,
|
918
918
|
selected_lines.ids,
|
919
919
|
switch_lines.ids,
|
920
|
-
lambda x: x.
|
920
|
+
lambda x: x.quantity,
|
921
921
|
)
|
922
922
|
|
923
923
|
def _increment_custom_qty(
|
@@ -983,7 +983,7 @@ class Checkout(Component):
|
|
983
983
|
|
984
984
|
def _put_lines_in_allowed_package(self, picking, lines_to_pack, package):
|
985
985
|
for line in lines_to_pack:
|
986
|
-
if line.qty_done < line.
|
986
|
+
if line.qty_done < line.quantity:
|
987
987
|
line._split_partial_quantity_to_be_done(line.qty_done, {})
|
988
988
|
lines_to_pack.write(
|
989
989
|
{"result_package_id": package.id, "shopfloor_checkout_done": True}
|
@@ -1104,7 +1104,7 @@ class Checkout(Component):
|
|
1104
1104
|
If none are found, return the first line for that product.
|
1105
1105
|
"""
|
1106
1106
|
return next(
|
1107
|
-
(line for line in product_lines if line.qty_done < line.
|
1107
|
+
(line for line in product_lines if line.qty_done < line.quantity),
|
1108
1108
|
fields.first(product_lines),
|
1109
1109
|
)
|
1110
1110
|
|
@@ -1308,7 +1308,7 @@ class Checkout(Component):
|
|
1308
1308
|
# Do not allow to proceed if the qty_done of
|
1309
1309
|
# any of the selected lines
|
1310
1310
|
# is higher than the quantity to do.
|
1311
|
-
if line.qty_done > line.
|
1311
|
+
if line.qty_done > line.quantity:
|
1312
1312
|
return self._response_for_select_package(
|
1313
1313
|
picking,
|
1314
1314
|
lines,
|
@@ -1519,7 +1519,7 @@ class Checkout(Component):
|
|
1519
1519
|
return self._response_for_select_document(message=message)
|
1520
1520
|
lines = picking.move_line_ids
|
1521
1521
|
if not confirmation:
|
1522
|
-
if not all(line.qty_done == line.
|
1522
|
+
if not all(line.qty_done == line.quantity for line in lines):
|
1523
1523
|
return self._response_for_summary(
|
1524
1524
|
picking,
|
1525
1525
|
need_confirm=True,
|
@@ -508,7 +508,7 @@ class ClusterPicking(Component):
|
|
508
508
|
"""Returns the quantity to increment depending on no_prefill_qty optione."""
|
509
509
|
if self.work.menu.no_prefill_qty:
|
510
510
|
return qty
|
511
|
-
return move_line.
|
511
|
+
return move_line.quantity
|
512
512
|
|
513
513
|
def _check_first_scan_location_or_pack_first(
|
514
514
|
self, move_line, sublocation=None, location_scanned=False
|
@@ -762,7 +762,7 @@ class ClusterPicking(Component):
|
|
762
762
|
if qty_check == "greater":
|
763
763
|
return self._response_for_scan_destination(
|
764
764
|
move_line,
|
765
|
-
message=self.msg_store.unable_to_pick_more(move_line.
|
765
|
+
message=self.msg_store.unable_to_pick_more(move_line.quantity),
|
766
766
|
qty_done=quantity,
|
767
767
|
)
|
768
768
|
|
@@ -221,7 +221,7 @@ class Delivery(Component):
|
|
221
221
|
for line in lines:
|
222
222
|
# note: the package level is automatically set to "is_done" when
|
223
223
|
# the qty_done is full
|
224
|
-
line.qty_done = line.
|
224
|
+
line.qty_done = line.quantity
|
225
225
|
picking = fields.first(lines.mapped("picking_id"))
|
226
226
|
return self._action_picking_done(picking, force=allow_prepackaged_product)
|
227
227
|
|
@@ -288,7 +288,7 @@ class Delivery(Component):
|
|
288
288
|
if product_qty:
|
289
289
|
domain.extend(
|
290
290
|
[
|
291
|
-
("
|
291
|
+
("quantity_product_uom", ">=", product_qty),
|
292
292
|
]
|
293
293
|
)
|
294
294
|
if picking:
|
@@ -317,7 +317,7 @@ class Delivery(Component):
|
|
317
317
|
if product_qty:
|
318
318
|
domain.extend(
|
319
319
|
[
|
320
|
-
("
|
320
|
+
("quantity_product_uom", ">=", product_qty),
|
321
321
|
]
|
322
322
|
)
|
323
323
|
if picking:
|
@@ -420,9 +420,7 @@ class Delivery(Component):
|
|
420
420
|
message=self.msg_store.product_not_unitary_in_package_scan_package(),
|
421
421
|
)
|
422
422
|
# We focus only on lines on which we can increase the 'qty_done'
|
423
|
-
lines = lines.filtered(
|
424
|
-
lambda x: (x.qty_done + product_qty) <= x.reserved_uom_qty
|
425
|
-
)
|
423
|
+
lines = lines.filtered(lambda x: (x.qty_done + product_qty) <= x.quantity)
|
426
424
|
# Filter lines to keep only ones from one delivery operation
|
427
425
|
# (we do not want to process lines of another delivery operation)
|
428
426
|
lines = lines._filter_on_picking(picking)
|
@@ -34,7 +34,10 @@ class ShopfloorPickingForm(Component):
|
|
34
34
|
company_carriers = self.env["delivery.carrier"].search(
|
35
35
|
["|", ("company_id", "=", False), ("company_id", "=", record.company_id.id)]
|
36
36
|
)
|
37
|
-
|
37
|
+
# TODO: this imply a dependency on sale_stock
|
38
|
+
available_carriers = company_carriers.available_carriers(
|
39
|
+
record.partner_id, record.sale_id
|
40
|
+
)
|
38
41
|
return available_carriers
|
39
42
|
|
40
43
|
|
@@ -770,7 +770,7 @@ class LocationContentTransfer(Component):
|
|
770
770
|
backorders = stock.validate_moves(move_line.move_id)
|
771
771
|
if backorders:
|
772
772
|
for move_line in backorders.mapped("move_line_ids"):
|
773
|
-
move_line.qty_done = move_line.
|
773
|
+
move_line.qty_done = move_line.quantity
|
774
774
|
backorders.user_id = self.env.user
|
775
775
|
# process first backorder of current line
|
776
776
|
move_lines = backorders.move_line_ids
|
@@ -31,7 +31,7 @@ class SinglePackTransfer(Component):
|
|
31
31
|
"name": package.name,
|
32
32
|
"weight_uom": package.weight_uom_id.name,
|
33
33
|
"weight": package.pack_weight,
|
34
|
-
"estimated_weight_kg": package.
|
34
|
+
"estimated_weight_kg": package.shopfloor_weight,
|
35
35
|
"location_src": self.data.location(package.location_id),
|
36
36
|
"location_dest": self.data.location(package_level.location_dest_id),
|
37
37
|
"products": self.data.products(move_lines.product_id),
|