From 24a2c9b97893ce272fad75800db8575a03ddbe50 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 7 Apr 2020 12:16:25 +0530 Subject: [PATCH] feat: Date range in financial statements (#21020) * feat: Date range in financial statements * feat: Add date range filter in consolidated financial statement * fix: Handle API changes in query_report Co-authored-by: Saqib --- .../report/balance_sheet/balance_sheet.py | 1 + .../accounts/report/cash_flow/cash_flow.js | 2 +- .../accounts/report/cash_flow/cash_flow.py | 3 +- .../consolidated_financial_statement.js | 33 +++++++++++++++++++ .../consolidated_financial_statement.py | 29 +++++++++------- .../accounts/report/financial_statements.py | 31 ++++++++++++----- .../profit_and_loss_statement.py | 3 +- erpnext/public/js/financial_statements.js | 33 +++++++++++++++++++ 8 files changed, 112 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index cb1fdc1a79a..a858c1998f5 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -9,6 +9,7 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c def execute(filters=None): 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) currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency") diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 904874f6459..e5d0c899181 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -9,7 +9,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. - frappe.query_reports["Cash Flow"]["filters"].splice(5, 1); + frappe.query_reports["Cash Flow"]["filters"].splice(8, 1); frappe.query_reports["Cash Flow"]["filters"].push( { diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index be6e93cae0b..cf0946beaba 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -17,7 +17,8 @@ def execute(filters=None): return execute_custom(filters=filters) period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, - filters.periodicity, filters.accumulated_values, filters.company) + filters.period_start_date, filters.period_end_date, filters.filter_based_on, + filters.periodicity, company=filters.company) cash_flow_accounts = get_cash_flow_accounts() diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 92c5ee9124c..38fd5fa2787 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -12,6 +12,39 @@ frappe.query_reports["Consolidated Financial Statement"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, { "fieldname":"from_fiscal_year", "label": __("Start Year"), diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 461291b4531..b62238b59bb 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt, cint +from frappe.utils import flt, cint, getdate 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.balance_sheet.balance_sheet import (get_provisional_profit_loss, @@ -208,17 +208,24 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i company_currency = get_company_currency(filters) + if filters.filter_based_on == 'Fiscal Year': + start_date = fiscal_year.year_start_date + end_date = fiscal_year.year_end_date + else: + start_date = filters.period_start_date + end_date = filters.period_end_date + gl_entries_by_account = {} 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(fiscal_year.year_start_date, - fiscal_year.year_end_date, root.lft, root.rgt, filters, + set_gl_entries_by_account(start_date, + end_date, root.lft, root.rgt, filters, gl_entries_by_account, accounts_by_name, ignore_closing_entries=False) - calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters) + calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters) accumulate_values_into_parents(accounts, accounts_by_name, companies) - out = prepare_data(accounts, fiscal_year, balance_must_be, companies, company_currency) + out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency) if out: add_total_row(out, root_type, balance_must_be, companies, company_currency) @@ -229,7 +236,7 @@ def get_company_currency(filters=None): return (filters.get('presentation_currency') or frappe.get_cached_value('Company', filters.company, "default_currency")) -def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters): +def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters): for entries in gl_entries_by_account.values(): for entry in entries: key = entry.account_number or entry.account_name @@ -241,7 +248,7 @@ def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_ and entry.company in companies.get(company)): d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit) - if entry.posting_date < fiscal_year.year_start_date: + if entry.posting_date < getdate(start_date): d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit) def accumulate_values_into_parents(accounts, accounts_by_name, companies): @@ -295,10 +302,8 @@ def get_accounts(root_type, filters): `tabAccount` where company = %s and root_type = %s """ , (filters.get('company'), root_type), as_dict=1) -def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_currency): +def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency): data = [] - year_start_date = fiscal_year.year_start_date - year_end_date = fiscal_year.year_end_date for d in accounts: # add to output @@ -309,8 +314,8 @@ def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_curr "account": _(d.account_name), "parent_account": _(d.parent_account), "indent": flt(d.indent), - "year_start_date": year_start_date, - "year_end_date": year_end_date, + "year_start_date": start_date, + "year_end_date": end_date, "currency": company_currency, "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1) }) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index e760b790963..81effd06683 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -18,17 +18,20 @@ from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, for from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children -def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, +def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, company=None, reset_period_on_fy_change=True): """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" - fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year) - validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year) - - # start with first day, so as to avoid year to_dates like 2-April if ever they occur] - year_start_date = getdate(fiscal_year.year_start_date) - year_end_date = getdate(fiscal_year.year_end_date) + if filter_based_on == 'Fiscal Year': + fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year) + validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year) + year_start_date = getdate(fiscal_year.year_start_date) + year_end_date = getdate(fiscal_year.year_end_date) + else: + validate_dates(period_start_date, period_end_date) + year_start_date = getdate(period_start_date) + year_end_date = getdate(period_end_date) months_to_add = { "Yearly": 12, @@ -42,6 +45,9 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v start_date = year_start_date months = get_months(year_start_date, year_end_date) + if (months // months_to_add) != (months / months_to_add): + months += months_to_add + for i in range(months // months_to_add): period = frappe._dict({ "from_date": start_date @@ -103,9 +109,18 @@ def get_fiscal_year_data(from_fiscal_year, to_fiscal_year): def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year): - if not fiscal_year.get('year_start_date') and not fiscal_year.get('year_end_date'): + if not fiscal_year.get('year_start_date') or not fiscal_year.get('year_end_date'): + frappe.throw(_("Start Year and End Year are mandatory")) + + if getdate(fiscal_year.get('year_end_date')) < getdate(fiscal_year.get('year_start_date')): frappe.throw(_("End Year cannot be before Start Year")) +def validate_dates(from_date, to_date): + if not from_date or not to_date: + frappe.throw("From Date and To Date are mandatory") + + if to_date < from_date: + frappe.throw("To Date cannot be less than From Date") def get_months(start_date, end_date): diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 81fb1e012e2..7caa7648b12 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -9,7 +9,8 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c def execute(filters=None): period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, - filters.periodicity, filters.accumulated_values, filters.company) + filters.period_start_date, filters.period_end_date, filters.filter_based_on, filters.periodicity, + company=filters.company) income = get_data(filters.company, "Income", "Credit", period_list, filters = filters, accumulated_values=filters.accumulated_values, diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index dead309a5d0..296c6280d84 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -78,6 +78,39 @@ function get_filters(){ "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, { "fieldname":"from_fiscal_year", "label": __("Start Year"),