diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 8cbaef43180..e41d1b0f768 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -4,7 +4,7 @@
import frappe
from frappe import _, throw
-from frappe.utils import cint, cstr
+from frappe.utils import add_to_date, cint, cstr, pretty_date
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
import erpnext
@@ -400,6 +400,7 @@ def validate_account_number(name, account_number, company):
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
+ _ensure_idle_system()
account = frappe.db.get_value("Account", name, "company", as_dict=True)
if not account:
return
@@ -461,6 +462,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
@frappe.whitelist()
def merge_account(old, new):
+ _ensure_idle_system()
# Validate properties before merging
new_account = frappe.get_cached_doc("Account", new)
old_account = frappe.get_cached_doc("Account", old)
@@ -514,3 +516,27 @@ def sync_update_account_number_in_child(
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
update_account_number(d["name"], account_name, account_number, from_descendant=True)
+
+
+def _ensure_idle_system():
+ # Don't allow renaming if accounting entries are actively being updated, there are two main reasons:
+ # 1. Correctness: It's next to impossible to ensure that renamed account is not being used *right now*.
+ # 2. Performance: Renaming requires locking out many tables entirely and severely degrades performance.
+
+ if frappe.flags.in_test:
+ return
+
+ try:
+ # We also lock inserts to GL entry table with for_update here.
+ last_gl_update = frappe.db.get_value("GL Entry", {}, "modified", for_update=True, wait=False)
+ except frappe.QueryTimeoutError:
+ # wait=False fails immediately if there's an active transaction.
+ last_gl_update = add_to_date(None, seconds=-1)
+
+ if last_gl_update > add_to_date(None, minutes=-5):
+ frappe.throw(
+ _(
+ "Last GL Entry update was done {}. This operation is not allowed while system is actively being used. Please wait for 5 minutes before retrying."
+ ).format(pretty_date(last_gl_update)),
+ title=_("System In Use"),
+ )
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 25b67e2db31..d40efc52d84 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1519,7 +1519,7 @@ class PaymentEntry(AccountsController):
allocated_positive_outstanding = paid_amount + allocated_negative_outstanding
- elif self.party_type in ("Supplier", "Employee"):
+ elif self.party_type in ("Supplier", "Customer"):
if paid_amount > total_negative_outstanding:
if total_negative_outstanding == 0:
frappe.msgprint(
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index f5a9d17f088..7c13fe50f1a 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -1623,6 +1623,5 @@
"states": [],
"timeline_field": "customer",
"title_field": "title",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
+ "track_changes": 1
+}
diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
index 46b430c6594..51dc3674594 100644
--- a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
+++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
@@ -13,17 +13,15 @@
"fields": [
{
"fieldname": "voucher_type",
- "fieldtype": "Link",
+ "fieldtype": "Data",
"in_list_view": 1,
- "label": "Voucher Type",
- "options": "DocType"
+ "label": "Voucher Type"
},
{
"fieldname": "voucher_name",
- "fieldtype": "Dynamic Link",
+ "fieldtype": "Data",
"in_list_view": 1,
- "label": "Voucher Name",
- "options": "voucher_type"
+ "label": "Voucher Name"
},
{
"fieldname": "taxable_amount",
@@ -36,7 +34,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-01-13 13:40:41.479208",
+ "modified": "2025-02-05 16:39:14.863698",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Withheld Vouchers",
diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
index f6efc8a685c..dc6192e7544 100644
--- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
+++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
@@ -27,6 +27,7 @@ def get_report_filters(report_filters):
["Purchase Invoice", "docstatus", "=", 1],
["Purchase Invoice", "per_received", "<", 100],
["Purchase Invoice", "update_stock", "=", 0],
+ ["Purchase Invoice", "is_opening", "!=", "Yes"],
]
if report_filters.get("purchase_invoice"):
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index e540aa9993c..db42d23a839 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -263,6 +263,7 @@ def get_actual_details(name, filters):
and ba.account=gl.account
and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year between %s and %s
+ and gl.is_cancelled = 0
and b.{budget_against} = %s
and exists(
select
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index aa06dffb043..25d52998449 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -506,6 +506,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
keylist.append(gle.get("cost_center"))
+ keylist.append(gle.get("project"))
key = tuple(keylist)
if key not in consolidated_gle:
@@ -617,10 +618,11 @@ def get_columns(filters):
{"label": _("Against Account"), "fieldname": "against", "width": 120},
{"label": _("Party Type"), "fieldname": "party_type", "width": 100},
{"label": _("Party"), "fieldname": "party", "width": 100},
- {"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100},
]
if filters.get("include_dimensions"):
+ columns.append({"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100})
+
for dim in get_accounting_dimensions(as_list=False):
columns.append(
{"label": _(dim.label), "options": dim.label, "fieldname": dim.fieldname, "width": 100}
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index b37658b7784..2f4c154cb00 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -801,7 +801,7 @@ class StockController(AccountsController):
child_tab.item_code,
child_tab.qty,
)
- .where(parent_tab.docstatus < 2)
+ .where(parent_tab.docstatus == 1)
)
if self.doctype == "Purchase Invoice":
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 14948d60e46..45ba4304985 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -4,7 +4,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd."
app_description = """ERP made simple"""
app_icon = "fa fa-th"
app_color = "#e74c3c"
-app_email = "info@erpnext.com"
+app_email = "hello@frappe.io"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
@@ -479,7 +479,7 @@ email_brand_image = "assets/erpnext/images/erpnext-logo.jpg"
default_mail_footer = """
Sent via
-
+
ERPNext
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index ab842095412..cdec75b64ec 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -371,4 +371,4 @@ erpnext.patches.v14_0.update_stock_uom_in_work_order_item
erpnext.patches.v14_0.disable_add_row_in_gross_profit
execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_posting_date", "Payment")
erpnext.patches.v14_0.update_posting_datetime
-
+erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes
diff --git a/erpnext/regional/address_template/templates/united_states.html b/erpnext/regional/address_template/templates/united_states.html
index 77fce46b9d7..f00f99c1299 100644
--- a/erpnext/regional/address_template/templates/united_states.html
+++ b/erpnext/regional/address_template/templates/united_states.html
@@ -1,4 +1,4 @@
{{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}
{% endif -%}
-{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}
{% endif -%}
+{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}{% endif -%}
{% if country != "United States" %}{{ country }}{% endif -%}
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index 7db9faacca2..6cb72d53b59 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -275,12 +275,12 @@ def get_past_order_list(search_term, status, limit=20):
invoice_list = []
if search_term and status:
- invoices_by_customer = frappe.db.get_all(
+ invoices_by_customer = frappe.db.get_list(
"POS Invoice",
filters={"customer": ["like", f"%{search_term}%"], "status": status},
fields=fields,
)
- invoices_by_name = frappe.db.get_all(
+ invoices_by_name = frappe.db.get_list(
"POS Invoice",
filters={"name": ["like", f"%{search_term}%"], "status": status},
fields=fields,
@@ -288,7 +288,7 @@ def get_past_order_list(search_term, status, limit=20):
invoice_list = invoices_by_customer + invoices_by_name
elif status:
- invoice_list = frappe.db.get_all("POS Invoice", filters={"status": status}, fields=fields)
+ invoice_list = frappe.db.get_list("POS Invoice", filters={"status": status}, fields=fields)
return invoice_list
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index dcf086f2d05..a5a75ebcce8 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -15,7 +15,7 @@ from erpnext.setup.doctype.incoterm.incoterm import create_incoterms
from .default_success_action import get_default_success_action
default_mail_footer = """