From b969662b6c2d6f90279b149d3096ed490f8ee2bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 06:42:18 +0000 Subject: [PATCH 01/15] fix: add warehouse vaildation for repack entry (backport #54866) (#54900) * fix: add warehouse vaildation for repack entry (#54866) (cherry picked from commit bc07b2d3e585e2b86d974dea940676a1255da133) # Conflicts: # erpnext/stock/doctype/stock_entry/stock_entry.py * chore: resolve conflicts --------- Co-authored-by: Pandiyan P Co-authored-by: Mihir Kandoi --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index ee832b8c6bb..a367dc2dc95 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -774,7 +774,7 @@ class StockEntry(StockController): else: frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) - if self.purpose == "Manufacture": + if self.purpose in ["Manufacture", "Repack"]: if d.is_finished_item or d.is_scrap_item: d.s_warehouse = None if not d.t_warehouse: From 6d3cd7d38aa6c5b8ada63ad2f8c0cb0701e833c2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 09:58:45 +0000 Subject: [PATCH 02/15] =?UTF-8?q?Revert=20"fix:=20debit=20credit=20not=20e?= =?UTF-8?q?qual=20in=20purchase=20transactions=20for=20mult=E2=80=A6=20(ba?= =?UTF-8?q?ckport=20#54906)=20(#54907)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "fix: debit credit not equal in purchase transactions for mult… (#54906) * Revert "fix: debit credit not equal in purchase transactions for multi currency" This reverts commit 75bcea57f4d7600334eec941fd15aa8b17dd6279. * Revert "test: add test case" This reverts commit 1d30a202c329183af7d1523fb49b1f8be2bbc761. * Revert "fix: include rejected qty in tax (purchase receipt)" This reverts commit 8c9a88abbedf6babcfa1d1fea84335768af4067e. (cherry picked from commit cf5e8ce87846ae4557087dc465ee78332f6e97df) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py * chore: resolve conflicts --------- Co-authored-by: Mihir Kandoi --- erpnext/controllers/buying_controller.py | 12 +------- erpnext/controllers/taxes_and_totals.py | 21 ++------------ .../purchase_receipt/purchase_receipt.py | 9 +----- .../purchase_receipt/test_purchase_receipt.py | 29 +------------------ 4 files changed, 5 insertions(+), 66 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index c1c3e182c79..dea76428d90 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -364,17 +364,7 @@ class BuyingController(SubcontractingController): get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0 ) - net_rate = ( - flt( - (item.base_net_amount / item.received_qty) * item.qty, - item.precision("base_net_amount"), - ) - if item.received_qty - and frappe.get_single_value( - "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" - ) - else item.base_net_amount - ) + net_rate = item.base_net_amount if item.sales_incoming_rate: # for internal transfer net_rate = item.qty * item.sales_incoming_rate diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 03befbe851a..54aee03e083 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -183,10 +183,6 @@ class calculate_taxes_and_totals: return if not self.discount_amount_applied: - bill_for_rejected_quantity_in_purchase_invoice = frappe.get_single_value( - "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" - ) - do_not_round_fields = ["valuation_rate", "incoming_rate"] for item in self.doc.items: @@ -244,13 +240,7 @@ class calculate_taxes_and_totals: elif not item.qty and self.doc.get("is_debit_note"): item.amount = flt(item.rate, item.precision("amount")) else: - qty = ( - (item.qty + item.rejected_qty) - if bill_for_rejected_quantity_in_purchase_invoice - and self.doc.doctype == "Purchase Receipt" - else item.qty - ) - item.amount = flt(item.rate * qty, item.precision("amount")) + item.amount = flt(item.rate * item.qty, item.precision("amount")) item.net_amount = item.amount @@ -382,16 +372,9 @@ class calculate_taxes_and_totals: self.doc.total ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0 - bill_for_rejected_quantity_in_purchase_invoice = frappe.get_single_value( - "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" - ) for item in self._items: self.doc.total += item.amount - self.doc.total_qty += ( - (item.qty + item.rejected_qty) - if bill_for_rejected_quantity_in_purchase_invoice and self.doc.doctype == "Purchase Receipt" - else item.qty - ) + self.doc.total_qty += item.qty self.doc.base_total += item.base_amount self.doc.net_total += item.net_amount self.doc.base_net_total += item.base_net_amount diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index b805f9cde3d..1e7e7e0bce8 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -510,14 +510,7 @@ class PurchaseReceipt(BuyingController): else flt(item.net_amount, item.precision("net_amount")) ) - outgoing_amount = ( - flt((item.base_net_amount / item.received_qty) * item.qty, item.precision("base_net_amount")) - if item.received_qty - and frappe.get_single_value( - "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" - ) - else item.base_net_amount - ) + outgoing_amount = item.base_net_amount if self.is_internal_transfer() and item.valuation_rate: outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse)) credit_amount = outgoing_amount diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 2d2e68bb0ea..c5527dbd43c 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4539,7 +4539,7 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(srbnb_cost, 1500) - def test_valuation_rate_for_rejected_materials_without_accepted_materials(self): + def test_valuation_rate_for_rejected_materials_withoout_accepted_materials(self): item = make_item("Test Item with Rej Material Valuation WO Accepted", {"is_stock_item": 1}) company = "_Test Company with perpetual inventory" @@ -5106,33 +5106,6 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(row.warehouse, "_Test Warehouse 1 - _TC") self.assertEqual(row.incoming_rate, 100) - def test_bill_for_rejected_quantity_in_purchase_invoice(self): - item_code = make_item("Test Rejected Qty", {"is_stock_item": 1}).name - - frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0) - pr = make_purchase_receipt( - item_code=item_code, - qty=10, - rejected_qty=2, - rate=10, - warehouse="_Test Warehouse - _TC", - ) - - self.assertEqual(pr.total_qty, 10) - self.assertEqual(pr.total, 100) - - frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 1) - pr = make_purchase_receipt( - item_code=item_code, - qty=10, - rejected_qty=2, - rate=10, - warehouse="_Test Warehouse - _TC", - ) - - self.assertEqual(pr.total_qty, 12) - self.assertEqual(pr.total, 120) - def test_different_exchange_rate_in_pr_and_pi(self): from erpnext.accounts.doctype.account.test_account import create_account From 48ed07816dbf4432bdcc0b32e41bc59d7581c940 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 15:20:13 +0000 Subject: [PATCH 03/15] fix(stock): add whole number quantity validation in Stock Reconciliation (backport #54922) (#54924) fix(stock): add whole number quantity validation in Stock Reconciliation (#54922) (cherry picked from commit f9dec730422c912c67e9de25647fc2ff4c8d930b) Co-authored-by: Pandiyan P --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 00b5c9bd5a1..dfbbdc9254e 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -82,6 +82,7 @@ class StockReconciliation(StockController): self.set_total_qty_and_amount() self.validate_putaway_capacity() self.validate_inventory_dimension() + self.validate_uom_is_integer("stock_uom", "qty") if self._action == "submit": self.validate_reserved_stock() From d1d448018710c6f06b9687980e573aa0f4adbfcc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 15 May 2026 10:26:20 +0530 Subject: [PATCH 04/15] refactor: flag to disable opening balance calculation (cherry picked from commit 28a2230d0221088ea424b71e2a783c622fbfc405) --- .../report/general_ledger/general_ledger.js | 6 ++++++ .../report/general_ledger/general_ledger.py | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index cebdad3744f..c8470bc4c3f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -176,10 +176,16 @@ frappe.query_reports["General Ledger"] = { fieldtype: "Check", default: 1, }, + { + fieldname: "disable_opening_balance_calculation", + label: __("Disable Opening Balance Calculation"), + fieldtype: "Check", + }, { fieldname: "show_opening_entries", label: __("Show Opening Entries"), fieldtype: "Check", + depends_on: "eval: !doc.disable_opening_balance_calculation", }, { fieldname: "include_default_book_entries", diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d28311d8876..3756fb8fc9f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -279,7 +279,15 @@ def get_conditions(filters): if filters.get("party"): conditions.append("party in %(party)s") - if not ( + if filters.get("disable_opening_balance_calculation"): + if not ignore_is_opening: + conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')") + else: + conditions.append("posting_date >=%(from_date)s") + + # opening balance calculation is done only if filtered on account/party + # so from_date filter is not applied + elif not ( filters.get("account") or filters.get("party") or filters.get("categorize_by") in ["Categorize by Account", "Categorize by Party"] @@ -528,7 +536,11 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot group_by_value = gle.get(group_by) gle.voucher_type = gle.voucher_type - if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries): + if gle.posting_date < from_date or ( + cstr(gle.is_opening) == "Yes" + and not show_opening_entries + and not filters.disable_opening_balance_calculation + ): if not group_by_voucher_consolidated: update_value_in_dict(gle_map[group_by_value].totals, "opening", gle, True) update_value_in_dict(gle_map[group_by_value].totals, "closing", gle, True) From 651af67b26a2decd818862011f358f73ccefb468 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 10:33:07 +0000 Subject: [PATCH 05/15] fix(payment_entry): fix paid/received amount calculation for multi-currency accounts (backport #54963) (#54969) * fix(payment_entry): `paid_amount` and `received_amount` calculation depending upon `account_currency` (cherry picked from commit 69642860ee5100438bf938df9fa9f457b23b02c1) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.json * chore: resolve conflicts --------- Co-authored-by: diptanilsaha --- .../doctype/payment_entry/payment_entry.js | 104 ++++++++---------- .../doctype/payment_entry/payment_entry.json | 4 +- 2 files changed, 45 insertions(+), 63 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 60c8e47f8f0..80a8388eea4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -725,31 +725,12 @@ frappe.ui.form.on("Payment Entry", { if (!frm.doc.paid_from_account_currency || !frm.doc.company) return; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if (frm.doc.paid_from_account_currency == company_currency) { - frm.set_value("source_exchange_rate", 1); - } else if (frm.doc.paid_from) { - if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) { - let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency; - frappe.call({ - method: "erpnext.setup.utils.get_exchange_rate", - args: { - from_currency: frm.doc.paid_from_account_currency, - to_currency: company_currency, - transaction_date: frm.doc.posting_date, - }, - callback: function (r, rt) { - frm.set_value("source_exchange_rate", r.message); - }, - }); - } else { - frm.events.set_current_exchange_rate( - frm, - "source_exchange_rate", - frm.doc.paid_from_account_currency, - company_currency - ); - } - } + frm.events.set_current_exchange_rate( + frm, + "source_exchange_rate", + frm.doc.paid_from_account_currency, + company_currency + ); }, paid_to_account_currency: function (frm) { @@ -781,49 +762,24 @@ frappe.ui.form.on("Payment Entry", { posting_date: function (frm) { frm.events.paid_from_account_currency(frm); + frm.events.paid_to_account_currency(frm); }, source_exchange_rate: function (frm) { - let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if (frm.doc.paid_amount) { - frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); - // target exchange rate should always be same as source if both account currencies is same - if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { - frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); - frm.set_value("base_received_amount", frm.doc.base_paid_amount); - } else if (company_currency == frm.doc.paid_to_account_currency) { - frm.set_value("received_amount", frm.doc.base_paid_amount); - frm.set_value("base_received_amount", frm.doc.base_paid_amount); - } - - // set_unallocated_amount is called by below method, - // no need trigger separately - frm.events.set_total_allocated_amount(frm); - } - - // Make read only if Accounts Settings doesn't allow stale rates - frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1); - }, - - target_exchange_rate: function (frm) { frm.set_paid_amount_based_on_received_amount = true; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if (frm.doc.received_amount) { - frm.set_value( - "base_received_amount", - flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate) - ); + if (frm.doc.base_received_amount && frm.doc.source_exchange_rate) { + frm.set_value("base_paid_amount", frm.doc.base_received_amount); - if ( - !frm.doc.source_exchange_rate && - frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency - ) { - frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate); - frm.set_value("base_paid_amount", frm.doc.base_received_amount); - } else if (company_currency == frm.doc.paid_from_account_currency) { - frm.set_value("paid_amount", frm.doc.base_received_amount); - frm.set_value("base_paid_amount", frm.doc.base_received_amount); + // target exchange rate should always be same as source if both account currencies is same + if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { + frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); + } else { + frm.set_value( + "paid_amount", + flt(frm.doc.base_paid_amount) / flt(frm.doc.source_exchange_rate) + ); } // set_unallocated_amount is called by below method, @@ -832,6 +788,32 @@ frappe.ui.form.on("Payment Entry", { } frm.set_paid_amount_based_on_received_amount = false; + // Make read only if Accounts Settings doesn't allow stale rates + frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1); + }, + + target_exchange_rate: function (frm) { + let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + + if (frm.doc.base_paid_amount && frm.doc.target_exchange_rate) { + frm.set_value("base_received_amount", frm.doc.base_paid_amount); + if ( + !frm.doc.source_exchange_rate && + frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency + ) { + frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate); + } else { + frm.set_value( + "received_amount", + flt(frm.doc.base_received_amount) / flt(frm.doc.target_exchange_rate) + ); + } + + // set_unallocated_amount is called by below method, + // no need trigger separately + frm.events.set_total_allocated_amount(frm); + } + // Make read only if Accounts Settings doesn't allow stale rates frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1); }, diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 93a591ed44f..4f5eae52a88 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -350,7 +350,7 @@ "reqd": 1 }, { - "depends_on": "doc.received_amount", + "depends_on": "eval:doc.received_amount;", "fieldname": "base_received_amount", "fieldtype": "Currency", "label": "Received Amount (Company Currency)", @@ -800,7 +800,7 @@ "table_fieldname": "payment_entries" } ], - "modified": "2025-05-15 18:01:04.013025", + "modified": "2026-05-15 13:31:01.166010", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", From f5d83599cceeac5849f0ee9b1621a7f622b74c6b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 Dec 2025 15:45:05 +0530 Subject: [PATCH 06/15] refactor: remove custom sql function in AR SQL procedure approach (cherry picked from commit d1e09922532ad255bced979a475d5a88a1e57dfc) # Conflicts: # erpnext/accounts/report/accounts_receivable/accounts_receivable.py --- .../accounts_settings/accounts_settings.py | 1 - .../accounts_receivable.py | 22 +++---------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 793f44bd5c8..0f096155949 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -175,6 +175,5 @@ class AccountsSettings(Document): def drop_ar_sql_procedures(self): from erpnext.accounts.report.accounts_receivable.accounts_receivable import InitSQLProceduresForAR - frappe.db.sql(f"drop function if exists {InitSQLProceduresForAR.genkey_function_name}") frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.init_procedure_name}") frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.allocate_procedure_name}") diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 96992895f93..da05fce3d3e 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1440,27 +1440,14 @@ class InitSQLProceduresForAR: amount_in_account_currency {_currency_type}) engine=memory; """ - # Function - genkey_function_name = "ar_genkey" - genkey_function_sql = f""" - create function `{genkey_function_name}`(rec row type of `{_row_def_table_name}`, allocate bool) returns char(40) - begin - if allocate then - return sha1(concat_ws(',', rec.account, rec.against_voucher_type, rec.against_voucher_no, rec.party)); - else - return sha1(concat_ws(',', rec.account, rec.voucher_type, rec.voucher_no, rec.party)); - end if; - end - """ - # Procedures init_procedure_name = "ar_init_tmp_table" init_procedure_sql = f""" create procedure ar_init_tmp_table(in ple row type of `{_row_def_table_name}`) begin - if not exists (select name from `{_voucher_balance_name}` where name = `{genkey_function_name}`(ple, false)) + if not exists (select name from `{_voucher_balance_name}` where name = sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party))) then - insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, false), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, ple.project, 0, 0, 0, 0, 0, 0); + insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0); end if; end; """ @@ -1502,16 +1489,13 @@ class InitSQLProceduresForAR: end if; - insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, true), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', '', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0); + insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.voucher_type, ple.voucher_no, ple.party)), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0); end; """ def __init__(self): existing_procedures = frappe.db.get_routines() - if self.genkey_function_name not in existing_procedures: - frappe.db.sql(self.genkey_function_sql) - if self.init_procedure_name not in existing_procedures: frappe.db.sql(self.init_procedure_sql) From 9309aec2093bdeab7548cc4757b9a7a0e9c7a1ae Mon Sep 17 00:00:00 2001 From: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com> Date: Mon, 18 May 2026 11:40:40 +0530 Subject: [PATCH 07/15] fix: toast message for item price insert (#55009) (cherry picked from commit ae9c632e3941149ca1aaa7132471359ee2403a56) # Conflicts: # erpnext/stock/get_item_details.py --- erpnext/stock/get_item_details.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index e055de42adf..66479d5b676 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1044,8 +1044,13 @@ def insert_item_price(args): ) item_price.insert() frappe.msgprint( +<<<<<<< HEAD _("Item Price Added for {0} in Price List {1}").format( get_link_to_form("Item", args.item_code), args.price_list +======= + _("Item Price added for {0} in Price List - {1}").format( + get_link_to_form("Item", ctx.item_code), ctx.price_list +>>>>>>> ae9c632e39 (fix: toast message for item price insert (#55009)) ), alert=True, ) @@ -1070,7 +1075,13 @@ def insert_item_price(args): ) item_price.insert() frappe.msgprint( +<<<<<<< HEAD _("Item Price added for {0} in Price List {1}").format(args.item_code, args.price_list), +======= + _("Item Price added for {0} in Price List - {1}").format( + get_link_to_form("Item", ctx.item_code), ctx.price_list + ), +>>>>>>> ae9c632e39 (fix: toast message for item price insert (#55009)) alert=True, ) From 8512eb449309181daea3ae701560010e08571393 Mon Sep 17 00:00:00 2001 From: Nishka Gosalia Date: Mon, 18 May 2026 11:44:57 +0530 Subject: [PATCH 08/15] fix: merge conflicts --- erpnext/stock/get_item_details.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 66479d5b676..8c205a10874 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1044,13 +1044,8 @@ def insert_item_price(args): ) item_price.insert() frappe.msgprint( -<<<<<<< HEAD - _("Item Price Added for {0} in Price List {1}").format( - get_link_to_form("Item", args.item_code), args.price_list -======= _("Item Price added for {0} in Price List - {1}").format( - get_link_to_form("Item", ctx.item_code), ctx.price_list ->>>>>>> ae9c632e39 (fix: toast message for item price insert (#55009)) + get_link_to_form("Item", args.item_code), args.price_list ), alert=True, ) @@ -1075,13 +1070,9 @@ def insert_item_price(args): ) item_price.insert() frappe.msgprint( -<<<<<<< HEAD - _("Item Price added for {0} in Price List {1}").format(args.item_code, args.price_list), -======= _("Item Price added for {0} in Price List - {1}").format( - get_link_to_form("Item", ctx.item_code), ctx.price_list + get_link_to_form("Item", args.item_code), args.price_list ), ->>>>>>> ae9c632e39 (fix: toast message for item price insert (#55009)) alert=True, ) From c705a93776f765722de21856d0dce2687b041374 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 18 May 2026 10:26:29 +0530 Subject: [PATCH 09/15] fix: remove sql procedure method from AR report (cherry picked from commit 63a7142b9b9ffaa3c469ff324f0cc0a53a92fcd4) # Conflicts: # erpnext/accounts/doctype/accounts_settings/accounts_settings.json # erpnext/accounts/doctype/accounts_settings/accounts_settings.py # erpnext/accounts/report/accounts_receivable/accounts_receivable.py # erpnext/patches.txt --- .../accounts_settings/accounts_settings.js | 10 - .../accounts_settings/accounts_settings.json | 14 +- .../accounts_settings/accounts_settings.py | 9 +- .../accounts_receivable.py | 194 ------------------ erpnext/patches.txt | 1 + ...clear_procedures_from_receivable_report.py | 12 ++ 6 files changed, 17 insertions(+), 223 deletions(-) create mode 100644 erpnext/patches/v16_0/clear_procedures_from_receivable_report.py diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index 931e05a716b..1e4d0bbca7e 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -30,16 +30,6 @@ frappe.ui.form.on("Accounts Settings", { add_taxes_from_item_tax_template(frm) { toggle_tax_settings(frm, "add_taxes_from_item_tax_template"); }, - - drop_ar_procedures: function (frm) { - frm.call({ - doc: frm.doc, - method: "drop_ar_sql_procedures", - callback: function (r) { - frappe.show_alert(__("Procedures dropped"), 5); - }, - }); - }, }); function toggle_tax_settings(frm, field_name) { diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 25e62e20f3e..18a0a86fd52 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -95,7 +95,6 @@ "receivable_payable_fetch_method", "default_ageing_range", "column_break_ntmi", - "drop_ar_procedures", "legacy_section", "ignore_is_opening_check_for_reporting", "payment_request_settings", @@ -561,7 +560,7 @@ "fieldname": "receivable_payable_fetch_method", "fieldtype": "Select", "label": "Data Fetch Method", - "options": "Buffered Cursor\nUnBuffered Cursor\nRaw SQL" + "options": "Buffered Cursor\nUnBuffered Cursor" }, { "fieldname": "accounts_receivable_payable_tuning_section", @@ -623,13 +622,6 @@ "fieldname": "column_break_ntmi", "fieldtype": "Column Break" }, - { - "depends_on": "eval:doc.receivable_payable_fetch_method == \"Raw SQL\"", - "description": "Drops existing SQL Procedures and Function setup by Accounts Receivable report", - "fieldname": "drop_ar_procedures", - "fieldtype": "Button", - "label": "Drop Procedures" - }, { "default": "0", "fieldname": "fetch_valuation_rate_for_internal_transaction", @@ -671,7 +663,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-03-06 14:49:11.467716", + "modified": "2026-05-18 12:16:33.679345", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -701,4 +693,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 0f096155949..5cd4955cdd7 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -60,7 +60,7 @@ class AccountsSettings(Document): merge_similar_account_heads: DF.Check over_billing_allowance: DF.Currency post_change_gl_entries: DF.Check - receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor", "Raw SQL"] + receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor"] receivable_payable_remarks_length: DF.Int reconciliation_queue_size: DF.Int role_allowed_to_over_bill: DF.Link | None @@ -170,10 +170,3 @@ class AccountsSettings(Document): ), title=_("Auto Tax Settings Error"), ) - - @frappe.whitelist() - def drop_ar_sql_procedures(self): - from erpnext.accounts.report.accounts_receivable.accounts_receivable import InitSQLProceduresForAR - - frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.init_procedure_name}") - frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.allocate_procedure_name}") diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index da05fce3d3e..d28f886a4fd 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -129,8 +129,6 @@ class ReceivablePayableReport: self.fetch_ple_in_buffered_cursor() elif self.ple_fetch_method == "UnBuffered Cursor": self.fetch_ple_in_unbuffered_cursor() - elif self.ple_fetch_method == "Raw SQL": - self.fetch_ple_in_sql_procedures() # Build delivery note map against all sales invoices self.build_delivery_note_map() @@ -321,81 +319,6 @@ class ReceivablePayableReport: row.paid -= amount row.paid_in_account_currency -= amount_in_account_currency - def fetch_ple_in_sql_procedures(self): - self.proc = InitSQLProceduresForAR() - - build_balance = f""" - begin not atomic - declare done boolean default false; - declare rec1 row type of `{self.proc._row_def_table_name}`; - declare ple cursor for {self.ple_query.get_sql()}; - declare continue handler for not found set done = true; - - open ple; - fetch ple into rec1; - while not done do - call {self.proc.init_procedure_name}(rec1); - fetch ple into rec1; - end while; - close ple; - - set done = false; - open ple; - fetch ple into rec1; - while not done do - call {self.proc.allocate_procedure_name}(rec1); - fetch ple into rec1; - end while; - close ple; - end; - """ - frappe.db.sql(build_balance) - - balances = frappe.db.sql( - f"""select - name, - voucher_type, - voucher_no, - party, - party_account `account`, - posting_date, - account_currency, - cost_center, - project, - sum(invoiced) `invoiced`, - sum(paid) `paid`, - sum(credit_note) `credit_note`, - sum(invoiced) - sum(paid) - sum(credit_note) `outstanding`, - sum(invoiced_in_account_currency) `invoiced_in_account_currency`, - sum(paid_in_account_currency) `paid_in_account_currency`, - sum(credit_note_in_account_currency) `credit_note_in_account_currency`, - sum(invoiced_in_account_currency) - sum(paid_in_account_currency) - sum(credit_note_in_account_currency) `outstanding_in_account_currency` - from `{self.proc._voucher_balance_name}` group by name order by posting_date;""", - as_dict=True, - ) - for x in balances: - if self.filters.get("ignore_accounts"): - key = (x.voucher_type, x.voucher_no, x.party) - else: - key = (x.account, x.voucher_type, x.voucher_no, x.party) - - _d = self.build_voucher_dict(x) - for field in [ - "invoiced", - "paid", - "credit_note", - "outstanding", - "invoiced_in_account_currency", - "paid_in_account_currency", - "credit_note_in_account_currency", - "outstanding_in_account_currency", - "cost_center", - "project", - ]: - _d[field] = x.get(field) - - self.voucher_balance[key] = _d - def update_sub_total_row(self, row, party): total_row = self.total_row_map.get(party) @@ -1390,120 +1313,3 @@ def get_party_group_with_children(party, party_groups): frappe.throw(_("{0}: {1} does not exist").format(group_dtype, d)) return list(set(all_party_groups)) - - -class InitSQLProceduresForAR: - """ - Initialize SQL Procedures, Functions and Temporary tables to build Receivable / Payable report - """ - - _varchar_type = get_definition("Data") - _currency_type = get_definition("Currency") - # Temporary Tables - _voucher_balance_name = "_ar_voucher_balance" - _voucher_balance_definition = f""" - create temporary table `{_voucher_balance_name}`( - name {_varchar_type}, - voucher_type {_varchar_type}, - voucher_no {_varchar_type}, - party {_varchar_type}, - party_account {_varchar_type}, - posting_date date, - account_currency {_varchar_type}, - cost_center {_varchar_type}, - project {_varchar_type}, - invoiced {_currency_type}, - paid {_currency_type}, - credit_note {_currency_type}, - invoiced_in_account_currency {_currency_type}, - paid_in_account_currency {_currency_type}, - credit_note_in_account_currency {_currency_type}) engine=memory; - """ - - _row_def_table_name = "_ar_ple_row" - _row_def_table_definition = f""" - create temporary table `{_row_def_table_name}`( - name {_varchar_type}, - account {_varchar_type}, - voucher_type {_varchar_type}, - voucher_no {_varchar_type}, - against_voucher_type {_varchar_type}, - against_voucher_no {_varchar_type}, - party_type {_varchar_type}, - cost_center {_varchar_type}, - project {_varchar_type}, - party {_varchar_type}, - posting_date date, - due_date date, - account_currency {_varchar_type}, - amount {_currency_type}, - amount_in_account_currency {_currency_type}) engine=memory; - """ - - # Procedures - init_procedure_name = "ar_init_tmp_table" - init_procedure_sql = f""" - create procedure ar_init_tmp_table(in ple row type of `{_row_def_table_name}`) - begin - if not exists (select name from `{_voucher_balance_name}` where name = sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party))) - then - insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0); - end if; - end; - """ - - allocate_procedure_name = "ar_allocate_to_tmp_table" - allocate_procedure_sql = f""" - create procedure ar_allocate_to_tmp_table(in ple row type of `{_row_def_table_name}`) - begin - declare invoiced {_currency_type} default 0; - declare invoiced_in_account_currency {_currency_type} default 0; - declare paid {_currency_type} default 0; - declare paid_in_account_currency {_currency_type} default 0; - declare credit_note {_currency_type} default 0; - declare credit_note_in_account_currency {_currency_type} default 0; - - - if ple.amount > 0 then - if (ple.voucher_type in ("Journal Entry", "Payment Entry") and (ple.voucher_no != ple.against_voucher_no)) then - set paid = -1 * ple.amount; - set paid_in_account_currency = -1 * ple.amount_in_account_currency; - else - set invoiced = ple.amount; - set invoiced_in_account_currency = ple.amount_in_account_currency; - end if; - else - - if ple.voucher_type in ("Sales Invoice", "Purchase Invoice") then - if (ple.voucher_no = ple.against_voucher_no) then - set paid = -1 * ple.amount; - set paid_in_account_currency = -1 * ple.amount_in_account_currency; - else - set credit_note = -1 * ple.amount; - set credit_note_in_account_currency = -1 * ple.amount_in_account_currency; - end if; - else - set paid = -1 * ple.amount; - set paid_in_account_currency = -1 * ple.amount_in_account_currency; - end if; - - end if; - - insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.voucher_type, ple.voucher_no, ple.party)), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0); - end; - """ - - def __init__(self): - existing_procedures = frappe.db.get_routines() - - if self.init_procedure_name not in existing_procedures: - frappe.db.sql(self.init_procedure_sql) - - if self.allocate_procedure_name not in existing_procedures: - frappe.db.sql(self.allocate_procedure_sql) - - frappe.db.sql(f"drop table if exists `{self._voucher_balance_name}`") - frappe.db.sql(self._voucher_balance_definition) - - frappe.db.sql(f"drop table if exists `{self._row_def_table_name}`") - frappe.db.sql(self._row_def_table_definition) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2dcee807fec..acb500e940f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -433,3 +433,4 @@ erpnext.patches.v15_0.replace_http_with_https_in_sales_partner erpnext.patches.v16_0.add_portal_redirects erpnext.patches.v16_0.update_order_qty_and_requested_qty_based_on_mr_and_po erpnext.patches.v16_0.depends_on_inv_dimensions +erpnext.patches.v16_0.clear_procedures_from_receivable_report diff --git a/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py b/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py new file mode 100644 index 00000000000..5eaf0f14fc8 --- /dev/null +++ b/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py @@ -0,0 +1,12 @@ +import frappe + + +def execute(): + if frappe.db.get_single_value("Accounts Settings", "receivable_payable_fetch_method") == "Raw SQL": + frappe.db.set_single_value( + "Accounts Settings", "receivable_payable_fetch_method", "UnBuffered Cursor" + ) + + frappe.db.sql("drop function if exists ar_genkey") + frappe.db.sql("drop procedure if exists ar_init_tmp_table") + frappe.db.sql("drop procedure if exists ar_allocate_to_tmp_table") From 5e1880f09e28d9c0ff3c7ddf45a900f4b448c2c9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:44:37 +0530 Subject: [PATCH 10/15] fix(stock): update buying amount calculation in gross profit report (backport #55020) (#55023) Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> fix(stock): update buying amount calculation in gross profit report (#55020) --- .../report/gross_profit/gross_profit.py | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index a53c2134e3f..21f999197c2 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -786,19 +786,11 @@ class GrossProfitGenerator: return self.calculate_buying_amount_from_sle( row, my_sle, parenttype, parent, row.item_row, item_code ) - elif self.delivery_notes.get((row.parent, row.item_code), None): - # check if Invoice has delivery notes - dn = self.delivery_notes.get((row.parent, row.item_code)) - parenttype, parent, item_row, dn_warehouse = ( - "Delivery Note", - dn["delivery_note"], - dn["item_row"], - dn["warehouse"], - ) - my_sle = self.get_stock_ledger_entries(item_code, dn_warehouse) - return self.calculate_buying_amount_from_sle( - row, my_sle, parenttype, parent, item_row, item_code - ) + elif row.item_row and self.delivery_notes.get(row.item_row): + dn = self.delivery_notes[row.item_row] + if flt(dn.total_qty): + return flt(row.qty) * flt(dn.total_incoming_value) / flt(dn.total_qty) + return flt(row.qty) * self.get_average_buying_rate(row, item_code) elif row.sales_order and row.so_detail: incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code) if incoming_amount: @@ -1049,25 +1041,29 @@ class GrossProfitGenerator: def get_delivery_notes(self): self.delivery_notes = frappe._dict({}) if self.si_list: + from frappe.query_builder.functions import Sum + invoices = [x.parent for x in self.si_list] dni = qb.DocType("Delivery Note Item") delivery_notes = ( qb.from_(dni) .select( - dni.against_sales_invoice.as_("sales_invoice"), - dni.item_code, - dni.warehouse, - dni.parent.as_("delivery_note"), - dni.name.as_("item_row"), + dni.si_detail, + Sum(dni.stock_qty * dni.incoming_rate).as_("total_incoming_value"), + Sum(dni.stock_qty).as_("total_qty"), ) - .where((dni.docstatus == 1) & (dni.against_sales_invoice.isin(invoices))) - .groupby(dni.against_sales_invoice, dni.item_code) - .orderby(dni.creation, order=Order.desc) + .where( + (dni.docstatus == 1) + & (dni.against_sales_invoice.isin(invoices)) + & (dni.si_detail.isnotnull()) + & (dni.si_detail != "") + ) + .groupby(dni.si_detail) .run(as_dict=True) ) for entry in delivery_notes: - self.delivery_notes[(entry.sales_invoice, entry.item_code)] = entry + self.delivery_notes[entry.si_detail] = entry def group_items_by_invoice(self): """ From 49b4830785545b6eca695f17b00fc3aeadfa900e Mon Sep 17 00:00:00 2001 From: ervishnucs Date: Wed, 6 May 2026 17:08:27 +0530 Subject: [PATCH 11/15] fix: normalize date comparison to avoid datatype mismatch (cherry picked from commit 01e382b1068db6940e142bec0dcebb70eb4e0f59) # Conflicts: # erpnext/accounts/party.py --- erpnext/accounts/party.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index c4622f0b06b..a27d3aab125 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -669,7 +669,7 @@ def validate_due_date_with_template(posting_date, due_date, bill_date, template_ if not default_due_date: return - if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date): + if getdate(default_due_date) != getdate(posting_date) and getdate(due_date) > getdate(default_due_date): if frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles(): party_type = "supplier" if doctype == "Purchase Invoice" else "customer" From dbacfd13b889aa87089bce15c7c887a1367105bf Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 19 May 2026 13:23:32 +0530 Subject: [PATCH 12/15] fix: stock balance showing incorrect value because of incorrect SLE (cherry picked from commit 94b95d6c2f9f6f8262ced0018d26d979aa17844c) --- erpnext/stock/stock_ledger.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 9cbce196e94..ffdb97ef9a6 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -2480,7 +2480,9 @@ def get_stock_value_difference( ) if voucher_detail_no: - query = query.where(table.voucher_detail_no != voucher_detail_no) + query = query.where( + (table.voucher_detail_no != voucher_detail_no) | (table.voucher_detail_no.isnull()) + ) elif voucher_no: query = query.where(table.voucher_no != voucher_no) From 67d67616caaaefd1f5cf670fd9ce3a8e155fe88f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 May 2026 16:12:25 +0530 Subject: [PATCH 13/15] fix(patch): drop dead procedures first before other changes (cherry picked from commit 61d24ba55f33e0d12f9956bc3311bc6c6740249d) --- .../v16_0/clear_procedures_from_receivable_report.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py b/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py index 5eaf0f14fc8..c362d4ef605 100644 --- a/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py +++ b/erpnext/patches/v16_0/clear_procedures_from_receivable_report.py @@ -2,11 +2,11 @@ import frappe def execute(): + frappe.db.sql("drop function if exists ar_genkey") + frappe.db.sql("drop procedure if exists ar_init_tmp_table") + frappe.db.sql("drop procedure if exists ar_allocate_to_tmp_table") + if frappe.db.get_single_value("Accounts Settings", "receivable_payable_fetch_method") == "Raw SQL": frappe.db.set_single_value( "Accounts Settings", "receivable_payable_fetch_method", "UnBuffered Cursor" ) - - frappe.db.sql("drop function if exists ar_genkey") - frappe.db.sql("drop procedure if exists ar_init_tmp_table") - frappe.db.sql("drop procedure if exists ar_allocate_to_tmp_table") From 5ad80b8fb9fb734c4d508eee176733afe4ef68ac Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 16:51:59 +0530 Subject: [PATCH 14/15] fix: validate company region in uae vat 201 (backport #54899) (#55054) Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com> fix: validate company region in uae vat 201 (#54899) --- .../regional/report/uae_vat_201/test_uae_vat_201.py | 8 ++++++++ erpnext/regional/report/uae_vat_201/uae_vat_201.js | 7 +++++++ erpnext/regional/report/uae_vat_201/uae_vat_201.py | 12 ++++++++++++ 3 files changed, 27 insertions(+) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 8c2b559fe52..e6f94689480 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -6,6 +6,7 @@ import erpnext from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( + execute, get_exempt_total, get_standard_rated_expenses_tax, get_standard_rated_expenses_total, @@ -39,6 +40,13 @@ class TestUaeVat201(TestCase): make_item("_Test UAE VAT Zero Rated Item", properties={"is_zero_rated": 1, "is_exempt": 0}) make_item("_Test UAE VAT Exempt Item", properties={"is_zero_rated": 0, "is_exempt": 1}) + def test_validate_company_region(self): + self.assertRaises( + frappe.exceptions.ValidationError, + execute, + {"company": "_Test Company"}, + ) + def test_uae_vat_201_report(self): make_sales_invoices() create_purchase_invoices() diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js index 49060fdf66a..e62d3395f20 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.js +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js @@ -10,6 +10,13 @@ frappe.query_reports["UAE VAT 201"] = { options: "Company", reqd: 1, default: frappe.defaults.get_user_default("Company"), + get_query: function () { + return { + filters: { + country: "United Arab Emirates", + }, + }; + }, }, { fieldname: "from_date", diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 7cf86adbe01..05f213d18dd 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -5,13 +5,25 @@ import frappe from frappe import _ +from erpnext import get_region + def execute(filters=None): + validate_company_region(filters) columns = get_columns() data, emirates, amounts_by_emirate = get_data(filters) return columns, data +def validate_company_region(filters): + if filters.get("company") and get_region(filters.get("company")) != "United Arab Emirates": + frappe.throw( + _( + "The company {0} is not in United Arab Emirates. UAE VAT 201 report is only available for companies in United Arab Emirates." + ).format(frappe.bold(filters.get("company"))) + ) + + def get_columns(): """Creates a list of dictionaries that are used to generate column headers of the data table.""" return [ From 0b41df5ac8c31dd4cc46f4cd56d2a97af34b5357 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2026 00:49:39 +0530 Subject: [PATCH 15/15] fix: item leaderboard uses Sales/Purchase Invoice instead of Orders (#55038) Fixes https://github.com/frappe/erpnext/issues/46657 --- erpnext/startup/leaderboard.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index 06f61fd961e..af168332d4f 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -88,7 +88,7 @@ def get_all_customers(date_range, company, field, limit=None): @frappe.whitelist() -def get_all_items(date_range, company, field, limit=None): +def get_all_items(date_range: str, company: str, field: str, limit: int | None = None): if field in ("available_stock_qty", "available_stock_value"): select_field = "sum(actual_qty)" if field == "available_stock_qty" else "sum(stock_value)" results = frappe.db.get_all( @@ -103,21 +103,21 @@ def get_all_items(date_range, company, field, limit=None): else: if field == "total_sales_amount": select_field = "base_net_amount" - select_doctype = "Sales Order" + select_doctype = "Sales Invoice" elif field == "total_purchase_amount": select_field = "base_net_amount" - select_doctype = "Purchase Order" + select_doctype = "Purchase Invoice" elif field == "total_qty_sold": select_field = "stock_qty" - select_doctype = "Sales Order" + select_doctype = "Sales Invoice" elif field == "total_qty_purchased": select_field = "stock_qty" - select_doctype = "Purchase Order" + select_doctype = "Purchase Invoice" filters = [["docstatus", "=", "1"], ["company", "=", company]] from_date, to_date = parse_date_range(date_range) if from_date and to_date: - filters.append(["transaction_date", "between", [from_date, to_date]]) + filters.append(["posting_date", "between", [from_date, to_date]]) child_doctype = f"{select_doctype} Item" return frappe.get_list(