odoo-addon-stock-move-location 16.0.1.4.0.2__py3-none-any.whl → 18.0.1.0.0.3__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_move_location/README.rst +48 -48
- odoo/addons/stock_move_location/__manifest__.py +1 -1
- odoo/addons/stock_move_location/i18n/stock_move_location.pot +37 -10
- odoo/addons/stock_move_location/init_hook.py +1 -5
- odoo/addons/stock_move_location/models/stock_picking.py +9 -7
- odoo/addons/stock_move_location/readme/CONTRIBUTORS.md +2 -0
- odoo/addons/stock_move_location/readme/USAGE.md +0 -1
- odoo/addons/stock_move_location/static/description/index.html +12 -12
- odoo/addons/stock_move_location/tests/test_common.py +16 -13
- odoo/addons/stock_move_location/tests/test_move_location.py +19 -24
- odoo/addons/stock_move_location/tests/test_stock_fillwithstock.py +23 -28
- odoo/addons/stock_move_location/views/stock_picking.xml +1 -3
- odoo/addons/stock_move_location/views/stock_picking_type_views.xml +4 -7
- odoo/addons/stock_move_location/wizard/stock_move_location.py +46 -61
- odoo/addons/stock_move_location/wizard/stock_move_location.xml +60 -32
- odoo/addons/stock_move_location/wizard/stock_move_location_line.py +18 -30
- {odoo_addon_stock_move_location-16.0.1.4.0.2.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.3.dist-info}/METADATA +53 -53
- {odoo_addon_stock_move_location-16.0.1.4.0.2.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.3.dist-info}/RECORD +20 -20
- {odoo_addon_stock_move_location-16.0.1.4.0.2.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.3.dist-info}/WHEEL +1 -1
- odoo_addon_stock_move_location-18.0.1.0.0.3.dist-info/top_level.txt +1 -0
- odoo_addon_stock_move_location-16.0.1.4.0.2.dist-info/top_level.txt +0 -1
@@ -2,63 +2,58 @@
|
|
2
2
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
3
3
|
|
4
4
|
|
5
|
-
|
5
|
+
from odoo.addons.base.tests.common import BaseCommon
|
6
6
|
|
7
7
|
|
8
|
-
class TestFillwithStock(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
context=dict(
|
13
|
-
self.env.context,
|
14
|
-
tracking_disable=True,
|
15
|
-
)
|
16
|
-
)
|
8
|
+
class TestFillwithStock(BaseCommon):
|
9
|
+
@classmethod
|
10
|
+
def setUpClass(cls):
|
11
|
+
super().setUpClass()
|
17
12
|
|
18
|
-
|
19
|
-
|
13
|
+
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
14
|
+
cls.pack_location = cls.env.ref("stock.location_pack_zone")
|
20
15
|
|
21
|
-
|
16
|
+
cls.shelf1_location = cls.env["stock.location"].create(
|
22
17
|
{
|
23
18
|
"name": "Test location",
|
24
19
|
"usage": "internal",
|
25
|
-
"location_id":
|
20
|
+
"location_id": cls.stock_location.id,
|
26
21
|
}
|
27
22
|
)
|
28
23
|
|
29
|
-
|
24
|
+
cls.product1 = cls.env["product.product"].create(
|
30
25
|
{
|
31
26
|
"name": "Product A",
|
32
|
-
"
|
27
|
+
"is_storable": True,
|
33
28
|
}
|
34
29
|
)
|
35
|
-
|
30
|
+
cls.product2 = cls.env["product.product"].create(
|
36
31
|
{
|
37
32
|
"name": "Product B",
|
38
|
-
"
|
33
|
+
"is_storable": True,
|
39
34
|
}
|
40
35
|
)
|
41
36
|
|
42
|
-
|
37
|
+
cls.env["stock.quant"].create(
|
43
38
|
{
|
44
|
-
"product_id":
|
45
|
-
"location_id":
|
39
|
+
"product_id": cls.product1.id,
|
40
|
+
"location_id": cls.shelf1_location.id,
|
46
41
|
"quantity": 5.0,
|
47
42
|
"reserved_quantity": 0.0,
|
48
43
|
}
|
49
44
|
)
|
50
|
-
|
45
|
+
cls.env["stock.quant"].create(
|
51
46
|
{
|
52
|
-
"product_id":
|
53
|
-
"location_id":
|
47
|
+
"product_id": cls.product1.id,
|
48
|
+
"location_id": cls.shelf1_location.id,
|
54
49
|
"quantity": 10.0,
|
55
50
|
"reserved_quantity": 5.0,
|
56
51
|
}
|
57
52
|
)
|
58
|
-
|
53
|
+
cls.env["stock.quant"].create(
|
59
54
|
{
|
60
|
-
"product_id":
|
61
|
-
"location_id":
|
55
|
+
"product_id": cls.product2.id,
|
56
|
+
"location_id": cls.shelf1_location.id,
|
62
57
|
"quantity": 5.0,
|
63
58
|
"reserved_quantity": 0.0,
|
64
59
|
}
|
@@ -80,7 +75,7 @@ class TestFillwithStock(common.TransactionCase):
|
|
80
75
|
picking_stock_pack.move_ids.filtered(
|
81
76
|
lambda m: m.product_id == self.product1
|
82
77
|
).product_uom_qty,
|
83
|
-
|
78
|
+
15.0,
|
84
79
|
)
|
85
80
|
self.assertEqual(
|
86
81
|
picking_stock_pack.move_ids.filtered(
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<?xml version="1.0" ?>
|
2
2
|
<odoo>
|
3
|
-
|
4
3
|
<record id="view_picking_form" model="ir.ui.view">
|
5
4
|
<field name="name">stock.picking.form.fillwithstock</field>
|
6
5
|
<field name="model">stock.picking</field>
|
@@ -9,7 +8,7 @@
|
|
9
8
|
<button name="action_confirm" position="before">
|
10
9
|
<button
|
11
10
|
name="button_fillwithstock"
|
12
|
-
|
11
|
+
invisible="state != 'draft'"
|
13
12
|
string="Fill with stock"
|
14
13
|
type="object"
|
15
14
|
class="oe_highlight"
|
@@ -18,5 +17,4 @@
|
|
18
17
|
</button>
|
19
18
|
</field>
|
20
19
|
</record>
|
21
|
-
|
22
20
|
</odoo>
|
@@ -5,11 +5,8 @@
|
|
5
5
|
<field name="model">stock.picking.type</field>
|
6
6
|
<field name="inherit_id" ref="stock.view_picking_type_form" />
|
7
7
|
<field name="arch" type="xml">
|
8
|
-
<field name="
|
9
|
-
<field
|
10
|
-
name="show_move_onhand"
|
11
|
-
attrs='{"invisible": [("code", "not in", ["internal", "outgoing"])]}'
|
12
|
-
/>
|
8
|
+
<field name="create_backorder" position="after">
|
9
|
+
<field name="show_move_onhand" invisible="code != 'internal'" />
|
13
10
|
</field>
|
14
11
|
</field>
|
15
12
|
</record>
|
@@ -20,7 +17,7 @@
|
|
20
17
|
<field name="code" position="after">
|
21
18
|
<field name="show_move_onhand" />
|
22
19
|
</field>
|
23
|
-
<
|
20
|
+
<button name="get_action_picking_tree_ready" position="after">
|
24
21
|
<div t-if="record.show_move_onhand.raw_value">
|
25
22
|
<button
|
26
23
|
name="action_move_location"
|
@@ -31,7 +28,7 @@
|
|
31
28
|
Move On Hand
|
32
29
|
</button>
|
33
30
|
</div>
|
34
|
-
</
|
31
|
+
</button>
|
35
32
|
</field>
|
36
33
|
</record>
|
37
34
|
</odoo>
|
@@ -3,9 +3,8 @@
|
|
3
3
|
# Copyright 2019 Sergio Teruel - Tecnativa <sergio.teruel@tecnativa.com>
|
4
4
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
5
5
|
|
6
|
-
from itertools import groupby
|
7
6
|
|
8
|
-
from odoo import api, fields, models
|
7
|
+
from odoo import Command, api, fields, models
|
9
8
|
from odoo.fields import first
|
10
9
|
from odoo.osv import expression
|
11
10
|
|
@@ -39,17 +38,20 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
39
38
|
"move_location_wizard_id",
|
40
39
|
string="Move Location lines",
|
41
40
|
)
|
41
|
+
company_id = fields.Many2one("res.company", default=lambda self: self.env.company)
|
42
42
|
picking_type_id = fields.Many2one(
|
43
43
|
compute="_compute_picking_type_id",
|
44
44
|
comodel_name="stock.picking.type",
|
45
45
|
readonly=False,
|
46
46
|
store=True,
|
47
|
+
domain="[('company_id', '=', company_id), ('code', '=', 'internal')]",
|
47
48
|
)
|
48
49
|
picking_id = fields.Many2one(
|
49
50
|
string="Connected Picking", comodel_name="stock.picking"
|
50
51
|
)
|
51
52
|
edit_locations = fields.Boolean(default=True)
|
52
53
|
apply_putaway_strategy = fields.Boolean()
|
54
|
+
exclude_reserved_qty = fields.Boolean(default=True)
|
53
55
|
|
54
56
|
@api.depends("edit_locations")
|
55
57
|
def _compute_readonly_locations(self):
|
@@ -67,15 +69,20 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
67
69
|
@api.depends_context("company")
|
68
70
|
@api.depends("origin_location_id")
|
69
71
|
def _compute_picking_type_id(self):
|
70
|
-
company_id = self.env.context.get("company_id") or self.env.company.id
|
71
72
|
for rec in self:
|
72
73
|
picking_type = self.env["stock.picking.type"]
|
73
74
|
base_domain = [
|
74
|
-
("code", "
|
75
|
-
("warehouse_id.company_id", "=", company_id),
|
75
|
+
("code", "=", "internal"),
|
76
|
+
("warehouse_id.company_id", "=", self.company_id.id),
|
76
77
|
]
|
77
78
|
if rec.origin_location_id:
|
78
79
|
location_id = rec.origin_location_id
|
80
|
+
if (
|
81
|
+
location_id
|
82
|
+
and rec.picking_type_id
|
83
|
+
and rec.picking_type_id.default_location_src_id == location_id
|
84
|
+
):
|
85
|
+
continue
|
79
86
|
while location_id and not picking_type:
|
80
87
|
domain = [("default_location_src_id", "=", location_id.id)]
|
81
88
|
domain = expression.AND([base_domain, domain])
|
@@ -102,8 +109,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
102
109
|
@api.model
|
103
110
|
def _prepare_wizard_move_lines(self, quants):
|
104
111
|
res = []
|
105
|
-
|
106
|
-
if not exclude_reserved_qty:
|
112
|
+
if not self.exclude_reserved_qty:
|
107
113
|
res = [
|
108
114
|
(
|
109
115
|
0,
|
@@ -113,6 +119,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
113
119
|
"move_quantity": quant.quantity,
|
114
120
|
"max_quantity": quant.quantity,
|
115
121
|
"reserved_quantity": quant.reserved_quantity,
|
122
|
+
"total_quantity": quant.quantity,
|
116
123
|
"origin_location_id": quant.location_id.id,
|
117
124
|
"lot_id": quant.lot_id.id,
|
118
125
|
"package_id": quant.package_id.id,
|
@@ -125,12 +132,13 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
125
132
|
]
|
126
133
|
else:
|
127
134
|
# if need move only available qty per product on location
|
128
|
-
for
|
129
|
-
# we need only one quant per product
|
130
|
-
quant = list(quant)[0]
|
135
|
+
for quant in quants:
|
131
136
|
qty = quant._get_available_quantity(
|
132
137
|
quant.product_id,
|
133
138
|
quant.location_id,
|
139
|
+
quant.lot_id,
|
140
|
+
quant.package_id,
|
141
|
+
quant.owner_id,
|
134
142
|
)
|
135
143
|
if qty:
|
136
144
|
res.append(
|
@@ -142,6 +150,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
142
150
|
"move_quantity": qty,
|
143
151
|
"max_quantity": qty,
|
144
152
|
"reserved_quantity": quant.reserved_quantity,
|
153
|
+
"total_quantity": quant.quantity,
|
145
154
|
"origin_location_id": quant.location_id.id,
|
146
155
|
"lot_id": quant.lot_id.id,
|
147
156
|
"package_id": quant.package_id.id,
|
@@ -153,16 +162,6 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
153
162
|
)
|
154
163
|
return res
|
155
164
|
|
156
|
-
@api.onchange("origin_location_id")
|
157
|
-
def _onchange_origin_location_id(self):
|
158
|
-
if not self.env.context.get("origin_location_disable", False):
|
159
|
-
self._clear_lines()
|
160
|
-
|
161
|
-
@api.onchange("destination_location_id")
|
162
|
-
def _onchange_destination_location_id(self):
|
163
|
-
for line in self.stock_move_location_line_ids:
|
164
|
-
line.destination_location_id = self.destination_location_id
|
165
|
-
|
166
165
|
def _clear_lines(self):
|
167
166
|
self.stock_move_location_line_ids = False
|
168
167
|
|
@@ -196,8 +195,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
196
195
|
groups = self.group_lines()
|
197
196
|
moves = self.env["stock.move"]
|
198
197
|
for lines in groups.values():
|
199
|
-
|
200
|
-
moves |= move
|
198
|
+
moves |= self._create_move(picking, lines)
|
201
199
|
return moves
|
202
200
|
|
203
201
|
def _get_move_values(self, picking, lines):
|
@@ -224,17 +222,8 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
224
222
|
lines.create_move_lines(picking, move)
|
225
223
|
if self.env.context.get("planned"):
|
226
224
|
for line in lines:
|
227
|
-
available_quantity = self.env["stock.quant"]._get_available_quantity(
|
228
|
-
line.product_id,
|
229
|
-
line.origin_location_id,
|
230
|
-
lot_id=line.lot_id,
|
231
|
-
package_id=line.package_id,
|
232
|
-
owner_id=line.owner_id,
|
233
|
-
strict=True,
|
234
|
-
)
|
235
225
|
move._update_reserved_quantity(
|
236
226
|
line.move_quantity,
|
237
|
-
available_quantity,
|
238
227
|
line.origin_location_id,
|
239
228
|
lot_id=line.lot_id,
|
240
229
|
package_id=line.package_id,
|
@@ -244,10 +233,11 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
244
233
|
# Force the state to be assigned, instead of _action_assign,
|
245
234
|
# to avoid discarding the selected move_location_line.
|
246
235
|
move.state = "assigned"
|
236
|
+
move.move_line_ids.filtered(lambda ml: not ml.quantity).unlink()
|
247
237
|
move.move_line_ids.write({"state": "assigned"})
|
248
238
|
return move
|
249
239
|
|
250
|
-
def _unreserve_moves(self):
|
240
|
+
def _unreserve_moves(self, picking):
|
251
241
|
"""
|
252
242
|
Try to unreserve moves that they has reserved quantity before user
|
253
243
|
moves products from a location to other one and change move origin
|
@@ -256,9 +246,9 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
256
246
|
"""
|
257
247
|
moves_to_reassign = self.env["stock.move"]
|
258
248
|
lines_to_ckeck_reverve = self.stock_move_location_line_ids.filtered(
|
259
|
-
lambda
|
260
|
-
|
261
|
-
and not
|
249
|
+
lambda line: (
|
250
|
+
line.move_quantity > line.max_quantity
|
251
|
+
and not line.origin_location_id.should_bypass_reservation()
|
262
252
|
)
|
263
253
|
)
|
264
254
|
for line in lines_to_ckeck_reverve:
|
@@ -270,7 +260,8 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
270
260
|
("lot_id", "=", line.lot_id.id),
|
271
261
|
("package_id", "=", line.package_id.id),
|
272
262
|
("owner_id", "=", line.owner_id.id),
|
273
|
-
("
|
263
|
+
("quantity", ">", 0.0),
|
264
|
+
("picking_id", "!=", picking.id),
|
274
265
|
]
|
275
266
|
)
|
276
267
|
moves_to_unreserve = move_lines.mapped("move_id")
|
@@ -281,13 +272,10 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
281
272
|
|
282
273
|
def action_move_location(self):
|
283
274
|
self.ensure_one()
|
284
|
-
if
|
285
|
-
picking = self._create_picking()
|
286
|
-
else:
|
287
|
-
picking = self.picking_id
|
275
|
+
picking = self.picking_id if self.picking_id else self._create_picking()
|
288
276
|
self._create_moves(picking)
|
289
277
|
if not self.env.context.get("planned"):
|
290
|
-
moves_to_reassign = self._unreserve_moves()
|
278
|
+
moves_to_reassign = self._unreserve_moves(picking)
|
291
279
|
picking.button_validate()
|
292
280
|
moves_to_reassign._action_assign()
|
293
281
|
self.picking_id = picking
|
@@ -318,7 +306,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
318
306
|
"quantity:sum",
|
319
307
|
"reserved_quantity:sum",
|
320
308
|
],
|
321
|
-
groupby=["product_id", "lot_id", "package_id", "owner_id"],
|
309
|
+
groupby=["id", "product_id", "lot_id", "package_id", "owner_id"],
|
322
310
|
orderby="id",
|
323
311
|
lazy=False,
|
324
312
|
)
|
@@ -335,12 +323,18 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
335
323
|
and self.destination_location_id._get_putaway_strategy(product).id
|
336
324
|
or self.destination_location_id.id
|
337
325
|
)
|
326
|
+
res_qty = group.get("reserved_quantity", 0.0)
|
327
|
+
total_qty = group.get("quantity", 0.0)
|
328
|
+
max_qty = (
|
329
|
+
total_qty if not self.exclude_reserved_qty else total_qty - res_qty
|
330
|
+
)
|
338
331
|
product_data.append(
|
339
332
|
{
|
340
333
|
"product_id": product.id,
|
341
|
-
"move_quantity":
|
342
|
-
"max_quantity":
|
343
|
-
"reserved_quantity":
|
334
|
+
"move_quantity": max_qty,
|
335
|
+
"max_quantity": max_qty,
|
336
|
+
"reserved_quantity": res_qty,
|
337
|
+
"total_quantity": total_qty,
|
344
338
|
"origin_location_id": self.origin_location_id.id,
|
345
339
|
"destination_location_id": location_dest_id,
|
346
340
|
# cursor returns None instead of False
|
@@ -357,21 +351,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
357
351
|
)
|
358
352
|
return product_data
|
359
353
|
|
360
|
-
|
361
|
-
lines = []
|
362
|
-
line_model = self.env["wiz.stock.move.location.line"]
|
363
|
-
for line_val in self._get_stock_move_location_lines_values():
|
364
|
-
if line_val.get("max_quantity") <= 0:
|
365
|
-
continue
|
366
|
-
line = line_model.create(line_val)
|
367
|
-
line.max_quantity = line.get_max_quantity()
|
368
|
-
line.reserved_quantity = line.reserved_quantity
|
369
|
-
lines.append(line)
|
370
|
-
self.update(
|
371
|
-
{"stock_move_location_line_ids": [(6, 0, [line.id for line in lines])]}
|
372
|
-
)
|
373
|
-
|
374
|
-
@api.onchange("origin_location_id")
|
354
|
+
@api.onchange("origin_location_id", "exclude_reserved_qty")
|
375
355
|
def onchange_origin_location(self):
|
376
356
|
# Get origin_location_disable context key to prevent load all origin
|
377
357
|
# location products when user opens the wizard from stock quants to
|
@@ -380,7 +360,12 @@ class StockMoveLocationWizard(models.TransientModel):
|
|
380
360
|
not self.env.context.get("origin_location_disable")
|
381
361
|
and self.origin_location_id
|
382
362
|
):
|
383
|
-
|
363
|
+
lines = [Command.clear()] + [
|
364
|
+
Command.create(line_vals)
|
365
|
+
for line_vals in self._get_stock_move_location_lines_values()
|
366
|
+
if line_vals.get("max_quantity", 0.0) > 0.0
|
367
|
+
]
|
368
|
+
self.update({"stock_move_location_line_ids": lines})
|
384
369
|
|
385
370
|
def clear_lines(self):
|
386
371
|
self._clear_lines()
|
@@ -10,15 +10,24 @@
|
|
10
10
|
<form>
|
11
11
|
<sheet>
|
12
12
|
<div class="oe_button_box" name="button_box" />
|
13
|
-
<
|
13
|
+
<div>
|
14
|
+
<label for="edit_locations">
|
15
|
+
Edit Locations
|
16
|
+
</label>
|
14
17
|
<field name="edit_locations" widget="boolean_toggle" />
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
/>
|
21
|
-
</
|
18
|
+
</div>
|
19
|
+
<div>
|
20
|
+
<label for="exclude_reserved_qty">
|
21
|
+
Exclude Reserved Quantities
|
22
|
+
</label>
|
23
|
+
<field name="exclude_reserved_qty" widget="boolean_toggle" />
|
24
|
+
</div>
|
25
|
+
<div groups="stock.group_stock_multi_locations">
|
26
|
+
<label for="apply_putaway_strategy">
|
27
|
+
Apply putaway strategy for moving products
|
28
|
+
</label>
|
29
|
+
<field name="apply_putaway_strategy" widget="boolean_toggle" />
|
30
|
+
</div>
|
22
31
|
<group name="picking_type">
|
23
32
|
<field name="picking_type_id" />
|
24
33
|
</group>
|
@@ -26,24 +35,23 @@
|
|
26
35
|
<field name="origin_location_disable" invisible="True" />
|
27
36
|
<field
|
28
37
|
name="origin_location_id"
|
29
|
-
|
38
|
+
readonly="origin_location_disable"
|
30
39
|
/>
|
31
40
|
<field name="destination_location_disable" invisible="True" />
|
32
41
|
<field
|
33
42
|
name="destination_location_id"
|
34
|
-
|
43
|
+
readonly="destination_location_disable"
|
35
44
|
/>
|
36
|
-
|
37
|
-
<group name="filters">
|
45
|
+
<field name="company_id" invisible="1" />
|
38
46
|
</group>
|
39
47
|
<group name="lines">
|
40
48
|
<field
|
41
49
|
name="stock_move_location_line_ids"
|
42
50
|
nolabel="1"
|
43
|
-
mode="
|
51
|
+
mode="list,kanban"
|
44
52
|
colspan="2"
|
45
53
|
>
|
46
|
-
<
|
54
|
+
<list
|
47
55
|
editable="bottom"
|
48
56
|
decoration-info="move_quantity != max_quantity"
|
49
57
|
decoration-danger="(move_quantity < 0) or (move_quantity > max_quantity)"
|
@@ -58,6 +66,11 @@
|
|
58
66
|
string="UoM"
|
59
67
|
groups="uom.group_uom"
|
60
68
|
/>
|
69
|
+
<field
|
70
|
+
name="product_uom_id"
|
71
|
+
string="UoM"
|
72
|
+
column_invisible="1"
|
73
|
+
/>
|
61
74
|
<field
|
62
75
|
name="origin_location_id"
|
63
76
|
readonly="1"
|
@@ -85,7 +98,7 @@
|
|
85
98
|
<field name="custom" invisible="1" />
|
86
99
|
<field
|
87
100
|
name="max_quantity"
|
88
|
-
|
101
|
+
readonly="not custom"
|
89
102
|
force_save="1"
|
90
103
|
/>
|
91
104
|
<field
|
@@ -93,27 +106,42 @@
|
|
93
106
|
readonly="1"
|
94
107
|
force_save="1"
|
95
108
|
/>
|
96
|
-
|
109
|
+
<field
|
110
|
+
name="total_quantity"
|
111
|
+
readonly="1"
|
112
|
+
force_save="1"
|
113
|
+
/>
|
114
|
+
</list>
|
97
115
|
<kanban class="o_kanban_mobile">
|
98
116
|
<templates>
|
99
|
-
<t t-name="
|
100
|
-
<
|
101
|
-
|
102
|
-
|
103
|
-
|
117
|
+
<t t-name="card">
|
118
|
+
<field name="product_id" class="fw-bolder" />
|
119
|
+
<div
|
120
|
+
groups="stock.group_production_lot"
|
121
|
+
invisible="not lot_id"
|
122
|
+
>
|
123
|
+
Lot/SN:
|
124
|
+
<field
|
125
|
+
name="lot_id"
|
126
|
+
invisible="not lot_id"
|
127
|
+
/>
|
128
|
+
</div>
|
129
|
+
<div groups="stock.group_stock_multi_locations">
|
130
|
+
<field name="origin_location_id" />
|
131
|
+
→
|
132
|
+
<field name="destination_location_id" />
|
133
|
+
</div>
|
134
|
+
<div class="row">
|
135
|
+
<div class="col-6">
|
104
136
|
<field
|
105
|
-
name="
|
106
|
-
|
137
|
+
name="move_quantity"
|
138
|
+
string="Quantity"
|
139
|
+
/>
|
140
|
+
<field
|
141
|
+
name="product_uom_id"
|
142
|
+
string="Unit of Measure"
|
143
|
+
groups="uom.group_uom"
|
107
144
|
/>
|
108
|
-
<field name="origin_location_id" />
|
109
|
-
<field name="destination_location_id" />
|
110
|
-
</div>
|
111
|
-
<div class="o_kanban_record_bottom">
|
112
|
-
<div class="oe_kanban_bottom_right">
|
113
|
-
<span>
|
114
|
-
<field name="move_quantity" />
|
115
|
-
</span>
|
116
|
-
</div>
|
117
145
|
</div>
|
118
146
|
</div>
|
119
147
|
</t>
|
@@ -22,7 +22,9 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
22
22
|
string="Origin Location", comodel_name="stock.location"
|
23
23
|
)
|
24
24
|
destination_location_id = fields.Many2one(
|
25
|
-
string="Destination Location",
|
25
|
+
string="Destination Location",
|
26
|
+
comodel_name="stock.location",
|
27
|
+
compute="_compute_destination_location_id",
|
26
28
|
)
|
27
29
|
product_uom_id = fields.Many2one(
|
28
30
|
string="Product Unit of Measure", comodel_name="uom.uom"
|
@@ -44,9 +46,19 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
44
46
|
max_quantity = fields.Float(
|
45
47
|
string="Maximum available quantity", digits="Product Unit of Measure"
|
46
48
|
)
|
49
|
+
total_quantity = fields.Float(
|
50
|
+
string="Total existence quantity", digits="Product Unit of Measure"
|
51
|
+
)
|
47
52
|
reserved_quantity = fields.Float(digits="Product Unit of Measure")
|
48
53
|
custom = fields.Boolean(string="Custom line", default=True)
|
49
54
|
|
55
|
+
@api.depends("move_location_wizard_id.destination_location_id")
|
56
|
+
def _compute_destination_location_id(self):
|
57
|
+
for record in self:
|
58
|
+
record.destination_location_id = (
|
59
|
+
record.move_location_wizard_id.destination_location_id
|
60
|
+
)
|
61
|
+
|
50
62
|
@staticmethod
|
51
63
|
def _compare(qty1, qty2, precision_rounding):
|
52
64
|
return float_compare(qty1, qty2, precision_rounding=precision_rounding)
|
@@ -64,32 +76,10 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
64
76
|
_("Move quantity can not exceed max quantity or be negative")
|
65
77
|
)
|
66
78
|
|
67
|
-
def get_max_quantity(self):
|
68
|
-
self.product_uom_id = self.product_id.uom_id
|
69
|
-
search_args = [
|
70
|
-
("location_id", "=", self.origin_location_id.id),
|
71
|
-
("product_id", "=", self.product_id.id),
|
72
|
-
]
|
73
|
-
if self.lot_id:
|
74
|
-
search_args.append(("lot_id", "=", self.lot_id.id))
|
75
|
-
else:
|
76
|
-
search_args.append(("lot_id", "=", False))
|
77
|
-
if self.package_id:
|
78
|
-
search_args.append(("package_id", "=", self.package_id.id))
|
79
|
-
else:
|
80
|
-
search_args.append(("package_id", "=", False))
|
81
|
-
if self.owner_id:
|
82
|
-
search_args.append(("owner_id", "=", self.owner_id.id))
|
83
|
-
else:
|
84
|
-
search_args.append(("owner_id", "=", False))
|
85
|
-
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
|
86
|
-
max_quantity = res[0]["quantity"]
|
87
|
-
return max_quantity
|
88
|
-
|
89
79
|
def create_move_lines(self, picking, move):
|
90
80
|
for line in self:
|
91
81
|
values = line._get_move_line_values(picking, move)
|
92
|
-
if not self.env.context.get("planned") and values.get("
|
82
|
+
if not self.env.context.get("planned") and values.get("quantity") <= 0:
|
93
83
|
continue
|
94
84
|
self.env["stock.move.line"].create(values)
|
95
85
|
return True
|
@@ -101,7 +91,7 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
101
91
|
and self.destination_location_id._get_putaway_strategy(self.product_id).id
|
102
92
|
or self.destination_location_id.id
|
103
93
|
)
|
104
|
-
|
94
|
+
qty_done = self._get_available_quantity()
|
105
95
|
return {
|
106
96
|
"product_id": self.product_id.id,
|
107
97
|
"lot_id": self.lot_id.id,
|
@@ -110,7 +100,7 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
110
100
|
"owner_id": self.owner_id.id,
|
111
101
|
"location_id": self.origin_location_id.id,
|
112
102
|
"location_dest_id": location_dest_id,
|
113
|
-
"
|
103
|
+
"quantity": qty_done,
|
114
104
|
"product_uom_id": self.product_uom_id.id,
|
115
105
|
"picking_id": picking.id,
|
116
106
|
"move_id": move.id,
|
@@ -126,7 +116,7 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
126
116
|
return 0
|
127
117
|
if self.env.context.get("planned"):
|
128
118
|
# for planned transfer we don't care about the amounts at all
|
129
|
-
return
|
119
|
+
return 0.0
|
130
120
|
search_args = [
|
131
121
|
("location_id", "=", self.origin_location_id.id),
|
132
122
|
("product_id", "=", self.product_id.id),
|
@@ -153,6 +143,4 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|
153
143
|
available_qty_lt_move_qty = (
|
154
144
|
self._compare(available_qty, self.move_quantity, rounding) == -1
|
155
145
|
)
|
156
|
-
if available_qty_lt_move_qty
|
157
|
-
return available_qty
|
158
|
-
return 0, self.move_quantity
|
146
|
+
return available_qty if available_qty_lt_move_qty else self.move_quantity
|