Skip to content

Commit d7380ef

Browse files
yostashiromav-adhoc
authored andcommitted
[FIX] stock_quant_manual_assign: Correct handling of immediate transfers
This commit fixes the following issues: - The previous code does not handle the immediate transfer scenario when auto_fill_qty_done is selected in the operation type. This raises a missing-record error, trying to update qty_done on non-existing move line records. - move._do_unreserve() keeps existing stock.move.line records if there is some qty_done set, which is not a desirable outcome. All the linked move line records should be unlinked before selected quants are assigned.
1 parent 308003d commit d7380ef

File tree

6 files changed

+82
-64
lines changed

6 files changed

+82
-64
lines changed

stock_quant_manual_assign/README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ Contributors
8686

8787
- Tony Gu <[email protected]>
8888

89+
* `Quartile <https://www.quartile.co>`_:
90+
91+
* Yoshi Tashiro
92+
8993
Maintainers
9094
-----------
9195

stock_quant_manual_assign/readme/CONFIGURATION.rst

Whitespace-only changes.

stock_quant_manual_assign/readme/CONTRIBUTORS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@
1515
* `Shine IT <https://www.openerp.cn>`_:
1616

1717
* Tony Gu <[email protected]>
18+
19+
* `Quartile <https://www.quartile.co>`_:
20+
21+
* Yoshi Tashiro

stock_quant_manual_assign/static/description/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,10 @@ <h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
433433
<li>Tony Gu &lt;<a class="reference external" href="mailto:tony&#64;openerp.cn">tony&#64;openerp.cn</a>&gt;</li>
434434
</ul>
435435
</li>
436+
<li><a class="reference external" href="https://www.quartile.co">Quartile</a>:<ul>
437+
<li>Yoshi Tashiro</li>
438+
</ul>
439+
</li>
436440
</ul>
437441
</div>
438442
<div class="section" id="maintainers">

