mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 16:34:46 +00:00
feat: Add report summary in financial statements (#20876)
* feat: Add report summary API for Balance Sheet and PL report * fix: Add summary cards in Cash flow report * feat: Add report summary in consolidated financial statements * fix: Remove accumulated values filter from P&L and cashflow report * fix: set company default currency in profit and loss statement if no data Co-authored-by: Prssanna Desai <prssud@gmail.com>
This commit is contained in:
@@ -58,7 +58,10 @@ def execute(filters=None):
|
|||||||
|
|
||||||
chart = get_chart_data(filters, columns, asset, liability, equity)
|
chart = get_chart_data(filters, columns, asset, liability, equity)
|
||||||
|
|
||||||
return columns, data, message, chart
|
report_summary = get_report_summary(period_list, asset, liability, equity, provisional_profit_loss,
|
||||||
|
total_credit, currency, filters)
|
||||||
|
|
||||||
|
return columns, data, message, chart, report_summary
|
||||||
|
|
||||||
def get_provisional_profit_loss(asset, liability, equity, period_list, company, currency=None, consolidated=False):
|
def get_provisional_profit_loss(asset, liability, equity, period_list, company, currency=None, consolidated=False):
|
||||||
provisional_profit_loss = {}
|
provisional_profit_loss = {}
|
||||||
@@ -120,6 +123,56 @@ def check_opening_balance(asset, liability, equity):
|
|||||||
return _("Previous Financial Year is not closed"),opening_balance
|
return _("Previous Financial Year is not closed"),opening_balance
|
||||||
return None,None
|
return None,None
|
||||||
|
|
||||||
|
def get_report_summary(period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency,
|
||||||
|
filters, consolidated=False):
|
||||||
|
|
||||||
|
net_asset, net_liability, net_equity, net_provisional_profit_loss = 0.0, 0.0, 0.0, 0.0
|
||||||
|
|
||||||
|
if filters.get('accumulated_values'):
|
||||||
|
period_list = [period_list[-1]]
|
||||||
|
|
||||||
|
for period in period_list:
|
||||||
|
key = period if consolidated else period.key
|
||||||
|
if asset:
|
||||||
|
net_asset += asset[-2].get(key)
|
||||||
|
if liability:
|
||||||
|
net_liability += liability[-2].get(key)
|
||||||
|
if equity:
|
||||||
|
net_equity += equity[-2].get(key)
|
||||||
|
if provisional_profit_loss:
|
||||||
|
net_provisional_profit_loss += provisional_profit_loss.get(key)
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"value": net_asset,
|
||||||
|
"label": "Total Asset",
|
||||||
|
"indicator": "Green",
|
||||||
|
"datatype": "Currency",
|
||||||
|
"currency": currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": net_liability,
|
||||||
|
"label": "Total Liability",
|
||||||
|
"datatype": "Currency",
|
||||||
|
"indicator": "Red",
|
||||||
|
"currency": currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": net_equity,
|
||||||
|
"label": "Total Equity",
|
||||||
|
"datatype": "Currency",
|
||||||
|
"indicator": "Blue",
|
||||||
|
"currency": currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": net_provisional_profit_loss,
|
||||||
|
"label": "Provisional Profit / Loss (Credit)",
|
||||||
|
"indicator": "Green" if net_provisional_profit_loss > 0 else "Red",
|
||||||
|
"datatype": "Currency",
|
||||||
|
"currency": currency
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
def get_chart_data(filters, columns, asset, liability, equity):
|
def get_chart_data(filters, columns, asset, liability, equity):
|
||||||
labels = [d.get("label") for d in columns[2:]]
|
labels = [d.get("label") for d in columns[2:]]
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);
|
frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);
|
||||||
|
|
||||||
frappe.query_reports["Cash Flow"]["filters"].push(
|
frappe.query_reports["Cash Flow"]["filters"].push(
|
||||||
{
|
|
||||||
"fieldname": "accumulated_values",
|
|
||||||
"label": __("Accumulated Values"),
|
|
||||||
"fieldtype": "Check"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "include_default_book_entries",
|
"fieldname": "include_default_book_entries",
|
||||||
"label": __("Include Default Book Entries"),
|
"label": __("Include Default Book Entries"),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from frappe.utils import cint, cstr
|
|||||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||||
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
|
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
@@ -29,6 +30,7 @@ def execute(filters=None):
|
|||||||
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
|
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
summary_data = {}
|
||||||
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||||
|
|
||||||
for cash_flow_account in cash_flow_accounts:
|
for cash_flow_account in cash_flow_accounts:
|
||||||
@@ -64,14 +66,16 @@ def execute(filters=None):
|
|||||||
section_data.append(account_data)
|
section_data.append(account_data)
|
||||||
|
|
||||||
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
||||||
period_list, company_currency)
|
period_list, company_currency, summary_data)
|
||||||
|
|
||||||
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
|
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data)
|
||||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
||||||
|
|
||||||
chart = get_chart_data(columns, data)
|
chart = get_chart_data(columns, data)
|
||||||
|
|
||||||
return columns, data, None, chart
|
report_summary = get_report_summary(summary_data, company_currency)
|
||||||
|
|
||||||
|
return columns, data, None, chart, report_summary
|
||||||
|
|
||||||
def get_cash_flow_accounts():
|
def get_cash_flow_accounts():
|
||||||
operation_accounts = {
|
operation_accounts = {
|
||||||
@@ -157,7 +161,7 @@ def get_start_date(period, accumulated_values, company):
|
|||||||
|
|
||||||
return start_date
|
return start_date
|
||||||
|
|
||||||
def add_total_row_account(out, data, label, period_list, currency, consolidated = False):
|
def add_total_row_account(out, data, label, period_list, currency, summary_data, consolidated = False):
|
||||||
total_row = {
|
total_row = {
|
||||||
"account_name": "'" + _("{0}").format(label) + "'",
|
"account_name": "'" + _("{0}").format(label) + "'",
|
||||||
"account": "'" + _("{0}").format(label) + "'",
|
"account": "'" + _("{0}").format(label) + "'",
|
||||||
@@ -176,6 +180,24 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated
|
|||||||
out.append(total_row)
|
out.append(total_row)
|
||||||
out.append({})
|
out.append({})
|
||||||
|
|
||||||
|
summary_data[label] = total_row["total"]
|
||||||
|
|
||||||
|
def get_report_summary(summary_data, currency):
|
||||||
|
report_summary = []
|
||||||
|
|
||||||
|
for label, value in iteritems(summary_data):
|
||||||
|
report_summary.append(
|
||||||
|
{
|
||||||
|
"value": value,
|
||||||
|
"label": label,
|
||||||
|
"datatype": "Currency",
|
||||||
|
"currency": currency
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return report_summary
|
||||||
|
|
||||||
|
|
||||||
def get_chart_data(columns, data):
|
def get_chart_data(columns, data):
|
||||||
labels = [d.get("label") for d in columns[2:]]
|
labels = [d.get("label") for d in columns[2:]]
|
||||||
datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')]
|
datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')]
|
||||||
|
|||||||
@@ -61,5 +61,17 @@ frappe.query_reports["Consolidated Financial Statement"] = {
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"default": 1
|
"default": 1
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
|
if (!data.parent_account) {
|
||||||
|
value = $(`<span>${value}</span>`);
|
||||||
|
|
||||||
|
var $value = $(value).css("font-weight", "bold");
|
||||||
|
|
||||||
|
value = $value.wrap("<p></p>").parent().html();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ from frappe.utils import flt, cint
|
|||||||
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
|
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
|
||||||
from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts
|
from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts
|
||||||
from erpnext.accounts.report.balance_sheet.balance_sheet import (get_provisional_profit_loss,
|
from erpnext.accounts.report.balance_sheet.balance_sheet import (get_provisional_profit_loss,
|
||||||
check_opening_balance, get_chart_data)
|
check_opening_balance, get_chart_data, get_report_summary as get_bs_summary)
|
||||||
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (get_net_profit_loss,
|
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (get_net_profit_loss,
|
||||||
get_chart_data as get_pl_chart_data)
|
get_chart_data as get_pl_chart_data, get_report_summary as get_pl_summary)
|
||||||
from erpnext.accounts.report.cash_flow.cash_flow import (get_cash_flow_accounts, get_account_type_based_gl_data,
|
from erpnext.accounts.report.cash_flow.cash_flow import (get_cash_flow_accounts, get_account_type_based_gl_data,
|
||||||
add_total_row_account)
|
add_total_row_account, get_report_summary as get_cash_flow_summary)
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
columns, data, message, chart = [], [], [], []
|
columns, data, message, chart = [], [], [], []
|
||||||
@@ -25,17 +25,17 @@ def execute(filters=None):
|
|||||||
columns = get_columns(companies_column)
|
columns = get_columns(companies_column)
|
||||||
|
|
||||||
if filters.get('report') == "Balance Sheet":
|
if filters.get('report') == "Balance Sheet":
|
||||||
data, message, chart = get_balance_sheet_data(fiscal_year, companies, columns, filters)
|
data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
|
||||||
elif filters.get('report') == "Profit and Loss Statement":
|
elif filters.get('report') == "Profit and Loss Statement":
|
||||||
data, message, chart = get_profit_loss_data(fiscal_year, companies, columns, filters)
|
data, message, chart, report_summary = get_profit_loss_data(fiscal_year, companies, columns, filters)
|
||||||
else:
|
else:
|
||||||
if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
|
if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
|
||||||
from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
|
from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
|
||||||
return execute_custom(filters=filters)
|
return execute_custom(filters=filters)
|
||||||
|
|
||||||
data = get_cash_flow_data(fiscal_year, companies, filters)
|
data, report_summary = get_cash_flow_data(fiscal_year, companies, filters)
|
||||||
|
|
||||||
return columns, data, message, chart
|
return columns, data, message, chart, report_summary
|
||||||
|
|
||||||
def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
||||||
asset = get_data(companies, "Asset", "Debit", fiscal_year, filters=filters)
|
asset = get_data(companies, "Asset", "Debit", fiscal_year, filters=filters)
|
||||||
@@ -75,9 +75,12 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
|||||||
if total_credit:
|
if total_credit:
|
||||||
data.append(total_credit)
|
data.append(total_credit)
|
||||||
|
|
||||||
|
report_summary = get_bs_summary(companies, asset, liability, equity, provisional_profit_loss, total_credit,
|
||||||
|
company_currency, filters, True)
|
||||||
|
|
||||||
chart = get_chart_data(filters, columns, asset, liability, equity)
|
chart = get_chart_data(filters, columns, asset, liability, equity)
|
||||||
|
|
||||||
return data, message, chart
|
return data, message, chart, report_summary
|
||||||
|
|
||||||
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
||||||
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
||||||
@@ -90,7 +93,9 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
|||||||
|
|
||||||
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
|
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||||
|
|
||||||
return data, None, chart
|
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, True)
|
||||||
|
|
||||||
|
return data, None, chart, report_summary
|
||||||
|
|
||||||
def get_income_expense_data(companies, fiscal_year, filters):
|
def get_income_expense_data(companies, fiscal_year, filters):
|
||||||
company_currency = get_company_currency(filters)
|
company_currency = get_company_currency(filters)
|
||||||
@@ -108,6 +113,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
|||||||
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
summary_data = {}
|
||||||
company_currency = get_company_currency(filters)
|
company_currency = get_company_currency(filters)
|
||||||
|
|
||||||
for cash_flow_account in cash_flow_accounts:
|
for cash_flow_account in cash_flow_accounts:
|
||||||
@@ -142,11 +148,13 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
|||||||
section_data.append(account_data)
|
section_data.append(account_data)
|
||||||
|
|
||||||
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
||||||
companies, company_currency, True)
|
companies, company_currency, summary_data, True)
|
||||||
|
|
||||||
add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, True)
|
add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, True)
|
||||||
|
|
||||||
return data
|
report_summary = get_cash_flow_summary(summary_data, company_currency)
|
||||||
|
|
||||||
|
return data, report_summary
|
||||||
|
|
||||||
def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ def get_data(
|
|||||||
|
|
||||||
calculate_values(
|
calculate_values(
|
||||||
accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy)
|
accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy)
|
||||||
accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values)
|
accumulate_values_into_parents(accounts, accounts_by_name, period_list)
|
||||||
out = prepare_data(accounts, balance_must_be, period_list, company_currency)
|
out = prepare_data(accounts, balance_must_be, period_list, company_currency)
|
||||||
out = filter_out_zero_value_rows(out, parent_children_map)
|
out = filter_out_zero_value_rows(out, parent_children_map)
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ def calculate_values(
|
|||||||
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
|
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
|
||||||
|
|
||||||
|
|
||||||
def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values):
|
def accumulate_values_into_parents(accounts, accounts_by_name, period_list):
|
||||||
"""accumulate children's values in parent accounts"""
|
"""accumulate children's values in parent accounts"""
|
||||||
for d in reversed(accounts):
|
for d in reversed(accounts):
|
||||||
if d.parent_account:
|
if d.parent_account:
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
return frappe.db.get_link_options('Project', txt);
|
return frappe.db.get_link_options('Project', txt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "accumulated_values",
|
|
||||||
"label": __("Accumulated Values"),
|
|
||||||
"fieldtype": "Check"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "include_default_book_entries",
|
"fieldname": "include_default_book_entries",
|
||||||
"label": __("Include Default Book Entries"),
|
"label": __("Include Default Book Entries"),
|
||||||
|
|||||||
@@ -31,20 +31,22 @@ def execute(filters=None):
|
|||||||
|
|
||||||
chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
|
chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||||
|
|
||||||
report_summary = get_report_summary(columns, income, expense, net_profit_loss, filters.periodicity, period_list)
|
default_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||||
|
report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, default_currency)
|
||||||
|
|
||||||
return columns, data, None, chart, report_summary
|
return columns, data, None, chart, report_summary
|
||||||
|
|
||||||
def get_report_summary(columns, income, expense, net_profit_loss, period_list, periodicity):
|
def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, default_currency, consolidated=False):
|
||||||
income_data, expense_data, net_profit = [], [], []
|
net_income, net_expense, net_profit = 0.0, 0.0, 0.0
|
||||||
|
|
||||||
for p in columns[2:]:
|
for period in period_list:
|
||||||
|
key = period if consolidated else period.key
|
||||||
if income:
|
if income:
|
||||||
income_data.append(income[-2].get(p.get("fieldname")))
|
net_income += income[-2].get(key)
|
||||||
if expense:
|
if expense:
|
||||||
expense_data.append(expense[-2].get(p.get("fieldname")))
|
net_expense += expense[-2].get(key)
|
||||||
if net_profit_loss:
|
if net_profit_loss:
|
||||||
net_profit.append(net_profit_loss.get(p.get("fieldname")))
|
net_profit += net_profit_loss.get(key)
|
||||||
|
|
||||||
if (len(period_list) == 1 and periodicity== 'Yearly'):
|
if (len(period_list) == 1 and periodicity== 'Yearly'):
|
||||||
profit_label = _("Profit This Year")
|
profit_label = _("Profit This Year")
|
||||||
@@ -57,23 +59,23 @@ def get_report_summary(columns, income, expense, net_profit_loss, period_list, p
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"value": net_profit[-1],
|
"value": net_profit,
|
||||||
"indicator": "Green" if net_profit[-1] > 0 else "Red",
|
"indicator": "Green" if net_profit > 0 else "Red",
|
||||||
"label": profit_label,
|
"label": profit_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": net_profit_loss.get("currency")
|
"currency": net_profit_loss.get("currency") if net_profit_loss else default_currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": income_data[-1],
|
"value": net_income,
|
||||||
"label": income_label,
|
"label": income_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": income[-1].get('currency')
|
"currency": income[-1].get('currency') if income else default_currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": expense_data[-1],
|
"value": net_expense,
|
||||||
"label": expense_label,
|
"label": expense_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": expense[-1].get('currency')
|
"currency": expense[-1].get('currency') if expense else default_currency
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user