fix(accounts): make General Ledger remarks alias Postgres-valid

When Accounts Settings -> general_ledger_remarks_length is set, the GL report
adds `substr(remarks, 1, n) as 'remarks'` to its raw SQL. Postgres treats a
single-quoted column alias as a string literal and raises a syntax error, so
the General Ledger report is broken on Postgres whenever that setting is on.

Use a bare alias (`as remarks`). substr() itself is portable.

Adds a test that sets general_ledger_remarks_length and runs the report,
asserting it executes (and returns rows) on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mihir Kandoi
2026-06-21 09:49:03 +05:30
parent 59dd3fe84e
commit acbf453def
2 changed files with 38 additions and 1 deletions

View File

@@ -164,7 +164,8 @@ def get_gl_entries(filters, accounting_dimensions):
if filters.get("show_remarks"):
if remarks_length := frappe.get_single_value("Accounts Settings", "general_ledger_remarks_length"):
select_fields += f",substr(remarks, 1, {remarks_length}) as 'remarks'"
# bare alias, not 'remarks' — Postgres treats a single-quoted alias as a string literal
select_fields += f",substr(remarks, 1, {remarks_length}) as remarks"
else:
select_fields += """,remarks"""

View File

@@ -15,6 +15,42 @@ class TestGeneralLedger(ERPNextTestSuite):
def setUp(self):
self.company = "_Test Company"
def test_gl_report_runs_with_remarks_length(self):
# general_ledger_remarks_length adds `substr(remarks, 1, n) as remarks` to the raw SQL; the
# alias must be unquoted to be valid on Postgres (a single-quoted alias is a string literal there).
from frappe.utils import today
frappe.db.set_single_value("Accounts Settings", "general_ledger_remarks_length", 50)
self.addCleanup(frappe.db.set_single_value, "Accounts Settings", "general_ledger_remarks_length", 0)
si = create_sales_invoice(company=self.company)
self.addCleanup(self._cancel_and_delete, "Sales Invoice", si.name)
columns, data = execute(
frappe._dict(
{
"company": self.company,
"from_date": today(),
"to_date": today(),
"group_by": "Group by Voucher (Consolidated)",
# required to reach the `substr(remarks, 1, n) as remarks` branch under test
"show_remarks": True,
}
)
)
self.assertTrue(columns)
self.assertTrue(data)
self.assertTrue(any("remarks" in row for row in data))
@staticmethod
def _cancel_and_delete(doctype, name):
if not frappe.db.exists(doctype, name):
return
doc = frappe.get_doc(doctype, name)
if doc.docstatus == 1:
doc.cancel()
frappe.delete_doc(doctype, name, force=1)
def clear_old_entries(self):
doctype_list = [
"GL Entry",