odoo-addon-stock-move-location 16.0.1.4.1__py3-none-any.whl → 18.0.1.0.0.4__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 (22) hide show
  1. odoo/addons/stock_move_location/README.rst +8 -7
  2. odoo/addons/stock_move_location/__manifest__.py +1 -1
  3. odoo/addons/stock_move_location/i18n/sk.po +301 -0
  4. odoo/addons/stock_move_location/i18n/stock_move_location.pot +37 -10
  5. odoo/addons/stock_move_location/init_hook.py +1 -5
  6. odoo/addons/stock_move_location/models/stock_picking.py +9 -7
  7. odoo/addons/stock_move_location/readme/CONTRIBUTORS.md +2 -0
  8. odoo/addons/stock_move_location/readme/USAGE.md +0 -1
  9. odoo/addons/stock_move_location/static/description/index.html +6 -5
  10. odoo/addons/stock_move_location/tests/test_common.py +16 -13
  11. odoo/addons/stock_move_location/tests/test_move_location.py +19 -24
  12. odoo/addons/stock_move_location/tests/test_stock_fillwithstock.py +23 -28
  13. odoo/addons/stock_move_location/views/stock_picking.xml +1 -3
  14. odoo/addons/stock_move_location/views/stock_picking_type_views.xml +4 -7
  15. odoo/addons/stock_move_location/wizard/stock_move_location.py +46 -65
  16. odoo/addons/stock_move_location/wizard/stock_move_location.xml +60 -32
  17. odoo/addons/stock_move_location/wizard/stock_move_location_line.py +18 -30
  18. {odoo_addon_stock_move_location-16.0.1.4.1.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.4.dist-info}/METADATA +14 -12
  19. {odoo_addon_stock_move_location-16.0.1.4.1.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.4.dist-info}/RECORD +21 -20
  20. {odoo_addon_stock_move_location-16.0.1.4.1.dist-info → odoo_addon_stock_move_location-18.0.1.0.0.4.dist-info}/WHEEL +1 -1
  21. odoo_addon_stock_move_location-18.0.1.0.0.4.dist-info/top_level.txt +1 -0
  22. odoo_addon_stock_move_location-16.0.1.4.1.dist-info/top_level.txt +0 -1
@@ -1,17 +1,17 @@
1
1
  # Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
2
2
  # Copyright 2018 Camptocamp SA
3
3
  # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
4
+ from odoo.tests import Form
4
5
 
5
- from odoo.tests import Form, common
6
+ from odoo.addons.base.tests.common import BaseCommon
6
7
 
7
8
 
8
- class TestsCommon(common.TransactionCase):
9
+ class TestsCommon(BaseCommon):
9
10
  @classmethod
10
11
  def setUpClass(cls):
11
12
  super().setUpClass()
12
- cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
13
13
  cls.location_obj = cls.env["stock.location"]
14
- product_obj = cls.env["product.product"]
14
+ cls.product_obj = cls.env["product.product"]
15
15
  cls.wizard_obj = cls.env["wiz.stock.move.location"]
16
16
  cls.quant_obj = cls.env["stock.quant"]
17
17
  cls.company = cls.env.ref("base.main_company")
@@ -43,14 +43,14 @@ class TestsCommon(common.TransactionCase):
43
43
  }
44
44
  )
45
45
  cls.uom_unit = cls.env.ref("uom.product_uom_unit")
