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

197 lines
6.7 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
from __future__ import unicode_literals
2015-12-16 10:39:43 +00:00
import frappe
2015-12-12 02:33:01 +00:00
from frappe import _
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
from frappe.utils import cint
2015-12-16 10:39:43 +00:00
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
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
return execute_custom(filters=filters)
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
2017-04-25 10:56:33 +00:00
filters.periodicity, filters.accumulated_values, 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 = []
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)
2015-12-16 10:39:43 +00:00
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
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)
return columns, data, None, chart
2015-12-16 10:39:43 +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
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
def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}):
cond = ""
filters = frappe._dict(filters)
if filters.finance_book:
cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(filters.finance_book))
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 = '' OR finance_book IS NULL)"
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), (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
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
def add_total_row_account(out, data, label, period_list, currency, 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
}
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)
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_chart_data(columns, data):
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 = 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