mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-24 12:36:37 +00:00
refactor(postgres): port Budget Variance report query to the query builder
The raw get_fiscal_years query had no ORDER BY (de-facto oldest-first); the get_all port adds explicit order_by="name asc" so the Fiscal Year doctype default (name DESC) does not reverse the report column order / cumulative values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder import CustomFunction
|
||||
from frappe.query_builder.custom import MonthName
|
||||
from frappe.utils import add_months, flt, formatdate
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
|
||||
@@ -113,7 +113,6 @@ def build_budget_map(budget_records, filters):
|
||||
|
||||
def get_actual_transactions(dimension_name, filters):
|
||||
budget_against = frappe.scrub(filters.get("budget_against"))
|
||||
monthname = CustomFunction("MONTHNAME", ["date"])
|
||||
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
budget = frappe.qb.DocType("Budget")
|
||||
@@ -126,7 +125,7 @@ def get_actual_transactions(dimension_name, filters):
|
||||
gle.debit,
|
||||
gle.credit,
|
||||
gle.fiscal_year,
|
||||
monthname(gle.posting_date).as_("month_name"),
|
||||
MonthName(gle.posting_date).as_("month_name"),
|
||||
budget[budget_against].as_("budget_against"),
|
||||
)
|
||||
.where(
|
||||
@@ -137,7 +136,10 @@ def get_actual_transactions(dimension_name, filters):
|
||||
& (gle.is_cancelled == 0)
|
||||
& (budget[budget_against] == dimension_name)
|
||||
)
|
||||
.groupby(gle.name)
|
||||
# budget[budget_against] is selected from the Budget table, which is not functionally
|
||||
# dependent on the grouped GL Entry PK, so postgres requires it in the GROUP BY. The WHERE
|
||||
# pins it to dimension_name (a constant), so grouping by it does not change the result.
|
||||
.groupby(gle.name, budget[budget_against])
|
||||
.orderby(gle.fiscal_year)
|
||||
)
|
||||
|
||||
@@ -157,15 +159,11 @@ def get_actual_transactions(dimension_name, filters):
|
||||
|
||||
|
||||
def get_budget_distributions(budget):
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
SELECT start_date, end_date, amount, percent
|
||||
FROM `tabBudget Distribution`
|
||||
WHERE parent = %s
|
||||
ORDER BY start_date ASC
|
||||
""",
|
||||
(budget.name,),
|
||||
as_dict=True,
|
||||
return frappe.get_all(
|
||||
"Budget Distribution",
|
||||
filters={"parent": budget.name},
|
||||
fields=["start_date", "end_date", "amount", "percent"],
|
||||
order_by="start_date asc",
|
||||
)
|
||||
|
||||
|
||||
@@ -351,20 +349,16 @@ def get_columns(filters):
|
||||
|
||||
|
||||
def get_fiscal_years(filters):
|
||||
fiscal_year = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name
|
||||
from
|
||||
`tabFiscal Year`
|
||||
where
|
||||
name between %(from_fiscal_year)s and %(to_fiscal_year)s
|
||||
""",
|
||||
{"from_fiscal_year": filters["from_fiscal_year"], "to_fiscal_year": filters["to_fiscal_year"]},
|
||||
return frappe.get_all(
|
||||
"Fiscal Year",
|
||||
filters={"name": ["between", [filters["from_fiscal_year"], filters["to_fiscal_year"]]]},
|
||||
fields=["name"],
|
||||
# the raw query had no ORDER BY (de-facto oldest-first); get_all would otherwise apply the
|
||||
# Fiscal Year doctype default (name DESC) and reverse column order / cumulative-mode values.
|
||||
order_by="name asc",
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
return fiscal_year
|
||||
|
||||
|
||||
def get_cost_center_with_children(cost_centers):
|
||||
"""Expand each cost center to include itself and all its descendants."""
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.report.budget_variance_report.budget_variance_report import execute
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestBudgetVarianceReport(ERPNextTestSuite):
|
||||
def test_report_executes(self):
|
||||
# Smoke-guards the raw-SQL -> query-builder port: the report query must compile and run on
|
||||
# both MariaDB and postgres.
|
||||
company = frappe.db.get_value("Company", {}, "name")
|
||||
fy = frappe.db.get_value("Fiscal Year", {}, "name", order_by="year_start_date desc")
|
||||
columns, *_rest = execute(
|
||||
frappe._dict(
|
||||
{
|
||||
"company": company,
|
||||
"from_fiscal_year": fy,
|
||||
"to_fiscal_year": fy,
|
||||
"period": "Yearly",
|
||||
"budget_against": "Cost Center",
|
||||
}
|
||||
)
|
||||
)
|
||||
self.assertTrue(columns)
|
||||
Reference in New Issue
Block a user