46
- cls.product_no_lots = product_obj.create(
47
- {"name": "Pineapple", "type": "product", "tracking": "none"}
46
+ cls.product_no_lots = cls.product_obj.create(
47
+ {"name": "Pineapple", "is_storable": True, "tracking": "none"}
48
48
  )
49
- cls.product_lots = product_obj.create(
50
- {"name": "Apple", "type": "product", "tracking": "lot"}
49
+ cls.product_lots = cls.product_obj.create(
50
+ {"name": "Apple", "is_storable": True, "tracking": "lot"}
51
51
  )
52
- cls.product_package = product_obj.create(
53
- {"name": "Orange", "type": "product", "tracking": "lot"}
52
+ cls.product_package = cls.product_obj.create(
53
+ {"name": "Orange", "is_storable": True, "tracking": "lot"}
54
54
  )
55
55
  cls.lot1 = cls.env["stock.lot"].create(
56
56
  {
@@ -73,8 +73,8 @@ class TestsCommon(common.TransactionCase):
73
73
  "company_id": cls.company.id,
74
74
  }
75
75
  )
76
- cls.product_package = product_obj.create(
77
- {"name": "Orange", "type": "product", "tracking": "lot"}
76
+ cls.product_package = cls.product_obj.create(
77
+ {"name": "Orange", "is_storable": True, "tracking": "lot"}
78
78
  )
79
79
  cls.lot4 = cls.env["stock.lot"].create(
80
80
  {
@@ -157,12 +157,15 @@ class TestsCommon(common.TransactionCase):
157
157
  amount,
158
158
  )
159
159
 
160
- def _create_wizard(self, origin_location, destination_location):
160
+ def _create_wizard(
161
+ self, origin_location, destination_location, exclude_reserved_qty=False
162
+ ):
161
163
  move_location_wizard = self.env["wiz.stock.move.location"]
162
164
  return move_location_wizard.create(
163
165
  {
164
166
  "origin_location_id": origin_location.id,
165
167
  "destination_location_id": destination_location.id,
168
+ "exclude_reserved_qty": exclude_reserved_qty,
166
169
  }
167
170
  )
168
171
 
@@ -84,20 +84,18 @@ class TestMoveLocation(TestsCommon):
84
84
  wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
85
85
  wizard.onchange_origin_location()
86
86
  self.assertEqual(len(wizard.stock_move_location_line_ids), 7)
87
- wizard._onchange_destination_location_id()
88
- self.assertEqual(len(wizard.stock_move_location_line_ids), 7)
89
87
  dest_location_line = wizard.stock_move_location_line_ids.mapped(
90
88
  "destination_location_id"
91
89
  )
92
90
  self.assertEqual(dest_location_line, wizard.destination_location_id)
93
- wizard._onchange_origin_location_id()
91
+ wizard.clear_lines()
94
92
  self.assertEqual(len(wizard.stock_move_location_line_ids), 0)
95
93
 
96
94
  def test_wizard_onchange_origin_location(self):
97
95
  """Test a product that have existing quants with undefined quantity."""
98
96
 
99
97
  product_not_available = self.env["product.product"].create(
100
- {"name": "Mango", "type": "product", "tracking": "none"}
98
+ {"name": "Mango", "is_storable": True, "tracking": "none"}
101
99
  )
102
100
  self.quant_obj.create(
103
101
  {
@@ -137,7 +135,7 @@ class TestMoveLocation(TestsCommon):
137
135
  )
138
136
  picking_lines = sorted(
139
137
  [
140
- (line.product_id.id, line.lot_id.id, line.reserved_uom_qty)
138
+ (line.product_id.id, line.lot_id.id, line.quantity)
141
139
  for line in picking.move_line_ids
142
140
  ],
143
141
  key=lambda x: (x[0], x[1]),
@@ -148,13 +146,13 @@ class TestMoveLocation(TestsCommon):
148
146
  "Mismatch between move location lines and move lines",
149
147
  )
150
148
  self.assertEqual(
151
- sorted(picking.move_line_ids.mapped("reserved_uom_qty")),
149
+ sorted(picking.move_line_ids.mapped("quantity")),
152
150
  [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 123.0],
153
151
  )
154
152
 
155
153
  def test_planned_transfer_strict(self):
156
154
  product = self.env["product.product"].create(
157
- {"name": "Test", "type": "product", "tracking": "lot"}
155
+ {"name": "Test", "is_storable": True, "tracking": "lot"}
158
156
  )
159
157
  lot = self.env["stock.lot"].create(
160
158
  {
@@ -193,14 +191,14 @@ class TestMoveLocation(TestsCommon):
193
191
  location_line.move_quantity,
194
192
  ]
195
193
  line = picking.move_line_ids
196
- picking_lines = [line.product_id.id, line.lot_id.id, line.reserved_uom_qty]
194
+ picking_lines = [line.product_id.id, line.lot_id.id, line.quantity]
197
195
  self.assertEqual(
198
196
  wizard_lines,
199
197
  picking_lines,
200
198
  "Mismatch between move location lines and move lines",
201
199
  )
202
200
  self.assertEqual(
203
- picking.move_line_ids.reserved_uom_qty,
201
+ picking.move_line_ids.quantity,
204
202
  10.0,
205
203
  )
206
204
 
@@ -214,14 +212,10 @@ class TestMoveLocation(TestsCommon):
214
212
  location_lines.unlink()
215
213
  wizard.action_move_location()
216
214
  picking = wizard.picking_id
217
- self.assertEqual(picking.state, "assigned")
218
- self.assertEqual(
219
- len(wizard.stock_move_location_line_ids), len(picking.move_line_ids)
220
- )
221
- self.assertEqual(
222
- picking.move_line_ids.mapped("reserved_uom_qty"),
223
- [0.0],
224
- )
215
+ # Planned transfer state is "confirmed"
216
+ # move lines (quantity is zero) are removed
217
+ self.assertEqual(picking.state, "confirmed")
218
+ self.assertFalse(picking.move_line_ids)
225
219
 
226
220
  def test_quant_transfer(self):
227
221
  """Test quants transfer."""
@@ -241,10 +235,8 @@ class TestMoveLocation(TestsCommon):
241
235
  wizard.onchange_origin_location()
242
236
  self.assertEqual(len(lines), 3)
243
237
  wizard.destination_location_id = self.internal_loc_1
244
- wizard._onchange_destination_location_id()
245
238
  self.assertEqual(lines.mapped("destination_location_id"), self.internal_loc_1)
246
239
  wizard.origin_location_id = self.internal_loc_2
247
- wizard._onchange_destination_location_id()
248
240
  self.assertEqual(len(lines), 3)
249
241
 
250
242
  def test_readonly_location_computation(self):
@@ -276,7 +268,7 @@ class TestMoveLocation(TestsCommon):
276
268
  lambda p: p.product_id == self.product_no_lots
277
269
  )[0]
278
270
  self.assertEqual(
279
- putaway_line.destination_location_id, self.internal_loc_2_shelf
271
+ putaway_line.destination_location_id, wizard.destination_location_id
280
272
  )
281
273
  picking_action = wizard.action_move_location()
282
274
  picking = self.env["stock.picking"].browse(picking_action["res_id"])
@@ -302,6 +294,7 @@ class TestMoveLocation(TestsCommon):
302
294
 
303
295
  # Create and assign a delivery picking to reserve some quantities
304
296
  delivery_picking = self._create_picking(delivery_order_type)
297
+ # delivery_picking.location_id = wh_stock_shelf_1
305
298
  delivery_move = self.env["stock.move"].create(
306
299
  {
307
300
  "name": "Delivery move",
@@ -315,12 +308,14 @@ class TestMoveLocation(TestsCommon):
315
308
  )
316
309
  delivery_picking.action_confirm()
317
310
  self.assertEqual(delivery_picking.state, "assigned")
311
+ self.assertEqual(delivery_move.move_line_ids.location_id, wh_stock_shelf_1)
318
312
 
319
313
  # Move all quantities to other location using module's wizard
320
314
  wizard = self._create_wizard(wh_stock_shelf_1, wh_stock_shelf_2)
321
315
  wizard.onchange_origin_location()
322
316
  wizard.action_move_location()
323
- self.assertEqual(delivery_picking.state, "confirmed")
317
+ self.assertEqual(delivery_picking.state, "assigned")
318
+ self.assertEqual(delivery_move.move_line_ids.location_id, wh_stock_shelf_2)
324
319
 
325
320
  # Do a planned transfer to move quantities to other location
326
321
  # without using module's wizard
@@ -344,8 +339,8 @@ class TestMoveLocation(TestsCommon):
344
339
  self.assertEqual(delivery_picking.state, "confirmed")
345
340
  internal_picking.action_confirm()
346
341
  internal_picking.action_assign()
347
- internal_picking.move_line_ids.qty_done = (
348
- internal_picking.move_line_ids.reserved_uom_qty
342
+ internal_picking.move_line_ids.quantity = (
343
+ internal_picking.move_line_ids.quantity
349
344
  )
350
345
  internal_picking.button_validate()
351
346
  self.assertEqual(internal_picking.state, "done")
@@ -354,5 +349,5 @@ class TestMoveLocation(TestsCommon):
354
349
  self.assertEqual(delivery_picking.state, "assigned")
355
350
  # The old reserved quantities must be in new location after confirm wizard
356
351
  self.assertEqual(len(delivery_move.move_line_ids), 1)
357
- self.assertEqual(delivery_move.move_line_ids.reserved_uom_qty, 20.0)
352
+ self.assertEqual(delivery_move.move_line_ids.quantity, 20.0)
358
353
  self.assertEqual(delivery_move.move_line_ids.location_id, wh_stock_shelf_3)
@@ -2,63 +2,58 @@
2
2
  # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3
3
 
4
4
 
5
- import odoo.tests.common as common
5
+ from odoo.addons.base.tests.common import BaseCommon
6
6
 
7
7
 
8
- class TestFillwithStock(common.TransactionCase):
9
- def setUp(self):
10
- super(TestFillwithStock, self).setUp()
11
- self.env = self.env(
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
- self.stock_location = self.env.ref("stock.stock_location_stock")
19
- self.pack_location = self.env.ref("stock.location_pack_zone")
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
- self.shelf1_location = self.env["stock.location"].create(
16
+ cls.shelf1_location = cls.env["stock.location"].create(
22
17
  {
23
18
  "name": "Test location",
24
19
  "usage": "internal",
25
- "location_id": self.stock_location.id,
20
+ "location_id": cls.stock_location.id,
26
21
  }
27
22
  )
28
23
 
29
- self.product1 = self.env["product.product"].create(
24
+ cls.product1 = cls.env["product.product"].create(
30
25
  {
31
26
  "name": "Product A",
32
- "type": "product",
27
+ "is_storable": True,
33
28
  }
34
29
  )
35
- self.product2 = self.env["product.product"].create(
30
+ cls.product2 = cls.env["product.product"].create(
36
31
  {
37
32
  "name": "Product B",
38
- "type": "product",
33
+ "is_storable": True,
39
34
  }
40
35
  )
41
36
 
42
- self.env["stock.quant"].create(
37
+ cls.env["stock.quant"].create(
43
38
  {
44
- "product_id": self.product1.id,
45
- "location_id": self.shelf1_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
- self.env["stock.quant"].create(
45
+ cls.env["stock.quant"].create(
51
46
  {
52
- "product_id": self.product1.id,
53
- "location_id": self.shelf1_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
- self.env["stock.quant"].create(
53
+ cls.env["stock.quant"].create(
59
54
  {
60
- "product_id": self.product2.id,
61
- "location_id": self.shelf1_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
- 10.0,
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
- states="draft"
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="show_operations" position="after">
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
- <xpath expr="//div[hasclass('o_kanban_primary_left')]" position="inside">
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
- </xpath>
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", "in", ("internal", "outgoing")),
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
- exclude_reserved_qty = self.env.context.get("only_reserved_qty", False)
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 _product, quant in groupby(quants, lambda r: r.product_id):
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
- move = self._create_move(picking, lines)
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 l: (
260
- l.move_quantity > l.max_quantity - l.reserved_quantity
261
- and not l.origin_location_id.should_bypass_reservation()
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
- ("qty_done", ">", 0.0),
263
+ ("quantity", ">", 0.0),
264
+ ("picking_id", "!=", picking.id),
274
265
  ]
275
266
  )
276
267
  moves_to_unreserve = move_lines.mapped("move_id")
@@ -281,17 +272,10 @@ class StockMoveLocationWizard(models.TransientModel):
281
272
 
282
273
  def action_move_location(self):
283
274
  self.ensure_one()
284
- if not self.picking_id:
285
- picking = self._create_picking()
286
- else:
287
- picking = self.picking_id
288
- # Prevent putaway rules to be excuted when we don't need to
289
- picking = picking.with_context(
290
- avoid_putaway_rules=not self.apply_putaway_strategy
291
- )
275
+ picking = self.picking_id if self.picking_id else self._create_picking()
292
276
  self._create_moves(picking)
293
277
  if not self.env.context.get("planned"):
294
- moves_to_reassign = self._unreserve_moves()
278
+ moves_to_reassign = self._unreserve_moves(picking)
295
279
  picking.button_validate()
296
280
  moves_to_reassign._action_assign()
297
281
  self.picking_id = picking
@@ -322,7 +306,7 @@ class StockMoveLocationWizard(models.TransientModel):
322
306
  "quantity:sum",
323
307
  "reserved_quantity:sum",
324
308
  ],
325
- groupby=["product_id", "lot_id", "package_id", "owner_id"],
309
+ groupby=["id", "product_id", "lot_id", "package_id", "owner_id"],
326
310
  orderby="id",
327
311
  lazy=False,
328
312
  )
@@ -339,12 +323,18 @@ class StockMoveLocationWizard(models.TransientModel):
339
323
  and self.destination_location_id._get_putaway_strategy(product).id
340
324
  or self.destination_location_id.id
341
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
+ )
342
331
  product_data.append(
343
332
  {
344
333
  "product_id": product.id,
345
- "move_quantity": group.get("quantity") or 0,
346
- "max_quantity": group.get("quantity") or 0,
347
- "reserved_quantity": group.get("reserved_quantity"),
334
+ "move_quantity": max_qty,
335
+ "max_quantity": max_qty,
336
+ "reserved_quantity": res_qty,
337
+ "total_quantity": total_qty,
348
338
  "origin_location_id": self.origin_location_id.id,
349
339
  "destination_location_id": location_dest_id,
350
340
  # cursor returns None instead of False
@@ -361,21 +351,7 @@ class StockMoveLocationWizard(models.TransientModel):
361
351
  )
362
352
  return product_data
363
353
 
364
- def _reset_stock_move_location_lines(self):
365
- lines = []
366
- line_model = self.env["wiz.stock.move.location.line"]
367
- for line_val in self._get_stock_move_location_lines_values():
368
- if line_val.get("max_quantity") <= 0:
369
- continue
370
- line = line_model.create(line_val)
371
- line.max_quantity = line.get_max_quantity()
372
- line.reserved_quantity = line.reserved_quantity
373
- lines.append(line)
374
- self.update(
375
- {"stock_move_location_line_ids": [(6, 0, [line.id for line in lines])]}
376
- )
377
-
378
- @api.onchange("origin_location_id")
354
+ @api.onchange("origin_location_id", "exclude_reserved_qty")
379
355
  def onchange_origin_location(self):
380
356
  # Get origin_location_disable context key to prevent load all origin
381
357
  # location products when user opens the wizard from stock quants to
@@ -384,7 +360,12 @@ class StockMoveLocationWizard(models.TransientModel):
384
360
  not self.env.context.get("origin_location_disable")
385
361
  and self.origin_location_id
386
362
  ):
387
- self._reset_stock_move_location_lines()
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})
388
369
 
389
370
  def clear_lines(self):
390
371
  self._clear_lines()