test: Purchase Order with Unit Price Items

- chore: Fix error message in accounts controller

(cherry picked from commit eea758f5b2)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/test_purchase_order.py
#	erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
This commit is contained in:
marination
2025-03-14 18:00:40 +01:00
committed by Mergify
parent c19065e675
commit eba73df88e
5 changed files with 112 additions and 7 deletions

View File

@@ -723,8 +723,10 @@ def set_missing_values(source, target):
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None):
has_unit_price_items = frappe.db.get_value("Purchase Order", source_name, "has_unit_price_items")
def update_item(obj, target, source_parent):
target.qty = flt(obj.qty) - flt(obj.received_qty)
target.qty = flt(obj.qty) - flt(obj.received_qty) if not has_unit_price_items else 0
target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor)
target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
target.base_amount = (
@@ -755,7 +757,9 @@ def make_purchase_receipt(source_name, target_doc=None):
"wip_composite_asset": "wip_composite_asset",
},
"postprocess": update_item,
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty)
"condition": lambda doc: (
abs(doc.received_qty) < abs(doc.qty) if not has_unit_price_items else True
)
and doc.delivered_by_supplier != 1,
},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True},

View File

@@ -5,7 +5,11 @@
import json
import frappe
<<<<<<< HEAD
from frappe.tests.utils import FrappeTestCase, change_settings
=======
from frappe.tests import IntegrationTestCase, UnitTestCase, change_settings
>>>>>>> eea758f5b2 (test: Purchase Order with Unit Price Items)
from frappe.utils import add_days, flt, getdate, nowdate
from frappe.utils.data import today
@@ -44,6 +48,21 @@ class TestPurchaseOrder(FrappeTestCase):
po.items[1].qty = 0
self.assertRaises(InvalidQtyError, po.save)
<<<<<<< HEAD
=======
# No error with qty=1
po.items[1].qty = 1
po.save()
self.assertEqual(po.items[1].qty, 1)
def test_purchase_order_zero_qty(self):
po = create_purchase_order(qty=0, do_not_save=True)
with change_settings("Buying Settings", {"allow_zero_qty_in_purchase_order": 1}):
po.save()
self.assertEqual(po.items[0].qty, 0)
>>>>>>> eea758f5b2 (test: Purchase Order with Unit Price Items)
def test_make_purchase_receipt(self):
po = create_purchase_order(do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
@@ -1199,6 +1218,82 @@ class TestPurchaseOrder(FrappeTestCase):
po.reload()
self.assertEqual(po.per_billed, 100)
@IntegrationTestCase.change_settings("Buying Settings", {"allow_zero_qty_in_purchase_order": 1})
def test_receive_zero_qty_purchase_order(self):
"""
Test the flow of a Unit Price PO and PR creation against it until completion.
Flow:
PO Qty 0 -> Receive +5 -> Receive +5 -> Update PO Qty +10 -> PO is 100% received
"""
po = create_purchase_order(qty=0)
pr = make_purchase_receipt(po.name)
self.assertEqual(pr.items[0].qty, 0)
pr.items[0].qty = 5
pr.submit()
po.reload()
self.assertEqual(po.items[0].received_qty, 5)
# PO still has qty 0, so billed % should be unset
self.assertFalse(po.per_received)
self.assertEqual(po.status, "To Receive and Bill")
# Test: PR can be made against PO as long PO qty is 0 OR PO qty > received qty
pr2 = make_purchase_receipt(po.name)
self.assertEqual(pr2.items[0].qty, 0)
pr2.items[0].qty = 5
pr2.submit()
po.reload()
self.assertEqual(po.items[0].received_qty, 10)
self.assertFalse(po.per_received)
self.assertEqual(po.status, "To Receive and Bill")
# Update PO Item Qty to 10 after receipt of items
first_item_of_po = po.items[0]
trans_item = json.dumps(
[
{
"item_code": first_item_of_po.item_code,
"rate": first_item_of_po.rate,
"qty": 10,
"docname": first_item_of_po.name,
}
]
)
update_child_qty_rate("Purchase Order", trans_item, po.name)
# PO should be updated to 100% received
po.reload()
self.assertEqual(po.items[0].qty, 10)
self.assertEqual(po.per_received, 100.0)
self.assertEqual(po.status, "To Bill")
@IntegrationTestCase.change_settings("Buying Settings", {"allow_zero_qty_in_purchase_order": 1})
def test_bill_zero_qty_purchase_order(self):
po = create_purchase_order(qty=0)
self.assertEqual(po.grand_total, 0)
self.assertFalse(po.per_billed)
self.assertEqual(po.items[0].qty, 0)
self.assertEqual(po.items[0].rate, 500)
pi = make_pi_from_po(po.name)
self.assertEqual(pi.items[0].qty, 0)
self.assertEqual(pi.items[0].rate, 500)
pi.items[0].qty = 5
pi.submit()
self.assertEqual(pi.grand_total, 2500)
po.reload()
self.assertEqual(po.items[0].amount, 0)
self.assertEqual(po.items[0].billed_amt, 2500)
# PO still has qty 0, so billed % should be unset
self.assertFalse(po.per_billed)
self.assertEqual(po.status, "To Receive and Bill")
def create_po_for_sc_testing():
from erpnext.controllers.tests.test_subcontracting_controller import (

View File

@@ -55,6 +55,7 @@ class TestRequestforQuotation(IntegrationTestCase):
with change_settings("Buying Settings", {"allow_zero_qty_in_request_for_quotation": 1}):
rfq.save()
self.assertEqual(rfq.items[0].qty, 0)
>>>>>>> 8f96c0b546 (test: Zero Qty in RFQ and Supplier Quotation)
def test_quote_status(self):
@@ -197,8 +198,8 @@ class TestRequestforQuotation(IntegrationTestCase):
supplier_doc.reload()
self.assertTrue(supplier_doc.portal_users[0].user)
@change_settings("Buying Settings", {"allow_zero_qty_in_request_for_quotation": 1})
def test_map_supplier_quotation_from_zero_qty_rfq(self):
@IntegrationTestCase.change_settings("Buying Settings", {"allow_zero_qty_in_request_for_quotation": 1})
def test_supplier_quotation_from_zero_qty_rfq(self):
rfq = make_request_for_quotation(qty=0)
sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get("suppliers")[0].supplier)

View File

@@ -50,6 +50,7 @@ class TestPurchaseOrder(IntegrationTestCase):
with change_settings("Buying Settings", {"allow_zero_qty_in_supplier_quotation": 1}):
sq.save()
self.assertEqual(sq.items[0].qty, 0)
def test_make_purchase_order(self):
sq = frappe.copy_doc(self.globalTestRecords["Supplier Quotation"][0]).insert()
@@ -73,10 +74,14 @@ class TestPurchaseOrder(IntegrationTestCase):
po.insert()
<<<<<<< HEAD
<<<<<<< HEAD
test_records = frappe.get_test_records("Supplier Quotation")
=======
@change_settings("Buying Settings", {"allow_zero_qty_in_supplier_quotation": 1})
=======
@IntegrationTestCase.change_settings("Buying Settings", {"allow_zero_qty_in_supplier_quotation": 1})
>>>>>>> eea758f5b2 (test: Purchase Order with Unit Price Items)
def test_map_purchase_order_from_zero_qty_supplier_quotation(self):
sq = frappe.copy_doc(self.globalTestRecords["Supplier Quotation"][0])
sq.items[0].qty = 0

View File

@@ -3746,9 +3746,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
)
if amount_below_billed_amt and row_rate > 0.0:
frappe.throw(
_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.").format(
child_item.idx, child_item.item_code
)
_(
"Row #{0}: Cannot set Rate if the billed amount is greater than the amount for Item {1}."
).format(child_item.idx, child_item.item_code)
)
else:
child_item.rate = row_rate