odoo-addon-stock-barcodes 15.0.3.1.6__py3-none-any.whl → 15.0.3.2.0.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/stock_barcodes/README.rst +1 -1
- odoo/addons/stock_barcodes/__manifest__.py +1 -1
- odoo/addons/stock_barcodes/hooks.py +15 -13
- odoo/addons/stock_barcodes/i18n/es.po +64 -124
- odoo/addons/stock_barcodes/i18n/it.po +61 -8
- odoo/addons/stock_barcodes/i18n/stock_barcodes.pot +55 -2
- odoo/addons/stock_barcodes/models/__init__.py +1 -0
- odoo/addons/stock_barcodes/models/stock_barcodes_action.py +1 -1
- odoo/addons/stock_barcodes/models/stock_move.py +37 -0
- odoo/addons/stock_barcodes/models/stock_move_line.py +6 -12
- odoo/addons/stock_barcodes/models/stock_picking.py +9 -36
- odoo/addons/stock_barcodes/models/stock_picking_type.py +5 -6
- odoo/addons/stock_barcodes/static/description/index.html +1 -1
- odoo/addons/stock_barcodes/tests/test_stock_barcodes_picking.py +1 -0
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read.py +3 -20
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking.py +230 -24
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +12 -1
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo.py +42 -105
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml +45 -3
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_views.xml +0 -1
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-15.0.3.2.0.1.dist-info}/METADATA +4 -4
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-15.0.3.2.0.1.dist-info}/RECORD +24 -23
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-15.0.3.2.0.1.dist-info}/WHEEL +1 -1
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-15.0.3.2.0.1.dist-info}/top_level.txt +0 -0
@@ -43,7 +43,7 @@ class StockBarcodesAction(models.Model):
|
|
43
43
|
}
|
44
44
|
if option_group.get_option_value("location_id", "filled_default"):
|
45
45
|
vals["location_id"] = (
|
46
|
-
self.env["stock.warehouse"].search([])
|
46
|
+
self.env["stock.warehouse"].search([], limit=1).lot_stock_id.id
|
47
47
|
)
|
48
48
|
wiz = self.env["wiz.stock.barcodes.read.inventory"].create(vals)
|
49
49
|
action = self.env["ir.actions.actions"]._for_xml_id(
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright 2024 Tecnativa - Sergio Teruel
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from odoo import fields, models
|
5
|
+
|
6
|
+
|
7
|
+
class StockMove(models.Model):
|
8
|
+
_inherit = "stock.move"
|
9
|
+
|
10
|
+
barcode_backorder_action = fields.Selection(
|
11
|
+
[
|
12
|
+
("pending", "Pending"),
|
13
|
+
("create_backorder", "Create Backorder"),
|
14
|
+
("skip_backorder", "No Backorder"),
|
15
|
+
],
|
16
|
+
string="Backorder action",
|
17
|
+
default="pending",
|
18
|
+
)
|
19
|
+
|
20
|
+
def _action_done(self, cancel_backorder=False):
|
21
|
+
moves_cancel_backorder = self.browse()
|
22
|
+
if not cancel_backorder:
|
23
|
+
moves_cancel_backorder = self.filtered(
|
24
|
+
lambda sm: sm.barcode_backorder_action == "skip_backorder"
|
25
|
+
)
|
26
|
+
super(StockMove, moves_cancel_backorder)._action_done(cancel_backorder=True)
|
27
|
+
moves_backorder = self - moves_cancel_backorder
|
28
|
+
moves_backorder.barcode_backorder_action = "pending"
|
29
|
+
return super(StockMove, moves_backorder)._action_done(
|
30
|
+
cancel_backorder=cancel_backorder
|
31
|
+
)
|
32
|
+
|
33
|
+
def copy_data(self, default=None):
|
34
|
+
vals_list = super().copy_data(default=default)
|
35
|
+
for vals in vals_list:
|
36
|
+
vals.pop("barcode_backorder_action", None)
|
37
|
+
return vals_list
|
@@ -28,19 +28,13 @@ class StockMoveLine(models.Model):
|
|
28
28
|
|
29
29
|
def action_barcode_detailed_operation_unlink(self):
|
30
30
|
for sml in self:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
sml.unlink()
|
31
|
+
stock_move = sml.move_id
|
32
|
+
stock_move.barcode_backorder_action = "pending"
|
33
|
+
sml.unlink()
|
35
34
|
# HACK: To force refresh wizard values
|
36
35
|
wiz_barcode = self.env["wiz.stock.barcodes.read.picking"].browse(
|
37
36
|
self.env.context.get("wiz_barcode_id", False)
|
38
37
|
)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
wiz_barcode.fill_todo_records()
|
43
|
-
wiz_barcode.determine_todo_action()
|
44
|
-
else:
|
45
|
-
wiz_barcode.fill_todo_records()
|
46
|
-
wiz_barcode.todo_line_id.line_ids = wiz_barcode.todo_line_id.line_ids
|
38
|
+
stock_move._action_assign()
|
39
|
+
wiz_barcode.fill_todo_records()
|
40
|
+
wiz_barcode.determine_todo_action()
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
2
2
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
3
|
from odoo import models
|
4
|
-
from odoo.tools.float_utils import float_compare
|
5
4
|
|
6
5
|
|
7
6
|
class StockPicking(models.Model):
|
@@ -19,7 +18,7 @@ class StockPicking(models.Model):
|
|
19
18
|
}
|
20
19
|
if self.picking_type_id.code == "outgoing":
|
21
20
|
vals["location_dest_id"] = self.location_dest_id.id
|
22
|
-
|
21
|
+
elif self.picking_type_id.code == "incoming":
|
23
22
|
vals["location_id"] = self.location_id.id
|
24
23
|
|
25
24
|
if option_group.get_option_value("location_id", "filled_default"):
|
@@ -33,8 +32,8 @@ class StockPicking(models.Model):
|
|
33
32
|
wiz = self.env["wiz.stock.barcodes.read.picking"].create(
|
34
33
|
self._prepare_barcode_wiz_vals(option_group)
|
35
34
|
)
|
36
|
-
wiz.determine_todo_action()
|
37
35
|
wiz.fill_pending_moves()
|
36
|
+
wiz.determine_todo_action()
|
38
37
|
action = self.env["ir.actions.actions"]._for_xml_id(
|
39
38
|
"stock_barcodes.action_stock_barcodes_read_picking"
|
40
39
|
)
|
@@ -42,41 +41,15 @@ class StockPicking(models.Model):
|
|
42
41
|
return action
|
43
42
|
|
44
43
|
def button_validate(self):
|
45
|
-
|
46
|
-
|
47
|
-
and not
|
48
|
-
)
|
49
|
-
|
50
|
-
|
51
|
-
# Variable initialized as True to optimize break loop
|
52
|
-
skip_backorder = True
|
44
|
+
put_in_pack_picks = self.filtered(
|
45
|
+
lambda p: p.picking_type_id.barcode_option_group_id.auto_put_in_pack
|
46
|
+
and not p.move_line_ids.result_package_id
|
47
|
+
)
|
48
|
+
if put_in_pack_picks:
|
49
|
+
put_in_pack_picks.action_put_in_pack()
|
53
50
|
if self.env.context.get("stock_barcodes_validate_picking", False):
|
54
|
-
# Avoid backorder when all move lines are processed (done or done_forced)
|
55
|
-
prec = self.env["decimal.precision"].precision_get(
|
56
|
-
"Product Unit of Measure"
|
57
|
-
)
|
58
|
-
for move in self.move_lines.filtered(lambda sm: sm.state != "cancel"):
|
59
|
-
if (
|
60
|
-
float_compare(
|
61
|
-
move.quantity_done, move.product_uom_qty, precision_digits=prec
|
62
|
-
)
|
63
|
-
< 0
|
64
|
-
):
|
65
|
-
# In normal conditions backorder will be created
|
66
|
-
create_backorder = True
|
67
|
-
if not move.move_line_ids or any(
|
68
|
-
sml.barcode_scan_state in ["pending"]
|
69
|
-
for sml in move.move_line_ids
|
70
|
-
):
|
71
|
-
# If any move are not processed we can not skip backorder
|
72
|
-
skip_backorder = False
|
73
|
-
break
|
74
|
-
if create_backorder and skip_backorder:
|
75
51
|
res = super(
|
76
|
-
StockPicking,
|
77
|
-
self.with_context(
|
78
|
-
picking_ids_not_to_backorder=self.ids, skip_backorder=True
|
79
|
-
),
|
52
|
+
StockPicking, self.with_context(skip_backorder=True)
|
80
53
|
).button_validate()
|
81
54
|
else:
|
82
55
|
res = super().button_validate()
|
@@ -26,17 +26,15 @@ class StockPickingType(models.Model):
|
|
26
26
|
"picking_mode": "picking",
|
27
27
|
}
|
28
28
|
if self.code == "outgoing":
|
29
|
-
location_dest_id = (
|
29
|
+
vals["location_dest_id"] = (
|
30
30
|
self.default_location_dest_id.id
|
31
31
|
or self.env.ref("stock.stock_location_customers").id
|
32
32
|
)
|
33
|
-
|
34
|
-
|
35
|
-
location_src_id = (
|
33
|
+
elif self.code == "incoming":
|
34
|
+
vals["location_id"] = (
|
36
35
|
self.default_location_src_id.id
|
37
36
|
or self.env.ref("stock.stock_location_suppliers").id
|
38
37
|
)
|
39
|
-
vals["location_id"] = location_src_id
|
40
38
|
if self.barcode_option_group_id.get_option_value(
|
41
39
|
"location_id", "filled_default"
|
42
40
|
):
|
@@ -46,8 +44,8 @@ class StockPickingType(models.Model):
|
|
46
44
|
):
|
47
45
|
vals["location_dest_id"] = self.default_location_dest_id.id
|
48
46
|
wiz = self.env["wiz.stock.barcodes.read.picking"].create(vals)
|
49
|
-
wiz.determine_todo_action()
|
50
47
|
wiz.fill_pending_moves()
|
48
|
+
wiz.determine_todo_action()
|
51
49
|
action = self.env["ir.actions.actions"]._for_xml_id(
|
52
50
|
"stock_barcodes.action_stock_barcodes_read_picking"
|
53
51
|
)
|
@@ -55,6 +53,7 @@ class StockPickingType(models.Model):
|
|
55
53
|
return action
|
56
54
|
|
57
55
|
def action_barcode_new_picking(self):
|
56
|
+
self.ensure_one()
|
58
57
|
picking = (
|
59
58
|
self.env["stock.picking"]
|
60
59
|
.with_context(default_immediate_transfer=True)
|
@@ -367,7 +367,7 @@ ul.auto-toc {
|
|
367
367
|
!! This file is generated by oca-gen-addon-readme !!
|
368
368
|
!! changes will be overwritten. !!
|
369
369
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
370
|
-
!! source digest: sha256:
|
370
|
+
!! source digest: sha256:aaf3270f55f43b616a2ceb165378f0909ba3cc1a3d0d36d21a65e4c04c06998f
|
371
371
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
372
372
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/stock-logistics-barcode/tree/15.0/stock_barcodes"><img alt="OCA/stock-logistics-barcode" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--barcode-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/stock-logistics-barcode-15-0/stock-logistics-barcode-15-0-stock_barcodes"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-barcode&target_branch=15.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
373
373
|
<p>This module provides a barcode reader interface for stock module.</p>
|
@@ -294,6 +294,7 @@ class TestStockBarcodesPicking(TestStockBarcodes):
|
|
294
294
|
self.product_tracking.categ_id.removal_strategy_id = self.env.ref(
|
295
295
|
"stock.removal_lifo"
|
296
296
|
)
|
297
|
+
self.wiz_scan_picking_out.action_clean_values()
|
297
298
|
self.action_barcode_scanned(self.wiz_scan_picking_out, "8433281006850")
|
298
299
|
self.assertEqual(self.wiz_scan_picking_out.lot_id, lot_3)
|
299
300
|
|
@@ -76,7 +76,6 @@ class WizStockBarcodesRead(models.AbstractModel):
|
|
76
76
|
show_scan_log = fields.Boolean(compute="_compute_is_manual_qty")
|
77
77
|
# Technical field to allow use in attrs
|
78
78
|
display_menu = fields.Boolean()
|
79
|
-
qty_available = fields.Float(compute="_compute_qty_available")
|
80
79
|
auto_lot = fields.Boolean(
|
81
80
|
string="Get lots automatically",
|
82
81
|
help="If checked the lot will be set automatically with the same "
|
@@ -122,24 +121,6 @@ class WizStockBarcodesRead(models.AbstractModel):
|
|
122
121
|
for rec in self:
|
123
122
|
rec.create_lot = rec.option_group_id.create_lot
|
124
123
|
|
125
|
-
@api.depends("location_id", "product_id", "lot_id")
|
126
|
-
def _compute_qty_available(self):
|
127
|
-
if not self.product_id or self.location_id.usage != "internal":
|
128
|
-
self.qty_available = 0.0
|
129
|
-
return
|
130
|
-
domain_quant = [
|
131
|
-
("product_id", "=", self.product_id.id),
|
132
|
-
("location_id", "=", self.location_id.id),
|
133
|
-
]
|
134
|
-
if self.lot_id:
|
135
|
-
domain_quant.append(("lot_id", "=", self.lot_id.id))
|
136
|
-
# if self.package_id:
|
137
|
-
# domain_quant.append(('package_id', '=', self.package_id.id))
|
138
|
-
groups = self.env["stock.quant"].read_group(
|
139
|
-
domain_quant, ["quantity"], [], orderby="id"
|
140
|
-
)
|
141
|
-
self.qty_available = groups[0]["quantity"]
|
142
|
-
|
143
124
|
@api.depends("product_id")
|
144
125
|
def _compute_display_assign_serial(self):
|
145
126
|
for rec in self:
|
@@ -630,7 +611,9 @@ class WizStockBarcodesRead(models.AbstractModel):
|
|
630
611
|
|
631
612
|
def action_clean_values(self):
|
632
613
|
options = self.option_group_id.option_ids
|
633
|
-
options_to_clean = options.filtered(
|
614
|
+
options_to_clean = options.filtered(
|
615
|
+
lambda op: op.clean_after_done and op.field_name in self
|
616
|
+
)
|
634
617
|
for option in options_to_clean:
|
635
618
|
if option.field_name == "result_package_id" and self.keep_result_package:
|
636
619
|
continue
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
|
2
2
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
3
|
import logging
|
4
|
+
from collections import OrderedDict, defaultdict
|
4
5
|
|
5
6
|
from odoo import _, api, fields, models
|
6
7
|
from odoo.exceptions import ValidationError
|
7
8
|
from odoo.fields import first
|
8
|
-
from odoo.tools.float_utils import float_compare
|
9
|
+
from odoo.tools.float_utils import float_compare, float_round
|
10
|
+
from odoo.tools.safe_eval import safe_eval
|
9
11
|
|
10
12
|
_logger = logging.getLogger(__name__)
|
11
13
|
|
@@ -74,6 +76,9 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
74
76
|
picking_location_id = fields.Many2one(related="picking_id.location_id")
|
75
77
|
picking_location_dest_id = fields.Many2one(related="picking_id.location_dest_id")
|
76
78
|
company_id = fields.Many2one(related="picking_id.company_id")
|
79
|
+
todo_line_is_extra_line = fields.Boolean(related="todo_line_id.is_extra_line")
|
80
|
+
forced_todo_key = fields.Char()
|
81
|
+
qty_available = fields.Float(compute="_compute_qty_available")
|
77
82
|
|
78
83
|
@api.depends("todo_line_id")
|
79
84
|
def _compute_todo_line_display_ids(self):
|
@@ -85,6 +90,9 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
85
90
|
if self.option_group_id.show_pending_moves:
|
86
91
|
self.pending_move_ids = self.todo_line_ids.filtered(
|
87
92
|
lambda t: t.state == "pending"
|
93
|
+
and any(
|
94
|
+
sm.barcode_backorder_action == "pending" for sm in t.stock_move_ids
|
95
|
+
)
|
88
96
|
)
|
89
97
|
else:
|
90
98
|
self.pending_move_ids = False
|
@@ -108,6 +116,40 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
108
116
|
rec.total_product_uom_qty += line.product_uom_qty
|
109
117
|
rec.total_product_qty_done += line.quantity_done
|
110
118
|
|
119
|
+
@api.depends("location_id", "product_id", "lot_id")
|
120
|
+
def _compute_qty_available(self):
|
121
|
+
if not self.product_id or self.location_id.usage != "internal":
|
122
|
+
self.qty_available = 0.0
|
123
|
+
return
|
124
|
+
domain_quant = [
|
125
|
+
("product_id", "=", self.product_id.id),
|
126
|
+
("location_id", "=", self.location_id.id),
|
127
|
+
]
|
128
|
+
if self.lot_id:
|
129
|
+
domain_quant.append(("lot_id", "=", self.lot_id.id))
|
130
|
+
# if self.package_id:
|
131
|
+
# domain_quant.append(('package_id', '=', self.package_id.id))
|
132
|
+
groups = self.env["stock.quant"].read_group(
|
133
|
+
domain_quant, ["quantity"], [], orderby="id"
|
134
|
+
)
|
135
|
+
self.qty_available = groups[0]["quantity"]
|
136
|
+
# Unexpected done quantities must reduce qty_available
|
137
|
+
if self.lot_id:
|
138
|
+
done_move_lines = self.move_line_ids.filtered(
|
139
|
+
lambda m: m.product_id == self.product_id and m.lot_id == self.lot_id
|
140
|
+
)
|
141
|
+
else:
|
142
|
+
done_move_lines = self.move_line_ids.filtered(
|
143
|
+
lambda m: m.product_id == self.product_id
|
144
|
+
)
|
145
|
+
for sml in done_move_lines:
|
146
|
+
over_done_qty = float_round(
|
147
|
+
sml.qty_done - sml.product_uom_qty,
|
148
|
+
precision_rounding=sml.product_uom_id.rounding,
|
149
|
+
)
|
150
|
+
if over_done_qty > 0.0:
|
151
|
+
self.qty_available -= over_done_qty
|
152
|
+
|
111
153
|
def name_get(self):
|
112
154
|
return [
|
113
155
|
(
|
@@ -141,8 +183,8 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
141
183
|
# view, so for create a candidate picking with the same default picking
|
142
184
|
# we need create it in this onchange
|
143
185
|
self._set_default_picking()
|
144
|
-
self.determine_todo_action()
|
145
186
|
self.fill_pending_moves()
|
187
|
+
self.determine_todo_action()
|
146
188
|
|
147
189
|
def get_sorted_move_lines(self, move_lines):
|
148
190
|
location_field = self.option_group_id.location_field_to_sort
|
@@ -180,12 +222,8 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
180
222
|
return move_lines
|
181
223
|
|
182
224
|
def fill_pending_moves(self):
|
183
|
-
|
184
|
-
|
185
|
-
and self.option_group_id.show_pending_moves
|
186
|
-
and not self.todo_line_ids
|
187
|
-
):
|
188
|
-
self.fill_todo_records()
|
225
|
+
# TODO: Unify method
|
226
|
+
self.fill_todo_records()
|
189
227
|
|
190
228
|
def get_moves_or_move_lines(self):
|
191
229
|
if self.option_group_id.source_pending_moves == "move_line_ids":
|
@@ -193,9 +231,12 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
193
231
|
else:
|
194
232
|
return self.picking_id.move_lines
|
195
233
|
|
234
|
+
def get_moves(self):
|
235
|
+
return self.picking_id.move_lines
|
236
|
+
|
196
237
|
def fill_todo_records(self):
|
197
238
|
move_lines = self.get_sorted_move_lines(self.get_moves_or_move_lines())
|
198
|
-
self.
|
239
|
+
self.fill_records([move_lines])
|
199
240
|
|
200
241
|
@api.model
|
201
242
|
def _get_fields_filled_special(self):
|
@@ -211,12 +252,6 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
211
252
|
self.visible_force_done = self.env.context.get("visible_force_done", False)
|
212
253
|
if not self.option_group_id.barcode_guided_mode == "guided":
|
213
254
|
return False
|
214
|
-
if not self.todo_line_ids:
|
215
|
-
self.fill_todo_records()
|
216
|
-
# When scanning all information in one step (e.g. using GS-1), the
|
217
|
-
# status and qty processed might have not been update, we ensure it
|
218
|
-
# invalidating the cache.
|
219
|
-
self.todo_line_ids.invalidate_cache()
|
220
255
|
self.todo_line_id = (
|
221
256
|
forced_todo_line
|
222
257
|
or self.todo_line_ids.filtered(lambda t: t._origin.state == "pending")[:1]
|
@@ -278,9 +313,24 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
278
313
|
if not self.keep_screen_values or self.todo_line_id.state != "pending":
|
279
314
|
if not self.env.context.get("skip_clean_values", False):
|
280
315
|
self.action_clean_values()
|
281
|
-
|
316
|
+
keep_vals = {}
|
282
317
|
else:
|
283
|
-
self.
|
318
|
+
keep_vals = self._convert_to_write(self._cache)
|
319
|
+
self.fill_todo_records()
|
320
|
+
if self.forced_todo_key:
|
321
|
+
self.todo_line_id = self.pending_move_ids.filtered(
|
322
|
+
lambda ln: str(self._group_key(ln)) == self.forced_todo_key
|
323
|
+
)[:1]
|
324
|
+
self.selected_pending_move_id = self.todo_line_id
|
325
|
+
self.determine_todo_action(self.todo_line_id)
|
326
|
+
else:
|
327
|
+
self.determine_todo_action()
|
328
|
+
self.action_show_step()
|
329
|
+
if keep_vals:
|
330
|
+
self.update_keep_values(keep_vals)
|
331
|
+
# Force refresh candidate pickings to show green if not pending moves
|
332
|
+
if not self.pending_move_ids:
|
333
|
+
self._set_candidate_pickings(self.picking_id)
|
284
334
|
# Now we can add read log with details.
|
285
335
|
_logger.info("Add scanned log barcode:{}".format(self.barcode))
|
286
336
|
self._add_read_log(log_detail=move_dic)
|
@@ -290,6 +340,13 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
290
340
|
self._add_read_log()
|
291
341
|
return res
|
292
342
|
|
343
|
+
def update_keep_values(self, keep_vals):
|
344
|
+
options = self.option_group_id.option_ids
|
345
|
+
fields_to_keep = options.filtered(
|
346
|
+
lambda op: self._fields[op.field_name].type != "float"
|
347
|
+
).mapped("field_name")
|
348
|
+
self.update({f_name: keep_vals[f_name] for f_name in fields_to_keep})
|
349
|
+
|
293
350
|
def action_manual_entry(self):
|
294
351
|
result = super().action_manual_entry()
|
295
352
|
if result:
|
@@ -456,6 +513,10 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
456
513
|
domain = self._prepare_stock_moves_domain()
|
457
514
|
if self.option_group_id.barcode_guided_mode == "guided":
|
458
515
|
moves_todo = self.todo_line_id.stock_move_ids
|
516
|
+
elif self.picking_id:
|
517
|
+
moves_todo = self.picking_id.move_lines.filtered(
|
518
|
+
lambda sm: sm.product_id == self.product_id
|
519
|
+
)
|
459
520
|
else:
|
460
521
|
moves_todo = StockMove.search(domain)
|
461
522
|
if not getattr(
|
@@ -615,13 +676,14 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
615
676
|
# link this new lines to the todo line details
|
616
677
|
# If user scan a product distinct of the todo line we need link to other
|
617
678
|
# alternative move
|
618
|
-
if
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
679
|
+
if self.option_group_id.source_pending_moves != "move_line_ids":
|
680
|
+
if move_to_link_in_todo_line and self.todo_line_id:
|
681
|
+
todo_line = self.todo_line_id
|
682
|
+
else:
|
683
|
+
todo_line = self.todo_line_ids.filtered(
|
684
|
+
lambda ln: ln.product_id == self.product_id
|
685
|
+
)
|
686
|
+
todo_line.line_ids = [(4, sml.id) for sml in stock_move_lines]
|
625
687
|
self.update_fields_after_process_stock(moves_todo)
|
626
688
|
return move_lines_dic
|
627
689
|
|
@@ -784,6 +846,150 @@ class WizStockBarcodesReadPicking(models.TransientModel):
|
|
784
846
|
return bool(self.location_dest_id)
|
785
847
|
return super()._option_required_hook(option_required)
|
786
848
|
|
849
|
+
def _group_key(self, line):
|
850
|
+
group_key_for_todo_records = self.option_group_id.group_key_for_todo_records
|
851
|
+
if group_key_for_todo_records:
|
852
|
+
return safe_eval(group_key_for_todo_records, globals_dict={"object": line})
|
853
|
+
if self.option_group_id.source_pending_moves == "move_line_ids":
|
854
|
+
return (
|
855
|
+
line.location_id.id,
|
856
|
+
line.product_id.id,
|
857
|
+
line.lot_id.id,
|
858
|
+
line.package_id.id,
|
859
|
+
)
|
860
|
+
else:
|
861
|
+
return (line.location_id.id, line.product_id.id)
|
862
|
+
|
863
|
+
def _get_all_products_quantities_in_package(self, package):
|
864
|
+
res = {}
|
865
|
+
for quant in package._get_contained_quants():
|
866
|
+
if quant.product_id not in res:
|
867
|
+
res[quant.product_id] = 0
|
868
|
+
res[quant.product_id] += quant.quantity
|
869
|
+
return res
|
870
|
+
|
871
|
+
def _prepare_fill_record_values(self, line, position):
|
872
|
+
vals = {
|
873
|
+
"wiz_barcode_id": self.id,
|
874
|
+
"product_id": line.product_id.id,
|
875
|
+
"product_uom_qty": line.product_uom_qty,
|
876
|
+
"name": "To do action",
|
877
|
+
"position_index": position,
|
878
|
+
"picking_code": line.picking_code,
|
879
|
+
}
|
880
|
+
if line._name == "stock.move.line":
|
881
|
+
package_product_dic = self._get_all_products_quantities_in_package(
|
882
|
+
line.package_id
|
883
|
+
)
|
884
|
+
vals.update(
|
885
|
+
{
|
886
|
+
"location_id": line.location_id.id,
|
887
|
+
"location_dest_id": line.location_dest_id.id,
|
888
|
+
"lot_id": line.lot_id.id,
|
889
|
+
"package_id": line.package_id.id,
|
890
|
+
"result_package_id": line.result_package_id.id,
|
891
|
+
"uom_id": line.product_uom_id.id,
|
892
|
+
"product_qty_reserved": line.product_qty,
|
893
|
+
"line_ids": [(6, 0, line.ids)],
|
894
|
+
"stock_move_ids": [(6, 0, line.move_id.ids)],
|
895
|
+
"package_product_qty": package_product_dic
|
896
|
+
and package_product_dic[line.product_id]
|
897
|
+
or 0.0,
|
898
|
+
"is_stock_move_line_origin": True,
|
899
|
+
}
|
900
|
+
)
|
901
|
+
else:
|
902
|
+
vals.update(
|
903
|
+
{
|
904
|
+
"location_id": (line.move_line_ids[:1] or line).location_id.id,
|
905
|
+
"location_dest_id": (
|
906
|
+
line.move_line_ids[:1] or line
|
907
|
+
).location_dest_id.id,
|
908
|
+
"uom_id": line.product_uom.id,
|
909
|
+
"product_qty_reserved": line.move_line_ids
|
910
|
+
and sum(line.move_line_ids.mapped("product_qty"))
|
911
|
+
or line.product_uom_qty,
|
912
|
+
"line_ids": [(6, 0, line.move_line_ids.ids)],
|
913
|
+
"stock_move_ids": [(6, 0, line.ids)],
|
914
|
+
"is_stock_move_line_origin": False,
|
915
|
+
}
|
916
|
+
)
|
917
|
+
return vals
|
918
|
+
|
919
|
+
def _update_fill_record_values(self, line, vals):
|
920
|
+
vals["product_uom_qty"] += line.product_uom_qty
|
921
|
+
if vals["is_stock_move_line_origin"]:
|
922
|
+
vals["product_qty_reserved"] += line.product_qty
|
923
|
+
vals["line_ids"][0][2].append(line.id)
|
924
|
+
vals["stock_move_ids"][0][2].append(line.move_id.id)
|
925
|
+
else:
|
926
|
+
vals["product_qty_reserved"] += (
|
927
|
+
line.move_line_ids
|
928
|
+
and sum(line.move_line_ids.mapped("product_qty"))
|
929
|
+
or line.product_uom_qty
|
930
|
+
)
|
931
|
+
vals["line_ids"][0][2].extend(line.move_line_ids.ids)
|
932
|
+
vals["stock_move_ids"][0][2].extend(line.ids)
|
933
|
+
return vals
|
934
|
+
|
935
|
+
@api.model
|
936
|
+
def fill_records(self, lines_list):
|
937
|
+
"""
|
938
|
+
:param lines_list: browse list
|
939
|
+
:return:
|
940
|
+
"""
|
941
|
+
self.forced_todo_key = str(
|
942
|
+
self._group_key(self.todo_line_id or self.selected_pending_move_id)
|
943
|
+
)
|
944
|
+
self.todo_line_ids.unlink()
|
945
|
+
self.todo_line_id = False
|
946
|
+
# self.position_index = 0
|
947
|
+
todo_vals = OrderedDict()
|
948
|
+
position = 0
|
949
|
+
move_qty_dic = defaultdict(float)
|
950
|
+
is_stock_move_line_origin = lines_list[0]._name == "stock.move.line"
|
951
|
+
for lines in lines_list:
|
952
|
+
for line in lines:
|
953
|
+
key = self._group_key(line)
|
954
|
+
if key not in todo_vals:
|
955
|
+
todo_vals[key] = self._prepare_fill_record_values(line, position)
|
956
|
+
position += 1
|
957
|
+
else:
|
958
|
+
todo_vals[key] = self._update_fill_record_values(
|
959
|
+
line, todo_vals[key]
|
960
|
+
)
|
961
|
+
if is_stock_move_line_origin:
|
962
|
+
move_qty_dic[line.move_id] += max(
|
963
|
+
line.product_uom_qty, line.qty_done
|
964
|
+
)
|
965
|
+
else:
|
966
|
+
move_qty_dic[line] += max(line.product_uom_qty, line.quantity_done)
|
967
|
+
for move in self.get_moves():
|
968
|
+
qty = move_qty_dic[move]
|
969
|
+
if (
|
970
|
+
move.barcode_backorder_action == "pending"
|
971
|
+
and move.product_uom_qty > qty
|
972
|
+
):
|
973
|
+
vals = self._prepare_fill_record_values(move, position)
|
974
|
+
vals.update(
|
975
|
+
{
|
976
|
+
"product_uom_qty": move.product_uom_qty - qty,
|
977
|
+
"product_qty_reserved": 0.0,
|
978
|
+
"line_ids": False,
|
979
|
+
"is_extra_line": True,
|
980
|
+
}
|
981
|
+
)
|
982
|
+
todo_vals[
|
983
|
+
(
|
984
|
+
move,
|
985
|
+
"M",
|
986
|
+
)
|
987
|
+
] = vals
|
988
|
+
position += 1
|
989
|
+
self.todo_line_ids = self.env["wiz.stock.barcodes.read.todo"].create(
|
990
|
+
list(todo_vals.values())
|
991
|
+
)
|
992
|
+
|
787
993
|
|
788
994
|
class WizCandidatePicking(models.TransientModel):
|
789
995
|
"""
|
@@ -127,6 +127,8 @@
|
|
127
127
|
<field name="picking_location_id" invisible="1" />
|
128
128
|
<field name="picking_location_dest_id" invisible="1" />
|
129
129
|
<field name="company_id" invisible="1" />
|
130
|
+
<field name="todo_line_is_extra_line" invisible="1" />
|
131
|
+
<field name="qty_available" invisible="1" />
|
130
132
|
</field>
|
131
133
|
<field name="location_id" position="attributes">
|
132
134
|
<attribute
|
@@ -161,6 +163,12 @@
|
|
161
163
|
</div>
|
162
164
|
</group>
|
163
165
|
</group>
|
166
|
+
<group name="scan_fields" position="attributes">
|
167
|
+
<!-- hide group scan_fields for extra todo lines -->
|
168
|
+
<attribute
|
169
|
+
name="attrs"
|
170
|
+
>{'invisible': [('todo_line_is_extra_line', '!=', False)]}</attribute>
|
171
|
+
</group>
|
164
172
|
<group name="scan_fields" position="after">
|
165
173
|
<group
|
166
174
|
string="Pending moves"
|
@@ -173,11 +181,14 @@
|
|
173
181
|
force_save="1"
|
174
182
|
mode="tree,kanban"
|
175
183
|
>
|
176
|
-
<tree>
|
184
|
+
<tree decoration-warning="is_extra_line">
|
177
185
|
<field name="state" invisible="1" />
|
186
|
+
<field name="is_extra_line" invisible="1" />
|
178
187
|
<field name="product_id" />
|
179
188
|
<field name="product_uom_qty" />
|
180
189
|
<field name="line_ids" invisible="1" />
|
190
|
+
<field name="location_id" optional="hide" />
|
191
|
+
<field name="location_dest_id" optional="hide" />
|
181
192
|
<field
|
182
193
|
name="lot_id"
|
183
194
|
groups="stock.group_production_lot"
|