stock_quant_manual_assign/tests/test_stock_quant_manual_assign.py

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,84 +9,74 @@ class TestStockQuantManualAssign(TransactionCase):
99
@classmethod
1010
def setUpClass(cls):
1111
super().setUpClass()
12-
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
13-
cls.quant_model = cls.env["stock.quant"]
14-
cls.picking_model = cls.env["stock.picking"]
15-
cls.location_model = cls.env["stock.location"]
16-
cls.move_model = cls.env["stock.move"]
1712
cls.quant_assign_wizard = cls.env["assign.manual.quants"]
18-
cls.ModelDataObj = cls.env["ir.model.data"]
1913
cls.product = cls.env["product.product"].create(
2014
{"name": "Product 4 test", "type": "product"}
2115
)
2216
cls.location_src = cls.env.ref("stock.stock_location_locations_virtual")
2317
cls.location_dst = cls.env.ref("stock.stock_location_customers")
24-
cls.picking_type_out = cls.ModelDataObj._xmlid_to_res_id(
18+
cls.picking_type_out = cls.env["ir.model.data"]._xmlid_to_res_id(
2519
"stock.picking_type_out"
2620
)
2721
cls.env["stock.picking.type"].browse(
2822
cls.picking_type_out
2923
).reservation_method = "manual"
30-
cls.location1 = cls.location_model.create(
31-
{
32-
"name": "Location 1",
33-
"usage": "internal",
34-
"location_id": cls.location_src.id,
35-
}
36-
)
37-
cls.location2 = cls.location_model.create(
38-
{
39-
"name": "Location 2",
40-
"usage": "internal",
41-
"location_id": cls.location_src.id,
42-
}
43-
)
44-
cls.location3 = cls.location_model.create(
45-
{
46-
"name": "Location 3",
47-
"usage": "internal",
48-
"location_id": cls.location_src.id,
49-
}
50-
)
24+
cls.location1 = cls._create_location(cls, "Location 1")
25+
cls.location2 = cls._create_location(cls, "Location 2")
26+
cls.location3 = cls._create_location(cls, "Location 3")
5127
cls.picking_type = cls.env.ref("stock.picking_type_out")
52-
cls.quant1 = cls.quant_model.sudo().create(
28+
cls.quant1 = cls._create_quant(cls, cls.product, 100.0, cls.location1)
29+
cls.quant2 = cls._create_quant(cls, cls.product, 100.0, cls.location2)
30+
cls.quant3 = cls._create_quant(cls, cls.product, 100.0, cls.location3)
31+
cls.picking = cls.env["stock.picking"].create(
5332
{
54-
"product_id": cls.product.id,
55-
"quantity": 100.0,
33+
"picking_type_id": cls.picking_type.id,
5634
"location_id": cls.location1.id,
35+
"location_dest_id": cls.location_dst.id,
5736
}
5837
)
59-
cls.quant2 = cls.quant_model.sudo().create(
60-
{
61-
"product_id": cls.product.id,
62-
"quantity": 100.0,
63-
"location_id": cls.location2.id,
64-
}
65-
)
66-
cls.quant3 = cls.quant_model.sudo().create(
67-
{
68-
"product_id": cls.product.id,
69-
"quantity": 100.0,
70-
"location_id": cls.location3.id,
71-
}
72-
)
73-
cls.move = cls.move_model.create(
38+
cls.move = cls.env["stock.move"].create(
7439
{
7540
"name": cls.product.name,
7641
"product_id": cls.product.id,
7742
"product_uom_qty": 400.0,
7843
"product_uom": cls.product.uom_id.id,
7944
"location_id": cls.location_src.id,
8045
"location_dest_id": cls.location_dst.id,
81-
"picking_type_id": cls.picking_type.id,
46+
"picking_id": cls.picking.id,
8247
}
8348
)
8449
cls.move._action_confirm()
8550

86-
def test_quant_assign_wizard(self):
87-
wizard = self.quant_assign_wizard.with_context(active_id=self.move.id).create(
88-
{}
51+
def _create_location(self, name):
52+
return self.env["stock.location"].create(
53+
{"name": name, "usage": "internal", "location_id": self.location_src.id}
54+
)
55+
56+
def _create_quant(self, product, qty, location):
57+
return (
58+
self.env["stock.quant"]
59+
.sudo()
60+
.create(
61+
{"product_id": product.id, "quantity": qty, "location_id": location.id}
62+
)
63+
)
64+
65+
def _create_wizard(self):
66+
return (
67+
self.env["assign.manual.quants"]
68+
.with_context(active_id=self.move.id)
69+
.create({})
8970
)
71+
72+
def _process_basic_manual_assign_steps(self, wizard):
73+
wizard.quants_lines[0].write({"selected": True})
74+
wizard.quants_lines[0]._onchange_selected()
75+
wizard.quants_lines[1].write({"selected": True, "qty": 50.0})
76+
self.assertEqual(wizard.lines_qty, 150.0)
77+
78+
def test_quant_assign_wizard(self):
79+
wizard = self._create_wizard()
9080
self.assertEqual(
9181
len(wizard.quants_lines.ids),
9282
3,
@@ -105,9 +95,7 @@ def test_quant_assign_wizard(self):
10595
self.assertEqual(wizard.move_qty, self.move.product_uom_qty)
10696

10797
def test_quant_assign_wizard_constraint(self):
108-
wizard = self.quant_assign_wizard.with_context(active_id=self.move.id).create(
109-
{}
110-
)
98+
wizard = self._create_wizard()
11199
self.assertEqual(
112100
len(wizard.quants_lines.ids),
113101
3,
@@ -129,31 +117,42 @@ def test_quant_assign_wizard_constraint(self):
129117
)
130118

131119
def test_quant_manual_assign(self):
132-
wizard = self.quant_assign_wizard.with_context(active_id=self.move.id).create(
133-
{}
134-
)
120+
wizard = self._create_wizard()
135121
self.assertEqual(
136122
len(wizard.quants_lines.ids),
137123
3,
138124
"Three quants created, three quants got by default",
139125
)
140-
wizard.quants_lines[0].write({"selected": True})
141-
wizard.quants_lines[0]._onchange_selected()
142-
wizard.quants_lines[1].write({"selected": True, "qty": 50.0})
143-
self.assertEqual(wizard.lines_qty, 150.0)
126+
self._process_basic_manual_assign_steps(wizard)
144127
self.assertEqual(wizard.move_qty, 250.0)
145128
wizard.assign_quants()
146129
self.assertAlmostEqual(
147130
len(self.move.move_line_ids),
148131
2,
149132
"There are 2 quants selected",
150133
)
134+
self.assertFalse(self.move.picking_type_id.auto_fill_qty_done)
135+
self.assertEqual(sum(self.move.move_line_ids.mapped("qty_done")), 0.0)
136+
137+
def _process_quant_manual_assign_auto_fill_qty_done(self):
138+
wizard = self._create_wizard()
139+
self._process_basic_manual_assign_steps(wizard)
140+
self.picking_type.auto_fill_qty_done = True
141+
wizard.assign_quants()
142+
self.assertTrue(self.move.picking_type_id.auto_fill_qty_done)
143+
self.assertEqual(sum(self.move.move_line_ids.mapped("qty_done")), 150.0)
144+
145+
def test_quant_manual_assign_auto_fill_qty_done_planned(self):
146+
self.assertFalse(self.picking.immediate_transfer)
147+
self._process_quant_manual_assign_auto_fill_qty_done()
148+
149+
def test_quant_manual_assign_auto_fill_qty_done_immediate(self):
150+
self.picking.immediate_transfer = True
151+
self._process_quant_manual_assign_auto_fill_qty_done()
151152

152153
def test_quant_assign_wizard_after_availability_check(self):
153154
self.move._action_assign()
154-
wizard = self.quant_assign_wizard.with_context(active_id=self.move.id).create(
155-
{}
156-
)
155+
wizard = self._create_wizard()
157156
self.assertEqual(
158157
len(wizard.quants_lines.ids),
159158
3,

stock_quant_manual_assign/wizard/assign_manual_quants.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,16 @@ def _compute_qties(self):
6060

6161
def assign_quants(self):
6262
move = self.move_id
63-
move._do_unreserve()
63+
self.move_id.move_line_ids.unlink()
6464
for line in self.quants_lines:
6565
line._assign_quant_line()
66+
if move.picking_type_id.auto_fill_qty_done:
67+
if move.from_immediate_transfer:
68+
move.quantity_done = self.lines_qty
69+
else:
70+
# Auto-fill all lines as done
71+
for ml in move.move_line_ids:
72+
ml.qty_done = ml.reserved_uom_qty
6673
move._recompute_state()
6774
move.mapped("picking_id")._compute_state()
6875
return {}

0 commit comments

Comments
 (0)