From 228108ad4cb285329bacf8f2f72c650ebfc99ae1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 09:20:26 +0000 Subject: [PATCH] fix(accounts): stop counting received qty on non-stock invoice returns (backport #58924) (#58928) Co-authored-by: Mihir Kandoi --- .../purchase_invoice/purchase_invoice.js | 30 +++++++++---------- .../purchase_invoice/test_purchase_invoice.py | 17 +++++++++++ .../controllers/sales_and_purchase_return.py | 7 ++++- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index cb7f9d6af76..fa254f5f356 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -78,7 +78,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. const me = this; super.refresh(); - hide_fields(this.frm.doc); + hide_fields(this.frm); // Show / Hide button this.show_general_ledger(); erpnext.accounts.ledger_preview.show_accounting_ledger_preview(this.frm); @@ -435,7 +435,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. } is_paid() { - hide_fields(this.frm.doc); + hide_fields(this.frm); if (cint(this.frm.doc.is_paid)) { this.frm.set_value("allocate_advances_automatically", 0); this.frm.set_value("payment_terms_template", ""); @@ -499,28 +499,26 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice); // Hide Fields // ------------ -function hide_fields(doc) { - var parent_fields = ["due_date", "is_opening", "advances_section", "from_date", "to_date"]; +function hide_fields(frm) { + const doc = frm.doc; + const parent_fields = ["due_date", "is_opening", "advances_section", "from_date", "to_date"]; if (cint(doc.is_paid) == 1) { - hide_field(parent_fields); + frm.toggle_display(parent_fields, false); } else { - for (var i in parent_fields) { - var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]]; - if (!docfield.hidden) unhide_field(parent_fields[i]); + for (const fieldname of parent_fields) { + const docfield = frappe.meta.docfield_map[doc.doctype][fieldname]; + if (!docfield.hidden) frm.toggle_display(fieldname, true); } } - var item_fields_stock = ["warehouse_section", "received_qty", "rejected_qty"]; + const item_fields_stock = ["warehouse_section", "received_qty", "rejected_qty"]; - if (cur_frm.fields_dict["items"]) { - cur_frm.fields_dict["items"].grid.set_column_disp( - item_fields_stock, - cint(doc.update_stock) == 1 || cint(doc.is_return) == 1 ? true : false - ); + if (frm.fields_dict["items"]) { + frm.fields_dict["items"].grid.set_column_disp(item_fields_stock, cint(doc.update_stock) == 1); } - cur_frm.refresh_fields(); + frm.refresh_fields(); } cur_frm.fields_dict.cash_bank_account.get_query = function (doc) { @@ -736,7 +734,7 @@ frappe.ui.form.on("Purchase Invoice", { }, update_stock: function (frm) { - hide_fields(frm.doc); + hide_fields(frm); frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock ? true : false); }, diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 44d94499516..e918d84b0ca 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -3081,6 +3081,23 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertRaises(StockOverReturnError, return_doc.save) + def test_partial_returns_ignore_received_qty_without_update_stock(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + invoice = make_purchase_invoice(qty=10, received_qty=10) + + first_return = make_return_doc(invoice.doctype, invoice.name) + first_return.items[0].qty = -4 + first_return.save().submit() + + self.assertEqual(first_return.items[0].received_qty, -10) + + second_return = make_return_doc(invoice.doctype, invoice.name) + second_return.items[0].qty = -6 + second_return.save().submit() + + self.assertEqual(second_return.docstatus, 1) + def test_apply_discount_on_grand_total(self): """ To test if after applying discount on grand total, diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 4d348112446..5bd9c535919 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -190,7 +190,12 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items): if (doc.doctype == "Purchase Invoice" or doc.doctype == "Sales Invoice") and not doc.update_stock: fields = ["qty"] - if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]: + tracks_accepted_rejected_split = doc.doctype in ( + "Purchase Receipt", + "Subcontracting Receipt", + ) or (doc.doctype == "Purchase Invoice" and doc.update_stock) + + if tracks_accepted_rejected_split: if not args.get("return_qty_from_rejected_warehouse"): fields.extend(["received_qty", "rejected_qty"]) else: