Skip to content

Commit c9c1675

Browse files
committed
[FIX] stock_request_purchase: Define the correct quantity of allocations to be created from purchase order lines
Example use case: - Create stock request for a product with quantity 10 - Confirm purchase order - Change purchase order line to quantity 12 - "In progress" quantity of the stock request must be 12 (not 22=10+12) TT51567
1 parent 8b97bdc commit c9c1675

File tree

2 files changed

+237
-11
lines changed

2 files changed

+237
-11
lines changed

stock_request_purchase/models/purchase_order_line.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from odoo import _, api, fields, models
55
from odoo.exceptions import ValidationError
6+
from odoo.tools import float_is_zero, float_round
67

78

89
class PurchaseOrderLine(models.Model):
@@ -28,20 +29,36 @@ def unlink(self):
2829
return res
2930

3031
def _prepare_stock_moves(self, picking):
32+
"""We define the allocation_ids with the corresponding quantity."""
3133
res = super()._prepare_stock_moves(picking)
32-
34+
if not self.stock_request_ids:
35+
return res
36+
precision = self.env["decimal.precision"].precision_get(
37+
"Product Unit of Measure"
38+
)
39+
requests_qty = sum(self.stock_request_ids.mapped("product_qty"))
40+
moves_qty = sum(re["product_uom_qty"] for re in res)
41+
diff_qty = moves_qty - requests_qty
3342
for re in res:
34-
re["allocation_ids"] = [
35-
(
36-
0,
37-
0,
38-
{
39-
"stock_request_id": request.id,
40-
"requested_product_uom_qty": request.product_qty,
41-
},
43+
allocations_data = []
44+
for request in self.stock_request_ids:
45+
qty = request.product_qty
46+
# Only add the extra (proportional) quantity if there is pending qty
47+
if not float_is_zero(diff_qty, precision_digits=precision):
48+
extra_qty = diff_qty * (request.product_qty / requests_qty)
49+
extra_qty = float_round(extra_qty, precision_digits=precision)
50+
qty += extra_qty
51+
allocations_data.append(
52+
(
53+
0,
54+
0,
55+
{
56+
"stock_request_id": request.id,
57+
"requested_product_uom_qty": qty,
58+
},
59+
)
4260
)
43-
for request in self.stock_request_ids
44-
]
61+
re["allocation_ids"] = allocations_data
4562
return res
4663

4764
@api.model

stock_request_purchase/tests/test_stock_request_purchase.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,167 @@ def test_create_request_02(self):
201201
self.assertEqual(stock_request_2.qty_in_progress, 0.0)
202202
self.assertEqual(stock_request_2.qty_done, stock_request_2.product_uom_qty)
203203

204+
def test_create_request_03(self):
205+
expected_date = fields.Datetime.now()
206+
vals = {
207+
"company_id": self.main_company.id,
208+
"warehouse_id": self.warehouse.id,
209+
"location_id": self.warehouse.lot_stock_id.id,
210+
"expected_date": expected_date,
211+
"stock_request_ids": [
212+
(
213+
0,
214+
0,
215+
{
216+
"product_id": self.product.id,
217+
"product_uom_id": self.product.uom_id.id,
218+
"product_uom_qty": 8.0,
219+
"company_id": self.main_company.id,
220+
"warehouse_id": self.warehouse.id,
221+
"location_id": self.warehouse.lot_stock_id.id,
222+
"expected_date": expected_date,
223+
},
224+
),
225+
(
226+
0,
227+
0,
228+
{
229+
"product_id": self.product.id,
230+
"product_uom_id": self.product.uom_id.id,
231+
"product_uom_qty": 4.0,
232+
"company_id": self.main_company.id,
233+
"warehouse_id": self.warehouse.id,
234+
"location_id": self.warehouse.lot_stock_id.id,
235+
"expected_date": expected_date,
236+
},
237+
),
238+
(
239+
0,
240+
0,
241+
{
242+
"product_id": self.product.id,
243+
"product_uom_id": self.product.uom_id.id,
244+
"product_uom_qty": 2.0,
245+
"company_id": self.main_company.id,
246+
"warehouse_id": self.warehouse.id,
247+
"location_id": self.warehouse.lot_stock_id.id,
248+
"expected_date": expected_date,
249+
},
250+
),
251+
],
252+
}
253+
order = (
254+
self.env["stock.request.order"]
255+
.with_user(self.stock_request_user)
256+
.create(vals)
257+
)
258+
order.action_confirm()
259+
self.assertEqual(order.state, "open")
260+
self.assertEqual(len(order.purchase_ids), 1)
261+
request_1 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 8)
262+
request_2 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 4)
263+
request_3 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 2)
264+
purchase = order.purchase_ids.sudo()
265+
purchase_line = purchase.order_line
266+
self.assertEqual(purchase_line.product_qty, 14)
267+
purchase.button_confirm()
268+
self.assertEqual(len(request_1.allocation_ids), 1)
269+
self.assertEqual(request_1.qty_in_progress, 8)
270+
self.assertEqual(len(request_2.allocation_ids), 1)
271+
self.assertEqual(request_2.qty_in_progress, 4)
272+
self.assertEqual(len(request_3.allocation_ids), 1)
273+
self.assertEqual(request_3.qty_in_progress, 2)
274+
purchase_line.write({"product_qty": 28})
275+
self.assertEqual(len(request_1.allocation_ids), 2)
276+
self.assertEqual(request_1.qty_in_progress, 16)
277+
self.assertEqual(len(request_2.allocation_ids), 2)
278+
self.assertEqual(request_2.qty_in_progress, 8)
279+
self.assertEqual(len(request_3.allocation_ids), 2)
280+
self.assertEqual(request_3.qty_in_progress, 4)
281+
picking = purchase.picking_ids
282+
picking.move_line_ids.qty_done = 28
283+
picking.button_validate()
284+
self.assertEqual(request_1.qty_in_progress, 0)
285+
self.assertEqual(request_1.qty_done, 16)
286+
self.assertEqual(request_1.state, "done")
287+
self.assertEqual(request_2.qty_in_progress, 0)
288+
self.assertEqual(request_2.qty_done, 8)
289+
self.assertEqual(request_2.state, "done")
290+
self.assertEqual(request_3.qty_in_progress, 0)
291+
self.assertEqual(request_3.qty_done, 4)
292+
self.assertEqual(request_3.state, "done")
293+
self.assertEqual(order.state, "done")
294+
295+
def test_create_request_04(self):
296+
expected_date = fields.Datetime.now()
297+
vals = {
298+
"company_id": self.main_company.id,
299+
"warehouse_id": self.warehouse.id,
300+
"location_id": self.warehouse.lot_stock_id.id,
301+
"expected_date": expected_date,
302+
"stock_request_ids": [
303+
(
304+
0,
305+
0,
306+
{
307+
"product_id": self.product.id,
308+
"product_uom_id": self.product.uom_id.id,
309+
"product_uom_qty": 7.0,
310+
"company_id": self.main_company.id,
311+
"warehouse_id": self.warehouse.id,
312+
"location_id": self.warehouse.lot_stock_id.id,
313+
"expected_date": expected_date,
314+
},
315+
),
316+
(
317+
0,
318+
0,
319+
{
320+
"product_id": self.product.id,
321+
"product_uom_id": self.product.uom_id.id,
322+
"product_uom_qty": 5.0,
323+
"company_id": self.main_company.id,
324+
"warehouse_id": self.warehouse.id,
325+
"location_id": self.warehouse.lot_stock_id.id,
326+
"expected_date": expected_date,
327+
},
328+
),
329+
],
330+
}
331+
order = (
332+
self.env["stock.request.order"]
333+
.with_user(self.stock_request_user)
334+
.create(vals)
335+
)
336+
order.action_confirm()
337+
self.assertEqual(order.state, "open")
338+
self.assertEqual(len(order.purchase_ids), 1)
339+
request_1 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 7)
340+
request_2 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 5)
341+
purchase = order.purchase_ids.sudo()
342+
purchase_line = purchase.order_line
343+
self.assertEqual(purchase_line.product_qty, 12)
344+
purchase.button_confirm()
345+
self.assertEqual(len(request_1.allocation_ids), 1)
346+
self.assertEqual(request_1.qty_in_progress, 7)
347+
self.assertEqual(len(request_2.allocation_ids), 1)
348+
self.assertEqual(request_2.qty_in_progress, 5)
349+
purchase_line.write({"product_qty": 13})
350+
self.assertEqual(len(request_1.allocation_ids), 2)
351+
self.assertEqual(request_1.qty_in_progress, 7.58)
352+
self.assertEqual(len(request_2.allocation_ids), 2)
353+
self.assertEqual(request_2.qty_in_progress, 5.42)
354+
picking = purchase.picking_ids
355+
picking.move_line_ids.qty_done = 13
356+
picking.button_validate()
357+
self.assertEqual(request_1.qty_in_progress, 0)
358+
self.assertEqual(request_1.qty_done, 7.58)
359+
self.assertEqual(request_1.state, "done")
360+
self.assertEqual(request_2.qty_in_progress, 0)
361+
self.assertEqual(request_2.qty_done, 5.42)
362+
self.assertEqual(request_2.state, "done")
363+
self.assertEqual(order.state, "done")
364+
204365
def test_create_request_cancel_purchase(self):
205366
vals = {
206367
"product_id": self.product.id,
@@ -218,6 +379,54 @@ def test_create_request_cancel_purchase(self):
218379
stock_request.action_cancel()
219380
self.assertEqual(stock_request.purchase_ids.state, "cancel")
220381

382+
def test_update_purchase_order_line_qty(self):
383+
expected_date = fields.Datetime.now()
384+
vals = {
385+
"company_id": self.main_company.id,
386+
"warehouse_id": self.warehouse.id,
387+
"location_id": self.warehouse.lot_stock_id.id,
388+
"expected_date": expected_date,
389+
"stock_request_ids": [
390+
(
391+
0,
392+
0,
393+
{
394+
"product_id": self.product.id,
395+
"product_uom_id": self.product.uom_id.id,
396+
"product_uom_qty": 10.0,
397+
"company_id": self.main_company.id,
398+
"warehouse_id": self.warehouse.id,
399+
"location_id": self.warehouse.lot_stock_id.id,
400+
"expected_date": expected_date,
401+
},
402+
),
403+
],
404+
}
405+
order = (
406+
self.env["stock.request.order"]
407+
.with_user(self.stock_request_user)
408+
.create(vals)
409+
)
410+
order.action_confirm()
411+
self.assertEqual(order.state, "open")
412+
self.assertEqual(len(order.purchase_ids), 1)
413+
request = order.stock_request_ids
414+
purchase = order.purchase_ids.sudo()
415+
purchase_line = purchase.order_line
416+
self.assertEqual(purchase_line.product_qty, 10)
417+
purchase.button_confirm()
418+
self.assertEqual(len(request.allocation_ids), 1)
419+
self.assertEqual(request.qty_in_progress, 10)
420+
purchase_line.write({"product_qty": 12})
421+
self.assertEqual(len(request.allocation_ids), 2)
422+
self.assertEqual(request.qty_in_progress, 12)
423+
picking = purchase.picking_ids
424+
picking.move_line_ids.qty_done = 12
425+
picking.button_validate()
426+
self.assertEqual(request.qty_in_progress, 0)
427+
self.assertEqual(request.qty_done, 12)
428+
self.assertEqual(request.state, "done")
429+
221430
def test_unlink_purchase_order_line(self):
222431
"""
223432
Test that when a purchase order line is unlinked,

0 commit comments

Comments
 (0)