Skip to content

Commit

Permalink
[FIX] stock_request_purchase: Define the correct quantity of allocati…
Browse files Browse the repository at this point in the history
…ons 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
  • Loading branch information
victoralmau committed Nov 6, 2024
1 parent 8b97bdc commit c9c1675
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 11 deletions.
39 changes: 28 additions & 11 deletions stock_request_purchase/models/purchase_order_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero, float_round


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

def _prepare_stock_moves(self, picking):
"""We define the allocation_ids with the corresponding quantity."""
res = super()._prepare_stock_moves(picking)

if not self.stock_request_ids:
return res

Check warning on line 35 in stock_request_purchase/models/purchase_order_line.py

View check run for this annotation

Codecov / codecov/patch

stock_request_purchase/models/purchase_order_line.py#L35

Added line #L35 was not covered by tests
precision = self.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
requests_qty = sum(self.stock_request_ids.mapped("product_qty"))
moves_qty = sum(re["product_uom_qty"] for re in res)
diff_qty = moves_qty - requests_qty
for re in res:
re["allocation_ids"] = [
(
0,
0,
{
"stock_request_id": request.id,
"requested_product_uom_qty": request.product_qty,
},
allocations_data = []
for request in self.stock_request_ids:
qty = request.product_qty
# Only add the extra (proportional) quantity if there is pending qty
if not float_is_zero(diff_qty, precision_digits=precision):
extra_qty = diff_qty * (request.product_qty / requests_qty)
extra_qty = float_round(extra_qty, precision_digits=precision)
qty += extra_qty
allocations_data.append(
(
0,
0,
{
"stock_request_id": request.id,
"requested_product_uom_qty": qty,
},
)
)
for request in self.stock_request_ids
]
re["allocation_ids"] = allocations_data
return res

@api.model
Expand Down
209 changes: 209 additions & 0 deletions stock_request_purchase/tests/test_stock_request_purchase.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,167 @@ def test_create_request_02(self):
self.assertEqual(stock_request_2.qty_in_progress, 0.0)
self.assertEqual(stock_request_2.qty_done, stock_request_2.product_uom_qty)

def test_create_request_03(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 8.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 4.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 2.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
],
}
order = (
self.env["stock.request.order"]
.with_user(self.stock_request_user)
.create(vals)
)
order.action_confirm()
self.assertEqual(order.state, "open")
self.assertEqual(len(order.purchase_ids), 1)
request_1 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 8)
request_2 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 4)
request_3 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 2)
purchase = order.purchase_ids.sudo()
purchase_line = purchase.order_line
self.assertEqual(purchase_line.product_qty, 14)
purchase.button_confirm()
self.assertEqual(len(request_1.allocation_ids), 1)
self.assertEqual(request_1.qty_in_progress, 8)
self.assertEqual(len(request_2.allocation_ids), 1)
self.assertEqual(request_2.qty_in_progress, 4)
self.assertEqual(len(request_3.allocation_ids), 1)
self.assertEqual(request_3.qty_in_progress, 2)
purchase_line.write({"product_qty": 28})
self.assertEqual(len(request_1.allocation_ids), 2)
self.assertEqual(request_1.qty_in_progress, 16)
self.assertEqual(len(request_2.allocation_ids), 2)
self.assertEqual(request_2.qty_in_progress, 8)
self.assertEqual(len(request_3.allocation_ids), 2)
self.assertEqual(request_3.qty_in_progress, 4)
picking = purchase.picking_ids
picking.move_line_ids.qty_done = 28
picking.button_validate()
self.assertEqual(request_1.qty_in_progress, 0)
self.assertEqual(request_1.qty_done, 16)
self.assertEqual(request_1.state, "done")
self.assertEqual(request_2.qty_in_progress, 0)
self.assertEqual(request_2.qty_done, 8)
self.assertEqual(request_2.state, "done")
self.assertEqual(request_3.qty_in_progress, 0)
self.assertEqual(request_3.qty_done, 4)
self.assertEqual(request_3.state, "done")
self.assertEqual(order.state, "done")

def test_create_request_04(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 7.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
],
}
order = (
self.env["stock.request.order"]
.with_user(self.stock_request_user)
.create(vals)
)
order.action_confirm()
self.assertEqual(order.state, "open")
self.assertEqual(len(order.purchase_ids), 1)
request_1 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 7)
request_2 = order.stock_request_ids.filtered(lambda x: x.product_uom_qty == 5)
purchase = order.purchase_ids.sudo()
purchase_line = purchase.order_line
self.assertEqual(purchase_line.product_qty, 12)
purchase.button_confirm()
self.assertEqual(len(request_1.allocation_ids), 1)
self.assertEqual(request_1.qty_in_progress, 7)
self.assertEqual(len(request_2.allocation_ids), 1)
self.assertEqual(request_2.qty_in_progress, 5)
purchase_line.write({"product_qty": 13})
self.assertEqual(len(request_1.allocation_ids), 2)
self.assertEqual(request_1.qty_in_progress, 7.58)
self.assertEqual(len(request_2.allocation_ids), 2)
self.assertEqual(request_2.qty_in_progress, 5.42)
picking = purchase.picking_ids
picking.move_line_ids.qty_done = 13
picking.button_validate()
self.assertEqual(request_1.qty_in_progress, 0)
self.assertEqual(request_1.qty_done, 7.58)
self.assertEqual(request_1.state, "done")
self.assertEqual(request_2.qty_in_progress, 0)
self.assertEqual(request_2.qty_done, 5.42)
self.assertEqual(request_2.state, "done")
self.assertEqual(order.state, "done")

def test_create_request_cancel_purchase(self):
vals = {
"product_id": self.product.id,
Expand All @@ -218,6 +379,54 @@ def test_create_request_cancel_purchase(self):
stock_request.action_cancel()
self.assertEqual(stock_request.purchase_ids.state, "cancel")

def test_update_purchase_order_line_qty(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 10.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
),
],
}
order = (
self.env["stock.request.order"]
.with_user(self.stock_request_user)
.create(vals)
)
order.action_confirm()
self.assertEqual(order.state, "open")
self.assertEqual(len(order.purchase_ids), 1)
request = order.stock_request_ids
purchase = order.purchase_ids.sudo()
purchase_line = purchase.order_line
self.assertEqual(purchase_line.product_qty, 10)
purchase.button_confirm()
self.assertEqual(len(request.allocation_ids), 1)
self.assertEqual(request.qty_in_progress, 10)
purchase_line.write({"product_qty": 12})
self.assertEqual(len(request.allocation_ids), 2)
self.assertEqual(request.qty_in_progress, 12)
picking = purchase.picking_ids
picking.move_line_ids.qty_done = 12
picking.button_validate()
self.assertEqual(request.qty_in_progress, 0)
self.assertEqual(request.qty_done, 12)
self.assertEqual(request.state, "done")

def test_unlink_purchase_order_line(self):
"""
Test that when a purchase order line is unlinked,
Expand Down

0 comments on commit c9c1675

Please sign in to comment.