mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 07:28:39 +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": [],
|
"columns": [],
|
||||||
"creation": "2014-07-18 11:43:33.173207",
|
"creation": "2014-07-18 11:43:33.173207",
|
||||||
"default_print_format": "P&L Statement Standard",
|
"default_print_format": "P&L Statement Standard",
|
||||||
|
"disable_prepared_report_automation": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
|
"doctype_to_sync": [
|
||||||
|
{
|
||||||
|
"doc_type": "GL Entry"
|
||||||
|
}
|
||||||
|
],
|
||||||
"filters": [],
|
"filters": [],
|
||||||
|
"generate_csv": 0,
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2026-05-22 14:36:04.544347",
|
"modified": "2026-06-22 13:06:12.602924",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Profit and Loss Statement",
|
"name": "Profit and Loss Statement",
|
||||||
@@ -30,5 +37,6 @@
|
|||||||
"role": "Auditor"
|
"role": "Auditor"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"synced_report": 1,
|
||||||
"timeout": 0
|
"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
|
get_xlsx_styles, #! DO NOT REMOVE - hook for styling
|
||||||
)
|
)
|
||||||
from erpnext.accounts.report.financial_statements import (
|
from erpnext.accounts.report.financial_statements import (
|
||||||
|
accumulate_values_into_parents,
|
||||||
|
add_total_row,
|
||||||
|
calculate_values,
|
||||||
compute_growth_view_data,
|
compute_growth_view_data,
|
||||||
compute_margin_view_data,
|
compute_margin_view_data,
|
||||||
|
filter_accounts,
|
||||||
|
filter_out_zero_value_rows,
|
||||||
|
get_accounts,
|
||||||
|
get_appropriate_currency,
|
||||||
get_columns,
|
get_columns,
|
||||||
get_data,
|
get_data,
|
||||||
get_filtered_list_for_consolidated_report,
|
get_filtered_list_for_consolidated_report,
|
||||||
get_period_list,
|
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
|
chart["currency"] = currency
|
||||||
|
|
||||||
return chart
|
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