brotherton-erpnext/erpnext/accounts/report/cash_flow/cash_flow.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

279 lines
7.5 KiB
Python
Raw Normal View History

2015-12-12 02:33:01 +00:00
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
2015-12-16 10:39:43 +00:00
import frappe
2015-12-12 02:33:01 +00:00
from frappe import _
from frappe.utils import cint, cstr
2015-12-12 02:33:01 +00:00
from erpnext.accounts.report.financial_statements import (
get_columns,
get_data,
get_filtered_list_for_consolidated_report,
get_period_list,
)
2015-12-12 02:33:01 +00:00
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
get_net_profit_loss,
)
2016-08-22 10:23:01 +00:00
from erpnext.accounts.utils import get_fiscal_year
2015-12-12 02:33:01 +00:00
def execute(filters=None):
Cutomisable Cash Flow Reports (#12969) * add child doctype - Cash Flow Mapping Account * adds new doctype - Cash Flow Mapping * adds new doctype - Cash Flow Mapper * adds new doctype Cash Flow Mapping Template * adds new doctype Cash Flow Mapping Template * adds adjustments to Cash Flow Mapper: - remove fields from Cash Flow Mapping Template Details - update in Cash FLow Mapper * get cash_flow_accouts from Cash Flow Mapping * change `tmp` to `mappers` and make sure `mappers` is sorted by its `position` field * changes description from 'Net Profit/Loss' to 'Profit for the year' * set `net_profit_loss` `parent_account` properly * modify `get_account_type_based_data`: - changed signature such that `account_type` parameter is now `account_name` - where clause in query is now based on `name` * remove zero rows * de-duplicates row, summing similar accounts in the process * makes gl sum calculation use `parent_account` as a condition * add the `section_leader` immediately after adding net profit, sorts `accounts` by `is_working_capital` field * adds `is_working_capital` to "account_types" so that we can use this to determine when to add "Changes in working capital" for operating activities * add "Movement in working capital" subheader * refactor code for readability * adds new fields to `Cash Flow Mapping`: - `is_interest_paid` to allow me recognise accounts for 'Interest Paid' - `is_income_tax_paid` to allow me recognise accounts for 'Income Taxes Paid' * allow `Cash Flow Mapping` to be renamable * adds new field - `section_subtotal` useful for only Operating Activities * changes `Cash Flow Mapping` doctype fields: - remove `is_income_tax`_field - add `is_income_tax_liability` field to identify tax payable accounts - add `is_income_tax_expense` field to identify tax expense accounts in P or L * calculates and shows tax paid adjustment in cash flow statement * renames `is_interest_paid` to `is_finance_cost` * - adds finance costs calculation - correctly sets opening balance dates * prevents users from selecting extra options in Cash Flow Mapping * adds validation to prevent selecting multiple options * adds new fields to Cash Flow Mapping * calculate non cash p or l items (2nd pass) * separates default cash flow generation from custom * adds new setting to Accounts Settings: - allow user elect to use customised cash flow report * clean up * removes mandatory constraint from accounts field * allow rename, disallow create and delete * adds patch to add default Cash Flow Mappers * refactors custom_cashflow * add article to explain configuration * refactor * further refactor * final clean up (hopefully) * clean up for codacy * more codacy fixes * more codacy fixes * fix broken patch * rename article to .md * create default mappers after install * PEP 8 * create the tables in `after_install` call
2018-02-22 05:38:36 +00:00
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
2022-03-28 13:22:46 +00:00
Cutomisable Cash Flow Reports (#12969) * add child doctype - Cash Flow Mapping Account * adds new doctype - Cash Flow Mapping * adds new doctype - Cash Flow Mapper * adds new doctype Cash Flow Mapping Template * adds new doctype Cash Flow Mapping Template * adds adjustments to Cash Flow Mapper: - remove fields from Cash Flow Mapping Template Details - update in Cash FLow Mapper * get cash_flow_accouts from Cash Flow Mapping * change `tmp` to `mappers` and make sure `mappers` is sorted by its `position` field * changes description from 'Net Profit/Loss' to 'Profit for the year' * set `net_profit_loss` `parent_account` properly * modify `get_account_type_based_data`: - changed signature such that `account_type` parameter is now `account_name` - where clause in query is now based on `name` * remove zero rows * de-duplicates row, summing similar accounts in the process * makes gl sum calculation use `parent_account` as a condition * add the `section_leader` immediately after adding net profit, sorts `accounts` by `is_working_capital` field * adds `is_working_capital` to "account_types" so that we can use this to determine when to add "Changes in working capital" for operating activities * add "Movement in working capital" subheader * refactor code for readability * adds new fields to `Cash Flow Mapping`: - `is_interest_paid` to allow me recognise accounts for 'Interest Paid' - `is_income_tax_paid` to allow me recognise accounts for 'Income Taxes Paid' * allow `Cash Flow Mapping` to be renamable * adds new field - `section_subtotal` useful for only Operating Activities * changes `Cash Flow Mapping` doctype fields: - remove `is_income_tax`_field - add `is_income_tax_liability` field to identify tax payable accounts - add `is_income_tax_expense` field to identify tax expense accounts in P or L * calculates and shows tax paid adjustment in cash flow statement * renames `is_interest_paid` to `is_finance_cost` * - adds finance costs calculation - correctly sets opening balance dates * prevents users from selecting extra options in Cash Flow Mapping * adds validation to prevent selecting multiple options * adds new fields to Cash Flow Mapping * calculate non cash p or l items (2nd pass) * separates default cash flow generation from custom * adds new setting to Accounts Settings: - allow user elect to use customised cash flow report * clean up * removes mandatory constraint from accounts field * allow rename, disallow create and delete * adds patch to add default Cash Flow Mappers * refactors custom_cashflow * add article to explain configuration * refactor * further refactor * final clean up (hopefully) * clean up for codacy * more codacy fixes * more codacy fixes * fix broken patch * rename article to .md * create default mappers after install * PEP 8 * create the tables in `after_install` call
2018-02-22 05:38:36 +00:00
return execute_custom(filters=filters)
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,
)
2015-12-16 10:39:43 +00:00
cash_flow_accounts = get_cash_flow_accounts()
2015-12-16 10:39:43 +00:00
# compute net profit / loss
2018-09-13 07:26:04 +00:00
income = get_data(
filters.company,
"Income",
"Credit",
period_list,
filters=filters,
2016-08-22 10:23:01 +00:00
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True,
ignore_accumulated_values_for_fy=True,
)
2018-09-13 07:26:04 +00:00
expense = get_data(
filters.company,
"Expense",
"Debit",
period_list,
filters=filters,
2016-08-22 10:23:01 +00:00
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True,
ignore_accumulated_values_for_fy=True,
)
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
2015-12-16 10:39:43 +00:00
data = []
summary_data = {}
2018-08-08 11:07:31 +00:00
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
2015-12-16 10:39:43 +00:00
for cash_flow_account in cash_flow_accounts:
section_data = []
data.append(
{
"account_name": cash_flow_account["section_header"],
2015-12-16 10:39:43 +00:00
"parent_account": None,
"indent": 0.0,
2015-12-16 10:39:43 +00:00
"account": cash_flow_account["section_header"],
}
)
if len(data) == 1:
# add first net income in operations section
if net_profit_loss:
net_profit_loss.update(
{"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]}
2015-12-16 10:39:43 +00:00
)
data.append(net_profit_loss)
section_data.append(net_profit_loss)
for account in cash_flow_account["account_types"]:
account_data = get_account_type_based_data(
filters.company, account["account_type"], period_list, filters.accumulated_values, filters
)
2015-12-16 10:39:43 +00:00
account_data.update(
{
"account_name": account["label"],
"account": account["label"],
2015-12-16 10:39:43 +00:00
"indent": 1,
"parent_account": cash_flow_account["section_header"],
"currency": company_currency,
2015-12-16 10:39:43 +00:00
}
)
data.append(account_data)
section_data.append(account_data)
add_total_row_account(
data,
section_data,
cash_flow_account["section_footer"],
period_list,
company_currency,
summary_data,
filters,
)
2015-12-16 10:39:43 +00:00
add_total_row_account(
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
)
2015-12-16 10:39:43 +00:00
2019-07-10 14:58:52 +00:00
chart = get_chart_data(columns, data)
report_summary = get_report_summary(summary_data, company_currency)
return columns, data, None, chart, report_summary
2015-12-16 10:39:43 +00:00
2022-03-28 13:22:46 +00:00
def get_cash_flow_accounts():
operation_accounts = {
"section_name": "Operations",
"section_footer": _("Net Cash from Operations"),
"section_header": _("Cash Flow from Operations"),
"account_types": [
{"account_type": "Depreciation", "label": _("Depreciation")},
{"account_type": "Receivable", "label": _("Net Change in Accounts Receivable")},
{"account_type": "Payable", "label": _("Net Change in Accounts Payable")},
{"account_type": "Stock", "label": _("Net Change in Inventory")},
],
}
investing_accounts = {
"section_name": "Investing",
"section_footer": _("Net Cash from Investing"),
"section_header": _("Cash Flow from Investing"),
"account_types": [{"account_type": "Fixed Asset", "label": _("Net Change in Fixed Asset")}],
}
financing_accounts = {
"section_name": "Financing",
"section_footer": _("Net Cash from Financing"),
"section_header": _("Cash Flow from Financing"),
"account_types": [{"account_type": "Equity", "label": _("Net Change in Equity")}],
}
# combine all cash flow accounts for iteration
return [operation_accounts, investing_accounts, financing_accounts]
Cutomisable Cash Flow Reports (#12969) * add child doctype - Cash Flow Mapping Account * adds new doctype - Cash Flow Mapping * adds new doctype - Cash Flow Mapper * adds new doctype Cash Flow Mapping Template * adds new doctype Cash Flow Mapping Template * adds adjustments to Cash Flow Mapper: - remove fields from Cash Flow Mapping Template Details - update in Cash FLow Mapper * get cash_flow_accouts from Cash Flow Mapping * change `tmp` to `mappers` and make sure `mappers` is sorted by its `position` field * changes description from 'Net Profit/Loss' to 'Profit for the year' * set `net_profit_loss` `parent_account` properly * modify `get_account_type_based_data`: - changed signature such that `account_type` parameter is now `account_name` - where clause in query is now based on `name` * remove zero rows * de-duplicates row, summing similar accounts in the process * makes gl sum calculation use `parent_account` as a condition * add the `section_leader` immediately after adding net profit, sorts `accounts` by `is_working_capital` field * adds `is_working_capital` to "account_types" so that we can use this to determine when to add "Changes in working capital" for operating activities * add "Movement in working capital" subheader * refactor code for readability * adds new fields to `Cash Flow Mapping`: - `is_interest_paid` to allow me recognise accounts for 'Interest Paid' - `is_income_tax_paid` to allow me recognise accounts for 'Income Taxes Paid' * allow `Cash Flow Mapping` to be renamable * adds new field - `section_subtotal` useful for only Operating Activities * changes `Cash Flow Mapping` doctype fields: - remove `is_income_tax`_field - add `is_income_tax_liability` field to identify tax payable accounts - add `is_income_tax_expense` field to identify tax expense accounts in P or L * calculates and shows tax paid adjustment in cash flow statement * renames `is_interest_paid` to `is_finance_cost` * - adds finance costs calculation - correctly sets opening balance dates * prevents users from selecting extra options in Cash Flow Mapping * adds validation to prevent selecting multiple options * adds new fields to Cash Flow Mapping * calculate non cash p or l items (2nd pass) * separates default cash flow generation from custom * adds new setting to Accounts Settings: - allow user elect to use customised cash flow report * clean up * removes mandatory constraint from accounts field * allow rename, disallow create and delete * adds patch to add default Cash Flow Mappers * refactors custom_cashflow * add article to explain configuration * refactor * further refactor * final clean up (hopefully) * clean up for codacy * more codacy fixes * more codacy fixes * fix broken patch * rename article to .md * create default mappers after install * PEP 8 * create the tables in `after_install` call
2018-02-22 05:38:36 +00:00
2022-03-28 13:22:46 +00:00
def get_account_type_based_data(company, account_type, period_list, accumulated_values, filters):
2015-12-16 10:39:43 +00:00
data = {}
total = 0
2015-12-16 10:39:43 +00:00
for period in period_list:
2017-04-25 10:56:33 +00:00
start_date = get_start_date(period, accumulated_values, company)
amount = get_account_type_based_gl_data(
company, start_date, period["to_date"], account_type, filters
)
if amount and account_type == "Depreciation":
amount *= -1
2016-08-22 10:23:01 +00:00
total += amount
data.setdefault(period["key"], amount)
2016-08-22 10:23:01 +00:00
data["total"] = total
2015-12-16 10:39:43 +00:00
return data
2022-03-28 13:22:46 +00:00
def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
cond = ""
filters = frappe._dict(filters or {})
2020-01-31 05:46:30 +00:00
if filters.include_default_book_entries:
company_fb = frappe.db.get_value("Company", company, "default_finance_book")
cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL)
""" % (
frappe.db.escape(filters.finance_book),
frappe.db.escape(company_fb),
)
else:
cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" % (
frappe.db.escape(cstr(filters.finance_book))
2022-03-28 13:22:46 +00:00
)
gl_sum = frappe.db.sql_list(
"""
select sum(credit) - sum(debit)
from `tabGL Entry`
where company=%s and posting_date >= %s and posting_date <= %s
and voucher_type != 'Period Closing Voucher'
and account in ( SELECT name FROM tabAccount WHERE account_type = %s) {cond}
""".format(
cond=cond
2022-03-28 13:22:46 +00:00
),
(company, start_date, end_date, account_type),
)
return gl_sum[0] if gl_sum and gl_sum[0] else 0
Cutomisable Cash Flow Reports (#12969) * add child doctype - Cash Flow Mapping Account * adds new doctype - Cash Flow Mapping * adds new doctype - Cash Flow Mapper * adds new doctype Cash Flow Mapping Template * adds new doctype Cash Flow Mapping Template * adds adjustments to Cash Flow Mapper: - remove fields from Cash Flow Mapping Template Details - update in Cash FLow Mapper * get cash_flow_accouts from Cash Flow Mapping * change `tmp` to `mappers` and make sure `mappers` is sorted by its `position` field * changes description from 'Net Profit/Loss' to 'Profit for the year' * set `net_profit_loss` `parent_account` properly * modify `get_account_type_based_data`: - changed signature such that `account_type` parameter is now `account_name` - where clause in query is now based on `name` * remove zero rows * de-duplicates row, summing similar accounts in the process * makes gl sum calculation use `parent_account` as a condition * add the `section_leader` immediately after adding net profit, sorts `accounts` by `is_working_capital` field * adds `is_working_capital` to "account_types" so that we can use this to determine when to add "Changes in working capital" for operating activities * add "Movement in working capital" subheader * refactor code for readability * adds new fields to `Cash Flow Mapping`: - `is_interest_paid` to allow me recognise accounts for 'Interest Paid' - `is_income_tax_paid` to allow me recognise accounts for 'Income Taxes Paid' * allow `Cash Flow Mapping` to be renamable * adds new field - `section_subtotal` useful for only Operating Activities * changes `Cash Flow Mapping` doctype fields: - remove `is_income_tax`_field - add `is_income_tax_liability` field to identify tax payable accounts - add `is_income_tax_expense` field to identify tax expense accounts in P or L * calculates and shows tax paid adjustment in cash flow statement * renames `is_interest_paid` to `is_finance_cost` * - adds finance costs calculation - correctly sets opening balance dates * prevents users from selecting extra options in Cash Flow Mapping * adds validation to prevent selecting multiple options * adds new fields to Cash Flow Mapping * calculate non cash p or l items (2nd pass) * separates default cash flow generation from custom * adds new setting to Accounts Settings: - allow user elect to use customised cash flow report * clean up * removes mandatory constraint from accounts field * allow rename, disallow create and delete * adds patch to add default Cash Flow Mappers * refactors custom_cashflow * add article to explain configuration * refactor * further refactor * final clean up (hopefully) * clean up for codacy * more codacy fixes * more codacy fixes * fix broken patch * rename article to .md * create default mappers after install * PEP 8 * create the tables in `after_install` call
2018-02-22 05:38:36 +00:00
2022-03-28 13:22:46 +00:00
def get_start_date(period, accumulated_values, company):
if not accumulated_values and period.get("from_date"):
return period["from_date"]
2016-08-22 10:23:01 +00:00
start_date = period["year_start_date"]
if accumulated_values:
start_date = get_fiscal_year(period.to_date, company=company)[1]
2016-08-22 10:23:01 +00:00
return start_date
2015-12-16 10:39:43 +00:00
2022-03-28 13:22:46 +00:00
def add_total_row_account(
out, data, label, period_list, currency, summary_data, filters, consolidated=False
):
2015-12-16 10:39:43 +00:00
total_row = {
"account_name": "'" + _("{0}").format(label) + "'",
"account": "'" + _("{0}").format(label) + "'",
"currency": currency,
2015-12-16 10:39:43 +00:00
}
summary_data[label] = 0
# from consolidated financial statement
if filters.get("accumulated_in_group_company"):
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
2015-12-16 10:39:43 +00:00
for row in data:
if row.get("parent_account"):
for period in period_list:
key = period if consolidated else period["key"]
total_row.setdefault(key, 0.0)
total_row[key] += row.get(key, 0.0)
summary_data[label] += row.get(key)
total_row.setdefault("total", 0.0)
total_row["total"] += row["total"]
2015-12-16 10:39:43 +00:00
out.append(total_row)
2019-07-10 14:58:52 +00:00
out.append({})
def get_report_summary(summary_data, currency):
report_summary = []
for label, value in summary_data.items():
report_summary.append(
{"value": value, "label": label, "datatype": "Currency", "currency": currency}
)
return report_summary
2019-07-10 14:58:52 +00:00
def get_chart_data(columns, data):
labels = [d.get("label") for d in columns[2:]]
datasets = [
{
"name": account.get("account").replace("'", ""),
"values": [account.get(d.get("fieldname")) for d in columns[2:]],
}
2019-07-10 14:58:52 +00:00
for account in data
if account.get("parent_account") == None and account.get("currency")
]
datasets = datasets[:-1]
chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
2019-07-12 06:54:05 +00:00
chart["fieldtype"] = "Currency"
2019-07-10 14:58:52 +00:00
2019-07-12 06:54:05 +00:00
return chart