mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-19 01:25:07 +00:00
feat: show opening/closing balance in cash flow report (#47877)
* feat: add checkbox to carryforward opening balance
* fix: ignore period closing voucher
* chore: rename filter check box
* feat: add total for opening and closing balance
* fix: update section name
* fix: remove section rename
---------
Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 88b9f8d68c)
This commit is contained in:
committed by
Mergify
parent
85f1efe67e
commit
7fd5b2b26a
@@ -14,9 +14,16 @@ erpnext.utils.add_dimensions("Cash Flow", 10);
|
|||||||
|
|
||||||
frappe.query_reports["Cash Flow"]["filters"].splice(8, 1);
|
frappe.query_reports["Cash Flow"]["filters"].splice(8, 1);
|
||||||
|
|
||||||
frappe.query_reports["Cash Flow"]["filters"].push({
|
frappe.query_reports["Cash Flow"]["filters"].push(
|
||||||
fieldname: "include_default_book_entries",
|
{
|
||||||
label: __("Include Default FB Entries"),
|
fieldname: "include_default_book_entries",
|
||||||
fieldtype: "Check",
|
label: __("Include Default FB Entries"),
|
||||||
default: 1,
|
fieldtype: "Check",
|
||||||
});
|
default: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "show_opening_and_closing_balance",
|
||||||
|
label: __("Show Opening and Closing Balance"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr
|
from frappe.query_builder import DocType
|
||||||
|
from frappe.utils import cstr, flt
|
||||||
|
from pypika import Order
|
||||||
|
|
||||||
from erpnext.accounts.report.financial_statements import (
|
from erpnext.accounts.report.financial_statements import (
|
||||||
get_columns,
|
get_columns,
|
||||||
@@ -12,6 +16,7 @@ from erpnext.accounts.report.financial_statements import (
|
|||||||
get_data,
|
get_data,
|
||||||
get_filtered_list_for_consolidated_report,
|
get_filtered_list_for_consolidated_report,
|
||||||
get_period_list,
|
get_period_list,
|
||||||
|
set_gl_entries_by_account,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
|
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
|
||||||
get_net_profit_loss,
|
get_net_profit_loss,
|
||||||
@@ -119,10 +124,20 @@ def execute(filters=None):
|
|||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
|
|
||||||
add_total_row_account(
|
net_change_in_cash = add_total_row_account(
|
||||||
data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters
|
data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters
|
||||||
)
|
)
|
||||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company, True)
|
|
||||||
|
if filters.show_opening_and_closing_balance:
|
||||||
|
show_opening_and_closing_balance(data, period_list, company_currency, net_change_in_cash, filters)
|
||||||
|
|
||||||
|
columns = get_columns(
|
||||||
|
filters.periodicity,
|
||||||
|
period_list,
|
||||||
|
filters.accumulated_values,
|
||||||
|
filters.company,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
chart = get_chart_data(columns, data, company_currency)
|
chart = get_chart_data(columns, data, company_currency)
|
||||||
|
|
||||||
@@ -255,6 +270,137 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data,
|
|||||||
out.append(total_row)
|
out.append(total_row)
|
||||||
out.append({})
|
out.append({})
|
||||||
|
|
||||||
|
return total_row
|
||||||
|
|
||||||
|
|
||||||
|
def show_opening_and_closing_balance(out, period_list, currency, net_change_in_cash, filters):
|
||||||
|
opening_balance = {
|
||||||
|
"section_name": "Opening",
|
||||||
|
"section": "Opening",
|
||||||
|
"currency": currency,
|
||||||
|
}
|
||||||
|
closing_balance = {
|
||||||
|
"section_name": "Closing (Opening + Total)",
|
||||||
|
"section": "Closing (Opening + Total)",
|
||||||
|
"currency": currency,
|
||||||
|
}
|
||||||
|
|
||||||
|
opening_amount = get_opening_balance(filters.company, period_list, filters) or 0.0
|
||||||
|
running_total = opening_amount
|
||||||
|
|
||||||
|
for i, period in enumerate(period_list):
|
||||||
|
key = period["key"]
|
||||||
|
change = net_change_in_cash.get(key, 0.0)
|
||||||
|
|
||||||
|
opening_balance[key] = opening_amount if i == 0 else running_total
|
||||||
|
running_total += change
|
||||||
|
closing_balance[key] = running_total
|
||||||
|
|
||||||
|
opening_balance["total"] = opening_balance[period_list[0]["key"]]
|
||||||
|
closing_balance["total"] = closing_balance[period_list[-1]["key"]]
|
||||||
|
|
||||||
|
out.extend([opening_balance, net_change_in_cash, closing_balance, {}])
|
||||||
|
|
||||||
|
|
||||||
|
def get_opening_balance(company, period_list, filters):
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
cash_value = {}
|
||||||
|
account_types = get_cash_flow_accounts()
|
||||||
|
net_profit_loss = 0.0
|
||||||
|
|
||||||
|
local_filters = deepcopy(filters)
|
||||||
|
local_filters.start_date, local_filters.end_date = get_opening_range_using_fiscal_year(
|
||||||
|
company, period_list
|
||||||
|
)
|
||||||
|
|
||||||
|
for section in account_types:
|
||||||
|
section_name = section.get("section_name")
|
||||||
|
cash_value.setdefault(section_name, 0.0)
|
||||||
|
|
||||||
|
if section_name == "Operations":
|
||||||
|
net_profit_loss += get_net_income(company, period_list, local_filters)
|
||||||
|
|
||||||
|
for account in section.get("account_types", []):
|
||||||
|
account_type = account.get("account_type")
|
||||||
|
local_filters.account_type = account_type
|
||||||
|
|
||||||
|
amount = get_account_type_based_gl_data(company, local_filters) or 0.0
|
||||||
|
|
||||||
|
if account_type == "Depreciation":
|
||||||
|
cash_value[section_name] += amount * -1
|
||||||
|
else:
|
||||||
|
cash_value[section_name] += amount
|
||||||
|
|
||||||
|
return sum(cash_value.values()) + net_profit_loss
|
||||||
|
|
||||||
|
|
||||||
|
def get_net_income(company, period_list, filters):
|
||||||
|
gl_entries_by_account_for_income, gl_entries_by_account_for_expense = {}, {}
|
||||||
|
income, expense = 0.0, 0.0
|
||||||
|
from_date, to_date = get_opening_range_using_fiscal_year(company, period_list)
|
||||||
|
|
||||||
|
for root_type in ["Income", "Expense"]:
|
||||||
|
for root in frappe.db.sql(
|
||||||
|
"""select lft, rgt from tabAccount
|
||||||
|
where root_type=%s and ifnull(parent_account, '') = ''""",
|
||||||
|
root_type,
|
||||||
|
as_dict=1,
|
||||||
|
):
|
||||||
|
set_gl_entries_by_account(
|
||||||
|
company,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
filters,
|
||||||
|
gl_entries_by_account_for_income
|
||||||
|
if root_type == "Income"
|
||||||
|
else gl_entries_by_account_for_expense,
|
||||||
|
root.lft,
|
||||||
|
root.rgt,
|
||||||
|
root_type=root_type,
|
||||||
|
ignore_closing_entries=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
for entries in gl_entries_by_account_for_income.values():
|
||||||
|
for entry in entries:
|
||||||
|
if entry.posting_date <= to_date:
|
||||||
|
amount = (entry.debit - entry.credit) * -1
|
||||||
|
income = flt((income + amount), 2)
|
||||||
|
|
||||||
|
for entries in gl_entries_by_account_for_expense.values():
|
||||||
|
for entry in entries:
|
||||||
|
if entry.posting_date <= to_date:
|
||||||
|
amount = entry.debit - entry.credit
|
||||||
|
expense = flt((expense + amount), 2)
|
||||||
|
|
||||||
|
return income - expense
|
||||||
|
|
||||||
|
|
||||||
|
def get_opening_range_using_fiscal_year(company, period_list):
|
||||||
|
first_from_date = period_list[0]["from_date"]
|
||||||
|
previous_day = first_from_date - timedelta(days=1)
|
||||||
|
|
||||||
|
# Get the earliest fiscal year for the company
|
||||||
|
|
||||||
|
FiscalYear = DocType("Fiscal Year")
|
||||||
|
FiscalYearCompany = DocType("Fiscal Year Company")
|
||||||
|
|
||||||
|
earliest_fy = (
|
||||||
|
frappe.qb.from_(FiscalYear)
|
||||||
|
.join(FiscalYearCompany)
|
||||||
|
.on(FiscalYearCompany.parent == FiscalYear.name)
|
||||||
|
.select(FiscalYear.year_start_date)
|
||||||
|
.where(FiscalYearCompany.company == company)
|
||||||
|
.orderby(FiscalYear.year_start_date, order=Order.asc)
|
||||||
|
.limit(1)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
if not earliest_fy:
|
||||||
|
frappe.throw(_("Not able to find the earliest Fiscal Year for the given company."))
|
||||||
|
|
||||||
|
company_start_date = earliest_fy[0]["year_start_date"]
|
||||||
|
return company_start_date, previous_day
|
||||||
|
|
||||||
|
|
||||||
def get_report_summary(summary_data, currency):
|
def get_report_summary(summary_data, currency):
|
||||||
report_summary = []
|
report_summary = []
|
||||||
@@ -276,7 +422,7 @@ def get_chart_data(columns, data, currency):
|
|||||||
for section in data
|
for section in data
|
||||||
if section.get("parent_section") is None and section.get("currency")
|
if section.get("parent_section") is None and section.get("currency")
|
||||||
]
|
]
|
||||||
datasets = datasets[:-1]
|
datasets = datasets[:-2]
|
||||||
|
|
||||||
chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
|
chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user