diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 00145ffa47c..681314dbd5f 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1138,10 +1138,14 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate returned_qty = flt(item_wise_returned_qty.get(item.name)) returned_amount = flt(returned_qty) * flt(item.rate) pending_amount = flt(item.amount) - returned_amount - if buying_settings.bill_for_rejected_quantity_in_purchase_invoice: - pending_amount = flt(item.amount) - total_billable_amount = abs(flt(item.amount)) + # When rejected qty is billable, its value is part of the billable base too + rejected_amount = 0.0 + if buying_settings.bill_for_rejected_quantity_in_purchase_invoice: + rejected_amount = flt(item.rejected_qty * item.rate, item.precision("amount")) + pending_amount = flt(item.amount) + rejected_amount + + total_billable_amount = abs(flt(item.amount) + rejected_amount) if pending_amount > 0: total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt @@ -1151,9 +1155,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate if pr_doc.get("is_return") and not total_amount and total_billed_amount: total_amount = total_billed_amount - amount = item.amount - if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"): - amount += flt(item.rejected_qty * item.rate, item.precision("amount")) + amount = flt(item.amount) + rejected_amount if adjust_incoming_rate: adjusted_amt = 0.0 diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index f30158b4f51..d8bb216a430 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -605,6 +605,42 @@ class TestPurchaseReceipt(FrappeTestCase): return_pr.cancel() pr.cancel() + def test_per_billed_for_fully_rejected_receipt(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage + + bill_rejected = frappe.db.get_single_value( + "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" + ) + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 1) + + try: + # Fully rejected receipt: accepted qty 0, whole qty in rejected warehouse + pr = make_purchase_receipt( + received_qty=10, + qty=0, + rejected_qty=10, + rate=9.5, + rejected_warehouse="_Test Warehouse 1 - _TC", + do_not_save=True, + ) + pr.items[0].warehouse = "" + pr.submit() + + # Bill the rejected qty (10 x 9.5) directly against the receipt item + pr.items[0].db_set("billed_amt", 95) + update_billing_percentage(pr) + + pr.load_from_db() + # Billing the rejected qty must not push per_billed above 100 + self.assertEqual(pr.per_billed, 100) + self.assertEqual(pr.status, "Completed") + + pr.cancel() + finally: + frappe.db.set_single_value( + "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", bill_rejected + ) + def test_purchase_receipt_for_rejected_gle_without_accepted_warehouse(self): from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse