mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-20 11:22:28 +00:00
feat(profit-and-loss): implement execute_synced_report with full parity to normal report
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 6a93baacf0)
This commit is contained in:
@@ -4,13 +4,20 @@
|
||||
"columns": [],
|
||||
"creation": "2014-07-18 11:43:33.173207",
|
||||
"default_print_format": "P&L Statement Standard",
|
||||
"disable_prepared_report_automation": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"doctype_to_sync": [
|
||||
{
|
||||
"doc_type": "GL Entry"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"generate_csv": 0,
|
||||
"idx": 2,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2026-05-22 14:36:04.544347",
|
||||
"modified": "2026-06-22 13:06:12.602924",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Profit and Loss Statement",
|
||||
@@ -30,5 +37,6 @@
|
||||
"role": "Auditor"
|
||||
}
|
||||
],
|
||||
"synced_report": 1,
|
||||
"timeout": 0
|
||||
}
|
||||
|
||||
@@ -11,12 +11,20 @@ from erpnext.accounts.doctype.financial_report_template.financial_report_engine
|
||||
get_xlsx_styles, #! DO NOT REMOVE - hook for styling
|
||||
)
|
||||
from erpnext.accounts.report.financial_statements import (
|
||||
accumulate_values_into_parents,
|
||||
add_total_row,
|
||||
calculate_values,
|
||||
compute_growth_view_data,
|
||||
compute_margin_view_data,
|
||||
filter_accounts,
|
||||
filter_out_zero_value_rows,
|
||||
get_accounts,
|
||||
get_appropriate_currency,
|
||||
get_columns,
|
||||
get_data,
|
||||
get_filtered_list_for_consolidated_report,
|
||||
get_period_list,
|
||||
prepare_data,
|
||||
)
|
||||
|
||||
|
||||
@@ -197,3 +205,125 @@ def get_chart_data(filters, chart_columns, income, expense, net_profit_loss, cur
|
||||
chart["currency"] = currency
|
||||
|
||||
return chart
|
||||
|
||||
|
||||
def execute_synced_report(filters):
|
||||
from frappe.database.duckdb.database import get_latest_sync
|
||||
|
||||
if not (conn := get_latest_sync("GL Entry")):
|
||||
frappe.throw(
|
||||
_("Profit and Loss Statement requires {0} to be synced to DuckDB").format(frappe.bold("GL Entry"))
|
||||
)
|
||||
|
||||
period_list = get_period_list(
|
||||
filters.from_fiscal_year,
|
||||
filters.to_fiscal_year,
|
||||
filters.period_start_date,
|
||||
filters.period_end_date,
|
||||
filters.filter_based_on,
|
||||
filters.periodicity,
|
||||
company=filters.company,
|
||||
)
|
||||
|
||||
income = _get_data_duckdb(conn, filters, "Income", "Credit", period_list)
|
||||
expense = _get_data_duckdb(conn, filters, "Expense", "Debit", period_list)
|
||||
|
||||
net_profit_loss = get_net_profit_loss(
|
||||
income, expense, period_list, filters.company, filters.presentation_currency
|
||||
)
|
||||
|
||||
data = []
|
||||
data.extend(income or [])
|
||||
data.extend(expense or [])
|
||||
if net_profit_loss:
|
||||
data.append(net_profit_loss)
|
||||
|
||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
||||
|
||||
currency = filters.presentation_currency or frappe.get_cached_value(
|
||||
"Company", filters.company, "default_currency"
|
||||
)
|
||||
chart = get_chart_data(filters, period_list, income, expense, net_profit_loss, currency)
|
||||
|
||||
report_summary, primitive_summary = get_report_summary(
|
||||
period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters
|
||||
)
|
||||
|
||||
if filters.get("selected_view") == "Growth":
|
||||
compute_growth_view_data(data, period_list)
|
||||
|
||||
if filters.get("selected_view") == "Margin":
|
||||
compute_margin_view_data(data, period_list, filters.accumulated_values)
|
||||
|
||||
return columns, data, None, chart, report_summary, primitive_summary
|
||||
|
||||
|
||||
def _get_data_duckdb(conn, filters, root_type, balance_must_be, period_list):
|
||||
accounts = get_accounts(filters.company, root_type)
|
||||
if not accounts:
|
||||
return None
|
||||
|
||||
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
|
||||
company_currency = get_appropriate_currency(filters.company, filters)
|
||||
|
||||
gl_entries_by_account = {}
|
||||
_load_gl_entries_duckdb(conn, filters, period_list, accounts, gl_entries_by_account)
|
||||
|
||||
calculate_values(
|
||||
accounts_by_name,
|
||||
gl_entries_by_account,
|
||||
period_list,
|
||||
filters.accumulated_values,
|
||||
False,
|
||||
)
|
||||
accumulate_values_into_parents(accounts, accounts_by_name, period_list)
|
||||
|
||||
out = prepare_data(
|
||||
accounts,
|
||||
balance_must_be,
|
||||
period_list,
|
||||
company_currency,
|
||||
accumulated_values=filters.accumulated_values,
|
||||
)
|
||||
out = filter_out_zero_value_rows(out, parent_children_map, filters.show_zero_values)
|
||||
|
||||
if out:
|
||||
add_total_row(out, root_type, balance_must_be, period_list, company_currency)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def _load_gl_entries_duckdb(conn, filters, period_list, accounts, gl_entries_by_account):
|
||||
from erpnext.accounts.report.trial_balance.trial_balance import (
|
||||
_extra_gl_conditions,
|
||||
_fetch_gl_rows_duckdb,
|
||||
)
|
||||
from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
|
||||
|
||||
company = filters.company
|
||||
leaf_accounts = [acc.name for acc in accounts if not acc.is_group]
|
||||
if not leaf_accounts:
|
||||
return
|
||||
|
||||
extra_cond, extra_params = _extra_gl_conditions(filters)
|
||||
account_placeholders = ", ".join(["?"] * len(leaf_accounts))
|
||||
base_conds = [
|
||||
"company = ?",
|
||||
"is_cancelled = 0",
|
||||
f"account IN ({account_placeholders})",
|
||||
"voucher_type != 'Period Closing Voucher'",
|
||||
]
|
||||
base_params = [company, *leaf_accounts]
|
||||
base_conds.extend(extra_cond)
|
||||
base_params.extend(extra_params)
|
||||
|
||||
for period in period_list:
|
||||
period_conds = [*base_conds, "posting_date >= ?", "posting_date <= ?"]
|
||||
period_params = [*base_params, period.from_date, period.to_date]
|
||||
|
||||
period_entries = _fetch_gl_rows_duckdb(conn, period_conds, period_params)
|
||||
if filters.get("presentation_currency"):
|
||||
convert_to_presentation_currency(period_entries, get_currency(filters))
|
||||
for entry in period_entries:
|
||||
entry.posting_date = period.to_date
|
||||
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
||||
|
||||
Reference in New Issue
Block a user