From ca94ad3a2440102f1c64229d4111c4237beec1ab Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 3 Mar 2025 13:38:38 +0530 Subject: [PATCH 1/2] fix: incorrectly billed amount in the purchase receipt (cherry picked from commit a5271fdb2e5c826ec24162ddd101ae95ed9fdcd7) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py --- .../purchase_receipt/purchase_receipt.py | 6 +- .../purchase_receipt/test_purchase_receipt.py | 132 ++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 4a9e0d08bd8..e7e17924e12 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -865,15 +865,19 @@ def get_billed_amount_against_po(po_items): if not po_items: return {} + purchase_invoice = frappe.qb.DocType("Purchase Invoice") purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item") query = ( frappe.qb.from_(purchase_invoice_item) + .inner_join(purchase_invoice) + .on(purchase_invoice_item.parent == purchase_invoice.name) .select(fn.Sum(purchase_invoice_item.amount).as_("billed_amt"), purchase_invoice_item.po_detail) .where( (purchase_invoice_item.po_detail.isin(po_items)) - & (purchase_invoice_item.docstatus == 1) + & (purchase_invoice.docstatus == 1) & (purchase_invoice_item.pr_detail.isnull()) + & (purchase_invoice.update_stock == 0) ) .groupby(purchase_invoice_item.po_detail) ).run(as_dict=1) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index adb6e690596..ac2da34785e 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2731,6 +2731,138 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(return_pr.per_billed, 100) self.assertEqual(return_pr.status, "Completed") +<<<<<<< HEAD +======= + def test_do_not_allow_to_inward_same_serial_no_multiple_times(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 0) + + item_code = make_item( + "Test Do Not Allow INWD Item 123", {"has_serial_no": 1, "serial_no_series": "SN-TDAISN-.#####"} + ).name + + pr = make_purchase_receipt(item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1) + serial_no = get_serial_nos_from_bundle(pr.items[0].serial_and_batch_bundle)[0] + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertTrue(status == "Active") + + make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=1, + serial_no=serial_no, + use_serial_batch_fields=1, + ) + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertFalse(status == "Active") + + pr = make_purchase_receipt( + item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1, do_not_submit=1 + ) + pr.items[0].serial_no = serial_no + pr.save() + + self.assertRaises(frappe.exceptions.ValidationError, pr.submit) + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) + + def test_seral_no_return_validation(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_return, + ) + + sn_item_code = make_item( + "Test Serial No for Validation", {"has_serial_no": 1, "serial_no_series": "SN-TSNFVAL-.#####"} + ).name + + pr1 = make_purchase_receipt(item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1) + pr1_serial_nos = get_serial_nos_from_bundle(pr1.items[0].serial_and_batch_bundle) + + serial_no_pr = make_purchase_receipt( + item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1 + ) + serial_no_pr_serial_nos = get_serial_nos_from_bundle(serial_no_pr.items[0].serial_and_batch_bundle) + + sn_return = make_purchase_return(serial_no_pr.name) + sn_return.items[0].qty = -1 + sn_return.items[0].received_qty = -1 + sn_return.items[0].serial_no = pr1_serial_nos[0] + sn_return.save() + self.assertRaises(frappe.ValidationError, sn_return.submit) + + sn_return = make_purchase_return(serial_no_pr.name) + sn_return.items[0].qty = -1 + sn_return.items[0].received_qty = -1 + sn_return.items[0].serial_no = serial_no_pr_serial_nos[0] + sn_return.save() + sn_return.submit() + + def test_batch_no_return_validation(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_return, + ) + + batch_item_code = make_item( + "Test Batch No for Validation", + {"has_batch_no": 1, "batch_number_series": "BT-TSNFVAL-.#####", "create_new_batch": 1}, + ).name + + pr1 = make_purchase_receipt(item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1) + batch_no = get_batch_from_bundle(pr1.items[0].serial_and_batch_bundle) + + batch_no_pr = make_purchase_receipt( + item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1 + ) + original_batch_no = get_batch_from_bundle(batch_no_pr.items[0].serial_and_batch_bundle) + + batch_return = make_purchase_return(batch_no_pr.name) + batch_return.items[0].qty = -1 + batch_return.items[0].received_qty = -1 + batch_return.items[0].batch_no = batch_no + batch_return.save() + self.assertRaises(frappe.ValidationError, batch_return.submit) + + batch_return = make_purchase_return(batch_no_pr.name) + batch_return.items[0].qty = -1 + batch_return.items[0].received_qty = -1 + batch_return.items[0].batch_no = original_batch_no + batch_return.save() + batch_return.submit() + + def test_pr_status_based_on_invoices_with_update_stock(self): + from erpnext.buying.doctype.purchase_order.purchase_order import ( + make_purchase_invoice as _make_purchase_invoice, + ) + from erpnext.buying.doctype.purchase_order.purchase_order import ( + make_purchase_receipt as _make_purchase_receipt, + ) + from erpnext.buying.doctype.purchase_order.test_purchase_order import ( + create_pr_against_po, + create_purchase_order, + ) + + item_code = "Test Item for PR Status Based on Invoices" + create_item(item_code) + + po = create_purchase_order(item_code=item_code, qty=10) + pi = _make_purchase_invoice(po.name) + pi.update_stock = 1 + pi.items[0].qty = 5 + pi.submit() + + po.reload() + self.assertEqual(po.per_billed, 50) + + pr = _make_purchase_receipt(po.name) + self.assertEqual(pr.items[0].qty, 5) + pr.submit() + pr.reload() + self.assertEqual(pr.status, "To Bill") + +>>>>>>> a5271fdb2e (fix: incorrectly billed amount in the purchase receipt) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From eebf6cf877bc371d2619917fd2c2239ebff1abf1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 3 Mar 2025 14:48:07 +0530 Subject: [PATCH 2/2] chore: fix conflicts --- .../purchase_receipt/test_purchase_receipt.py | 102 ------------------ 1 file changed, 102 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index ac2da34785e..ef690cda67d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2731,107 +2731,6 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(return_pr.per_billed, 100) self.assertEqual(return_pr.status, "Completed") -<<<<<<< HEAD -======= - def test_do_not_allow_to_inward_same_serial_no_multiple_times(self): - from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - - frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 0) - - item_code = make_item( - "Test Do Not Allow INWD Item 123", {"has_serial_no": 1, "serial_no_series": "SN-TDAISN-.#####"} - ).name - - pr = make_purchase_receipt(item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1) - serial_no = get_serial_nos_from_bundle(pr.items[0].serial_and_batch_bundle)[0] - - status = frappe.db.get_value("Serial No", serial_no, "status") - self.assertTrue(status == "Active") - - make_stock_entry( - item_code=item_code, - source=pr.items[0].warehouse, - qty=1, - serial_no=serial_no, - use_serial_batch_fields=1, - ) - - status = frappe.db.get_value("Serial No", serial_no, "status") - self.assertFalse(status == "Active") - - pr = make_purchase_receipt( - item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1, do_not_submit=1 - ) - pr.items[0].serial_no = serial_no - pr.save() - - self.assertRaises(frappe.exceptions.ValidationError, pr.submit) - - frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) - - def test_seral_no_return_validation(self): - from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( - make_purchase_return, - ) - - sn_item_code = make_item( - "Test Serial No for Validation", {"has_serial_no": 1, "serial_no_series": "SN-TSNFVAL-.#####"} - ).name - - pr1 = make_purchase_receipt(item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1) - pr1_serial_nos = get_serial_nos_from_bundle(pr1.items[0].serial_and_batch_bundle) - - serial_no_pr = make_purchase_receipt( - item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1 - ) - serial_no_pr_serial_nos = get_serial_nos_from_bundle(serial_no_pr.items[0].serial_and_batch_bundle) - - sn_return = make_purchase_return(serial_no_pr.name) - sn_return.items[0].qty = -1 - sn_return.items[0].received_qty = -1 - sn_return.items[0].serial_no = pr1_serial_nos[0] - sn_return.save() - self.assertRaises(frappe.ValidationError, sn_return.submit) - - sn_return = make_purchase_return(serial_no_pr.name) - sn_return.items[0].qty = -1 - sn_return.items[0].received_qty = -1 - sn_return.items[0].serial_no = serial_no_pr_serial_nos[0] - sn_return.save() - sn_return.submit() - - def test_batch_no_return_validation(self): - from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( - make_purchase_return, - ) - - batch_item_code = make_item( - "Test Batch No for Validation", - {"has_batch_no": 1, "batch_number_series": "BT-TSNFVAL-.#####", "create_new_batch": 1}, - ).name - - pr1 = make_purchase_receipt(item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1) - batch_no = get_batch_from_bundle(pr1.items[0].serial_and_batch_bundle) - - batch_no_pr = make_purchase_receipt( - item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1 - ) - original_batch_no = get_batch_from_bundle(batch_no_pr.items[0].serial_and_batch_bundle) - - batch_return = make_purchase_return(batch_no_pr.name) - batch_return.items[0].qty = -1 - batch_return.items[0].received_qty = -1 - batch_return.items[0].batch_no = batch_no - batch_return.save() - self.assertRaises(frappe.ValidationError, batch_return.submit) - - batch_return = make_purchase_return(batch_no_pr.name) - batch_return.items[0].qty = -1 - batch_return.items[0].received_qty = -1 - batch_return.items[0].batch_no = original_batch_no - batch_return.save() - batch_return.submit() - def test_pr_status_based_on_invoices_with_update_stock(self): from erpnext.buying.doctype.purchase_order.purchase_order import ( make_purchase_invoice as _make_purchase_invoice, @@ -2862,7 +2761,6 @@ class TestPurchaseReceipt(FrappeTestCase): pr.reload() self.assertEqual(pr.status, "To Bill") ->>>>>>> a5271fdb2e (fix: incorrectly billed amount in the purchase receipt) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier