From b3035ec7d4f087ee7d583ef32b25f2235d648b39 Mon Sep 17 00:00:00 2001 From: Logesh Periyasamy Date: Tue, 5 Aug 2025 20:55:44 +0530 Subject: [PATCH 1/9] Merge pull request #48761 from aerele/exchange-gain-or-loss-on-repost fix: prevent gain or loss entry cancellation upon reposting (cherry picked from commit a8d17b7590265151c24550acfe14b7c2b2961306) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../repost_accounting_ledger.py | 4 +- .../doctype/sales_invoice/sales_invoice.py | 1 - .../sales_invoice/test_sales_invoice.py | 54 +++++++++++++++++++ erpnext/controllers/stock_controller.py | 5 +- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index c76cf5d6e80..94baf0b8588 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -151,7 +151,7 @@ def start_repost(account_repost_doc=str) -> None: if doc.doctype in ["Sales Invoice", "Purchase Invoice"]: if not repost_doc.delete_cancelled_entries: doc.docstatus = 2 - doc.make_gl_entries_on_cancel() + doc.make_gl_entries_on_cancel(from_repost=True) doc.docstatus = 1 if doc.doctype == "Sales Invoice": @@ -163,7 +163,7 @@ def start_repost(account_repost_doc=str) -> None: elif doc.doctype == "Purchase Receipt": if not repost_doc.delete_cancelled_entries: doc.docstatus = 2 - doc.make_gl_entries_on_cancel() + doc.make_gl_entries_on_cancel(from_repost=True) doc.docstatus = 1 doc.make_gl_entries(from_repost=True) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index dbeeda00f49..08ad0a905ff 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1025,7 +1025,6 @@ class SalesInvoice(SellingController): self.make_exchange_gain_loss_journal() elif self.docstatus == 2: - cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name)) make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) if update_outstanding == "No": diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 2b9af8669d3..192607b0552 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3889,6 +3889,60 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(invoice.outstanding_amount, 0) + def test_system_generated_exchange_gain_or_loss_je_after_repost(self): + from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import ( + update_repost_settings, + ) + + update_repost_settings() + + si = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=80, + ) + + pe = get_payment_entry("Sales Invoice", si.name) + pe.reference_no = "10" + pe.reference_date = nowdate() + pe.paid_from_account_currency = si.currency + pe.paid_to_account_currency = "INR" + pe.source_exchange_rate = 85 + pe.target_exchange_rate = 1 + pe.paid_amount = si.outstanding_amount + pe.received_amount = 85 + pe.insert() + pe.submit() + + ral = frappe.new_doc("Repost Accounting Ledger") + ral.company = si.company + ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name}) + ral.save() + ral.submit() + + je = frappe.qb.DocType("Journal Entry") + jea = frappe.qb.DocType("Journal Entry Account") + q = ( + ( + frappe.qb.from_(je) + .join(jea) + .on(je.name == jea.parent) + .select(je.docstatus) + .where( + (je.voucher_type == "Exchange Gain Or Loss") + & (jea.reference_name == si.name) + & (jea.reference_type == "Sales Invoice") + & (je.is_system_generated == 1) + ) + ) + .limit(1) + .run() + ) + + self.assertEqual(q[0][0], 1) + def check_gl_entries(doc, voucher_no, expected_gle, posting_date): gl_entries = frappe.db.sql( diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 38e2d99f8c7..90e76dc7cd1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -520,8 +520,9 @@ class StockController(AccountsController): make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher) - def make_gl_entries_on_cancel(self): - cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name)) + def make_gl_entries_on_cancel(self, from_repost=False): + if not from_repost: + cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name)) if frappe.db.sql( """select name from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", From 06c3839abc0178a6459010653bb0f6e70c03a130 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 6 Aug 2025 16:41:39 +0530 Subject: [PATCH 2/9] fix: timeout while submitting purchase receipt (cherry picked from commit c433943c466f91486aba333947c05be3ccd1795c) # Conflicts: # erpnext/stock/doctype/purchase_receipt/purchase_receipt.json --- .../stock/doctype/purchase_receipt/purchase_receipt.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index fabeb74462f..5e6f4287554 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1077,7 +1077,8 @@ "no_copy": 1, "options": "Delivery Note", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "scan_barcode", @@ -1242,7 +1243,11 @@ "idx": 261, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-11-13 16:55:14.129055", +======= + "modified": "2025-08-06 16:41:02.690658", +>>>>>>> c433943c46 (fix: timeout while submitting purchase receipt) "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 22e63099eedd97319cc96a689806668fdffbf2a6 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 6 Aug 2025 22:24:45 +0530 Subject: [PATCH 3/9] chore: fix conflicts --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 5e6f4287554..e2eec2e5085 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1243,11 +1243,7 @@ "idx": 261, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-11-13 16:55:14.129055", -======= "modified": "2025-08-06 16:41:02.690658", ->>>>>>> c433943c46 (fix: timeout while submitting purchase receipt) "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 89d00ee4a229c4ac8352173338cf4be87bd3b453 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 7 Aug 2025 01:37:33 +0530 Subject: [PATCH 4/9] fix: nonetype error on applying presentation_currency filter on financial statements and trial balance report (cherry picked from commit d7e22de44c23582a606144a258d82d2931c8c157) --- erpnext/accounts/report/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 92c305e6cd8..337beaa701b 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -118,7 +118,7 @@ def convert_to_presentation_currency(gl_entries, currency_info, filters=None): len(account_currencies) == 1 and account_currency == presentation_currency and not exchange_gain_or_loss - ) and not filters.get("show_amount_in_company_currency"): + ) and not (filters and filters.get("show_amount_in_company_currency")): entry["debit"] = debit_in_account_currency entry["credit"] = credit_in_account_currency else: From e0d9a47ff7df6bba391fe3662e2340e4142582ad Mon Sep 17 00:00:00 2001 From: mithili Date: Mon, 28 Jul 2025 17:10:46 +0530 Subject: [PATCH 5/9] fix(purchase invoice): filter only enabled account (cherry picked from commit c3111db6e2d9135c01f93d6f94c1c459b1ee7b37) --- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 5 ++++- erpnext/controllers/queries.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 11bfc5e8dde..c14b8528f38 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -29,7 +29,10 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. this.frm.set_query("expense_account", "items", function () { return { query: "erpnext.controllers.queries.get_expense_account", - filters: { company: doc.company }, + filters: { + company: doc.company, + disabled: 0, + }, }; }); } diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index be965e41b3e..96b2629b17a 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -640,6 +640,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" + condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}" return frappe.db.sql( f"""select tabAccount.name from `tabAccount` From 8fc8aa2dfd4009af77d8660fb05a8d7485878018 Mon Sep 17 00:00:00 2001 From: mithili Date: Wed, 30 Jul 2025 11:19:23 +0530 Subject: [PATCH 6/9] fix: add condition to fetch active accounts (cherry picked from commit 7c8dd86a35ee3029404943542358ec66430edec1) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 1 - erpnext/controllers/queries.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c14b8528f38..cc274335fe5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,7 +31,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. query: "erpnext.controllers.queries.get_expense_account", filters: { company: doc.company, - disabled: 0, }, }; }); diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 96b2629b17a..1e0e01211e6 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -640,14 +640,13 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): condition = "" if filters.get("company"): condition += "and tabAccount.company = %(company)s" - condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}" return frappe.db.sql( f"""select tabAccount.name from `tabAccount` where (tabAccount.report_type = "Profit and Loss" or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress")) and tabAccount.is_group=0 - and tabAccount.docstatus!=2 + and tabAccount.disabled = 0 and tabAccount.{searchfield} LIKE %(txt)s {condition} {get_match_cond(doctype)}""", {"company": filters.get("company", ""), "txt": "%" + txt + "%"}, From a230a0d94ca6a0b4bab042a089788ae725781c90 Mon Sep 17 00:00:00 2001 From: mithili Date: Mon, 4 Aug 2025 18:48:48 +0530 Subject: [PATCH 7/9] chore: add back filter (cherry picked from commit 23308f6d10e1e804c969cca928ca110de03c52b2) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index cc274335fe5..c14b8528f38 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,6 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. query: "erpnext.controllers.queries.get_expense_account", filters: { company: doc.company, + disabled: 0, }, }; }); From 92ee871b79cfe0d96cd1313c038da6305cb7f942 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Fri, 1 Aug 2025 17:01:11 +0530 Subject: [PATCH 8/9] fix(tax withholding details): avoid voucher duplication (cherry picked from commit 88370162437652fd16ffde3d77eaca2025e06c3c) --- .../report/tds_payable_monthly/tds_payable_monthly.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index b380959a3a8..39f7be87e9e 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -45,6 +45,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_ gle_map = get_gle_map(tds_docs) out = [] + entries = {} for name, details in gle_map.items(): for entry in details: tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 @@ -119,8 +120,13 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_ "supplier_invoice_date": bill_date, } ) - out.append(row) + key = entry.voucher_no + if key in entries: + entries[key]["tax_amount"] += tax_amount + else: + entries[key] = row + out = list(entries.values()) out.sort(key=lambda x: (x["section_code"], x["transaction_date"])) return out From 5eeaaf47573757386172ed4c51a091a7098ea90c Mon Sep 17 00:00:00 2001 From: Vignesh S Date: Tue, 12 Aug 2025 16:25:59 +0530 Subject: [PATCH 9/9] chore: fix text case failure casued by mergify --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 192607b0552..dd8acec196e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3912,7 +3912,7 @@ class TestSalesInvoice(FrappeTestCase): pe.source_exchange_rate = 85 pe.target_exchange_rate = 1 pe.paid_amount = si.outstanding_amount - pe.received_amount = 85 + pe.received_amount = 8500 pe.insert() pe.submit()