From 0a7c3581da02d1ff442934ffa2e76583b84e03a8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:11:12 +0530 Subject: [PATCH] Avoid status updation for purchase invoice from paid to unpaid by issuing a paid debit note against it (backport #54382) (#55575) Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com> --- .../purchase_invoice/purchase_invoice.js | 19 +++++++++++ .../purchase_invoice/purchase_invoice.py | 34 ++++++++++++++++--- erpnext/controllers/accounts_controller.py | 2 +- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index a25af18e5fc..195b29e38c6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -608,6 +608,25 @@ frappe.ui.form.on("Purchase Invoice", { }; }); + frm.set_query("write_off_account", function (doc) { + return { + filters: { + report_type: "Profit and Loss", + is_group: 0, + company: doc.company, + }, + }; + }); + + frm.set_query("write_off_cost_center", function (doc) { + return { + filters: { + is_group: 0, + company: doc.company, + }, + }; + }); + frm.fields_dict["items"].grid.get_field("deferred_expense_account").get_query = function (doc) { return { filters: { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b80eadc7ae0..12d90c975f1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -287,6 +287,7 @@ class PurchaseInvoice(BuyingController): self.validate_expense_account() self.set_against_expense_account() self.validate_write_off_account() + self.validate_write_off_cost_center() self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount") self.set_status() self.validate_purchase_receipt_if_update_stock() @@ -657,6 +658,27 @@ class PurchaseInvoice(BuyingController): if self.write_off_amount and not self.write_off_account: throw(_("Please enter Write Off Account")) + if not self.write_off_account: + return + + doc = frappe.db.get_value( + "Account", self.write_off_account, ["report_type", "is_group", "company"], as_dict=True + ) + + if not doc or doc.report_type != "Profit and Loss" or doc.is_group or doc.company != self.company: + throw(_("Please enter a valid Write Off Account")) + + def validate_write_off_cost_center(self): + if not self.write_off_cost_center: + return + + doc = frappe.db.get_value( + "Cost Center", self.write_off_cost_center, ["is_group", "company"], as_dict=True + ) + + if not doc or doc.is_group or doc.company != self.company: + throw(_("Please enter a valid Write Off Cost Center")) + def check_prev_docstatus(self): for d in self.get("items"): if d.purchase_order: @@ -737,6 +759,7 @@ class PurchaseInvoice(BuyingController): def validate_for_repost(self): self.validate_write_off_account() + self.validate_write_off_cost_center() self.validate_expense_account() validate_docs_for_voucher_types(["Purchase Invoice"]) validate_docs_for_deferred_accounting([], [self.name]) @@ -850,7 +873,9 @@ class PurchaseInvoice(BuyingController): if update_outstanding == "No": update_voucher_outstanding( voucher_type=self.doctype, - voucher_no=self.return_against if cint(self.is_return) and self.return_against else self.name, + voucher_no=self.return_against + if (cint(self.is_return) and self.return_against) + else self.name, account=self.credit_to, party_type="Supplier", party=self.supplier, @@ -1533,6 +1558,9 @@ class PurchaseInvoice(BuyingController): def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries if cint(self.is_paid) and self.cash_bank_account and self.paid_amount: + against_voucher = self.name + if self.is_return and self.return_against and not self.update_outstanding_for_self: + against_voucher = self.return_against bank_account_currency = get_account_currency(self.cash_bank_account) # CASH, make payment entries gl_entries.append( @@ -1547,9 +1575,7 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency == self.company_currency else self.paid_amount, "debit_in_transaction_currency": self.paid_amount, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": against_voucher, "against_voucher_type": self.doctype, "cost_center": self.cost_center, "project": self.project, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d0d7d58f30d..6bcf437e0d0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -178,7 +178,7 @@ class AccountsController(TransactionBase): if not get_meta(self.doctype).has_field("outstanding_amount"): return - if self.get("is_return") and self.return_against and not self.get("is_pos"): + if self.get("is_return") and self.return_against and not (self.get("is_pos") or self.get("is_paid")): against_voucher_outstanding = frappe.get_value( self.doctype, self.return_against, "outstanding_amount" )