Merge pull request #39559 from ruthra-kumar/prevent_cr_note_with_different_account_to_sales_invoice
fix: prevent Return Invoices(Credit/Debit Note) from using a different account
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 992fbe6..5da6f8b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1995,6 +1995,21 @@
self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
+ def test_debit_note_with_account_mismatch(self):
+ new_creditors = create_account(
+ parent_account="Accounts Payable - _TC",
+ account_name="Creditors 2",
+ company="_Test Company",
+ account_type="Payable",
+ )
+ pi = make_purchase_invoice(qty=1, rate=1000)
+ dr_note = make_purchase_invoice(
+ qty=-1, rate=1000, is_return=1, return_against=pi.name, do_not_save=True
+ )
+ dr_note.credit_to = new_creditors
+
+ self.assertRaises(frappe.ValidationError, dr_note.save)
+
def test_debit_note_without_item(self):
pi = make_purchase_invoice(item_name="_Test Item", qty=10, do_not_submit=True)
pi.items[0].item_code = ""
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 672fec2..8c3aede 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1550,6 +1550,19 @@
self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500)
+ def test_return_invoice_with_account_mismatch(self):
+ debtors2 = create_account(
+ parent_account="Accounts Receivable - _TC",
+ account_name="Debtors 2",
+ company="_Test Company",
+ account_type="Receivable",
+ )
+ si = create_sales_invoice(qty=1, rate=1000)
+ cr_note = create_sales_invoice(
+ qty=-1, rate=1000, is_return=1, return_against=si.name, debit_to=debtors2, do_not_save=True
+ )
+ self.assertRaises(frappe.ValidationError, cr_note.save)
+
def test_gle_made_when_asset_is_returned(self):
create_asset_data()
asset = create_asset(item_code="Macbook Pro")
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 1ed719d..a0569ec 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -202,6 +202,7 @@
self.validate_party()
self.validate_currency()
self.validate_party_account_currency()
+ self.validate_return_against_account()
if self.doctype in ["Purchase Invoice", "Sales Invoice"]:
if invalid_advances := [
@@ -350,6 +351,20 @@
for bundle in bundles:
frappe.delete_doc("Serial and Batch Bundle", bundle.name)
+ def validate_return_against_account(self):
+ if (
+ self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against
+ ):
+ cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to"
+ cr_dr_account_label = "Debit To" if self.doctype == "Sales Invoice" else "Credit To"
+ cr_dr_account = self.get(cr_dr_account_field)
+ if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != cr_dr_account:
+ frappe.throw(
+ _("'{0}' account: '{1}' should match the Return Against Invoice").format(
+ frappe.bold(cr_dr_account_label), frappe.bold(cr_dr_account)
+ )
+ )
+
def validate_deferred_income_expense_account(self):
field_map = {
"Sales Invoice": "deferred_revenue_account",