diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 760fa649e67..f22f3a10091 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -11,5 +11,3 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": 1 }); }); - - diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 455664ffea9..391f57beac6 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + // The last item in the array is the definition for Presentation Currency + // 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"].pop(); + frappe.query_reports["Cash Flow"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index b0c49dfbd81..5d2a35bd960 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -2,12 +2,14 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe + import re -from frappe import _ -from frappe.utils import (flt, getdate, get_first_day, get_last_day, date_diff, - add_months, add_days, formatdate, cint) + +import frappe +from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from erpnext.accounts.utils import get_fiscal_year +from frappe import _ +from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate) def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, @@ -84,6 +86,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v return period_list + def get_fiscal_year_data(from_fiscal_year, to_fiscal_year): fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date, max(year_end_date) as year_end_date from `tabFiscal Year` where @@ -92,16 +95,19 @@ def get_fiscal_year_data(from_fiscal_year, to_fiscal_year): return fiscal_year[0] if fiscal_year else {} + 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'): frappe.throw(_("End Year cannot be before Start Year")) + def get_months(start_date, end_date): diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month) return diff + 1 + def get_label(periodicity, from_date, to_date): - if periodicity=="Yearly": + if periodicity == "Yearly": if formatdate(from_date, "YYYY") == formatdate(to_date, "YYYY"): label = formatdate(from_date, "YYYY") else: @@ -111,28 +117,34 @@ def get_label(periodicity, from_date, to_date): return label -def get_data(company, root_type, balance_must_be, period_list, filters=None, + +def get_data( + company, root_type, balance_must_be, period_list, filters=None, accumulated_values=1, only_current_fiscal_year=True, ignore_closing_entries=False, ignore_accumulated_values_for_fy=False): + accounts = get_accounts(company, root_type) if not accounts: return None accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) - company_currency = frappe.db.get_value("Company", company, "default_currency") + company_currency = get_appropriate_currency(company, filters) 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(company, + set_gl_entries_by_account( + company, period_list[0]["year_start_date"] if only_current_fiscal_year else None, period_list[-1]["to_date"], root.lft, root.rgt, filters, - gl_entries_by_account, ignore_closing_entries=ignore_closing_entries) + gl_entries_by_account, ignore_closing_entries=ignore_closing_entries + ) - calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy) + calculate_values( + 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) out = prepare_data(accounts, balance_must_be, period_list, company_currency) out = filter_out_zero_value_rows(out, parent_children_map) @@ -143,7 +155,15 @@ def get_data(company, root_type, balance_must_be, period_list, filters=None, return out -def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy): +def get_appropriate_currency(company, filters=None): + if filters and filters.get("presentation_currency"): + return filters["presentation_currency"] + else: + return frappe.db.get_value("Company", company, "default_currency") + + +def calculate_values( + accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy): for entries in gl_entries_by_account.values(): for entry in entries: d = accounts_by_name.get(entry.account) @@ -164,6 +184,7 @@ def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accum if entry.posting_date < period_list[0].year_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, period_list, accumulated_values): """accumulate children's values in parent accounts""" for d in reversed(accounts): @@ -175,6 +196,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accu accounts_by_name[d.parent_account]["opening_balance"] = \ accounts_by_name[d.parent_account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0) + def prepare_data(accounts, balance_must_be, period_list, company_currency): data = [] year_start_date = period_list[0]["year_start_date"].strftime("%Y-%m-%d") @@ -192,10 +214,10 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency): "year_start_date": year_start_date, "year_end_date": year_end_date, "currency": company_currency, - "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1) + "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1) }) for period in period_list: - if d.get(period.key) and balance_must_be=="Credit": + if d.get(period.key) and balance_must_be == "Credit": # change sign based on Debit or Credit, since calculation is done using (debit - credit) d[period.key] *= -1 @@ -212,6 +234,7 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency): return data + def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False): data_with_value = [] for d in data: @@ -228,6 +251,7 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False return data_with_value + def add_total_row(out, root_type, balance_must_be, period_list, company_currency): total_row = { "account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'", @@ -246,16 +270,19 @@ def add_total_row(out, root_type, balance_must_be, period_list, company_currency total_row["total"] += flt(row["total"]) row["total"] = "" - if total_row.has_key("total"): + if "total" in total_row: out.append(total_row) # blank row after Total out.append({}) + def get_accounts(company, root_type): - return frappe.db.sql("""select name, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount` + return frappe.db.sql( + """select name, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount` where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True) + def filter_accounts(accounts, depth=10): parent_children_map = {} accounts_by_name = {} @@ -280,6 +307,7 @@ def filter_accounts(accounts, depth=10): return filtered_accounts, accounts_by_name, parent_children_map + def sort_root_accounts(roots): """Sort root types as Asset, Liability, Equity, Income, Expense""" @@ -299,13 +327,14 @@ def sort_root_accounts(roots): roots.sort(compare_roots) -def set_gl_entries_by_account(company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, - ignore_closing_entries=False): + +def set_gl_entries_by_account( + company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters) - gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year from `tabGL Entry` + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s @@ -321,11 +350,15 @@ def set_gl_entries_by_account(company, from_date, to_date, root_lft, root_rgt, f }, as_dict=True) + if filters and filters.get('presentation_currency'): + convert_to_presentation_currency(gl_entries, get_currency(filters)) + for entry in gl_entries: gl_entries_by_account.setdefault(entry.account, []).append(entry) return gl_entries_by_account + def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] @@ -337,15 +370,17 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if filters: if filters.get("project"): - additional_conditions.append("project = '%s'"%(frappe.db.escape(filters.get("project")))) + additional_conditions.append("project = '%s'" % (frappe.db.escape(filters.get("project")))) if filters.get("cost_center"): additional_conditions.append(get_cost_center_cond(filters.get("cost_center"))) return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" + def get_cost_center_cond(cost_center): lft, rgt = frappe.db.get_value("Cost Center", cost_center, ["lft", "rgt"]) - return (""" cost_center in (select name from `tabCost Center` where lft >=%s and rgt <=%s)"""%(lft, rgt)) + return """ cost_center in (select name from `tabCost Center` where lft >=%s and rgt <=%s)""" % (lft, rgt) + def get_columns(periodicity, period_list, accumulated_values=1, company=None): columns = [{ diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 9e1b884e1a8..5cfdc9f1560 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -44,44 +44,24 @@
{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %} {% } %} - {% if(filters.print_in_account_currency) { %} - {%= format_currency(data[i].debit_in_account_currency, data[i].account_currency) %} - + {%= format_currency(data[i].debit, filters.presentation_currency) %} - {%= format_currency(data[i].credit_in_account_currency, data[i].account_currency) %} - - {% } else { %} - - {%= format_currency(data[i].debit) %} - - {%= format_currency(data[i].credit) %} - {% } %} + {%= format_currency(data[i].credit, filters.presentation_currency) %} {% } else { %} {%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %} - {% if(filters.print_in_account_currency) { %} - - {%= data[i].account && format_currency(data[i].debit_in_account_currency, data[i].account_currency) %} - - {%= data[i].account && format_currency(data[i].credit_in_account_currency, data[i].account_currency) %} - {% } else { %} - - {%= data[i].account && format_currency(data[i].debit) %} - - - {%= data[i].account && format_currency(data[i].credit) %} - - {% } %} - {% } %} - {% if(filters.print_in_account_currency) { %} - {%= format_currency(data[i].balance_in_account_currency, data[i].account_currency) %} + {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency) %} + + + {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency) %} + + {% } %} + + {%= format_currency(data[i].balance, filters.presentation_currency) %} - {% } else { %} - {%= format_currency(data[i].balance) %} - {% } %} {% } %} diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index adefadd2db0..d6a2aec9545 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -1,4 +1,4 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.query_reports["General Ledger"] = { @@ -107,9 +107,10 @@ frappe.query_reports["General Ledger"] = { "fieldtype": "Check", }, { - "fieldname":"print_in_account_currency", - "label": __("Print in Account Currency"), - "fieldtype": "Check", + "fieldname": "presentation_currency", + "label": __("Currency"), + "fieldtype": "Select", + "options": erpnext.get_presentation_currency_list() } ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 8e4259c5426..70ab67f7d05 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -3,10 +3,13 @@ from __future__ import unicode_literals import frappe +from erpnext import get_company_currency, get_default_company +from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from frappe.utils import getdate, cstr, flt, fmt_money from frappe import _, _dict from erpnext.accounts.utils import get_account_currency + def execute(filters=None): account_details = {} @@ -29,6 +32,7 @@ def execute(filters=None): return columns, res + def validate_filters(filters, account_details): if not filters.get('company'): frappe.throw(_('{0} is mandatory').format(_('Company'))) @@ -37,7 +41,7 @@ def validate_filters(filters, account_details): frappe.throw(_("Account {0} does not exists").format(filters.account)) if filters.get("account") and filters.get("group_by_account") \ - and account_details[filters.account].is_group == 0: + and account_details[filters.account].is_group == 0: frappe.throw(_("Can not filter based on Account, if grouped by Account")) if filters.get("voucher_no") and filters.get("group_by_voucher"): @@ -56,6 +60,7 @@ def validate_party(filters): elif not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) + def set_account_currency(filters): if not (filters.get("account") or filters.get("party")): return filters @@ -66,8 +71,13 @@ def set_account_currency(filters): if filters.get("account"): account_currency = get_account_currency(filters.account) elif filters.get("party"): - gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type, - "party": filters.party, "company": filters.company}, "account_currency") + gle_currency = frappe.db.get_value( + "GL Entry", { + "party_type": filters.party_type, "party": filters.party, "company": filters.company + }, + "account_currency" + ) + if gle_currency: account_currency = gle_currency else: @@ -90,29 +100,39 @@ def get_result(filters, account_details): return result + def get_gl_entries(filters): + currency_map = get_currency(filters) select_fields = """, sum(debit_in_account_currency) as debit_in_account_currency, sum(credit_in_account_currency) as credit_in_account_currency""" \ - if filters.get("show_in_account_currency") else "" + group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \ if filters.get("group_by_voucher") else "group by name" - gl_entries = frappe.db.sql(""" + gl_entries = frappe.db.sql( + """ select posting_date, account, party_type, party, sum(debit) as debit, sum(credit) as credit, voucher_type, voucher_no, cost_center, project, - against_voucher_type, against_voucher, + against_voucher_type, against_voucher, account_currency, remarks, against, is_opening {select_fields} from `tabGL Entry` where company=%(company)s {conditions} {group_by_condition} - order by posting_date, account"""\ - .format(select_fields=select_fields, conditions=get_conditions(filters), - group_by_condition=group_by_condition), filters, as_dict=1) + order by posting_date, account + """.format( + select_fields=select_fields, conditions=get_conditions(filters), + group_by_condition=group_by_condition + ), + filters, as_dict=1) + + if filters.get('presentation_currency'): + return convert_to_presentation_currency(gl_entries, currency_map) + else: + return gl_entries - return gl_entries def get_conditions(filters): conditions = [] @@ -132,16 +152,20 @@ def get_conditions(filters): if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")): conditions.append("posting_date >=%(from_date)s") + conditions.append("posting_date <=%(to_date)s") if filters.get("project"): conditions.append("project=%(project)s") from frappe.desk.reportview import build_match_conditions match_conditions = build_match_conditions("GL Entry") - if match_conditions: conditions.append(match_conditions) + + if match_conditions: + conditions.append(match_conditions) return "and {}".format(" and ".join(conditions)) if conditions else "" + def get_data_with_opening_closing(filters, account_details, gl_entries): data = [] gle_map = initialize_gle_map(gl_entries) @@ -178,14 +202,15 @@ def get_data_with_opening_closing(filters, account_details, gl_entries): return data + def get_totals_dict(): def _get_debit_credit_dict(label): return _dict( - account = "'{0}'".format(label), - debit = 0.0, - credit = 0.0, - debit_in_account_currency = 0.0, - credit_in_account_currency = 0.0 + account="'{0}'".format(label), + debit=0.0, + credit=0.0, + debit_in_account_currency=0.0, + credit_in_account_currency=0.0 ) return _dict( opening = _get_debit_credit_dict(_('Opening')), @@ -193,12 +218,14 @@ def get_totals_dict(): closing = _get_debit_credit_dict(_('Closing (Opening + Total)')) ) + def initialize_gle_map(gl_entries): gle_map = frappe._dict() for gle in gl_entries: - gle_map.setdefault(gle.account, _dict(totals = get_totals_dict(), entries = [])) + gle_map.setdefault(gle.account, _dict(totals=get_totals_dict(), entries=[])) return gle_map + def get_accountwise_gle(filters, gl_entries, gle_map): totals = get_totals_dict() entries = [] @@ -210,13 +237,12 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) for gle in gl_entries: if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes": update_value_in_dict(gle_map[gle.account].totals, 'opening', gle) update_value_in_dict(totals, 'opening', gle) - + update_value_in_dict(gle_map[gle.account].totals, 'closing', gle) update_value_in_dict(totals, 'closing', gle) @@ -233,6 +259,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): return totals, entries + def get_result_as_list(data, filters): balance, balance_in_account_currency = 0, 0 inv_details = get_supplier_invoice_details() @@ -272,6 +299,15 @@ def get_balance(row, balance, debit_field, credit_field): return balance def get_columns(filters): + if filters.get("presentation_currency"): + currency = filters["presentation_currency"] + else: + if filters.get("company"): + currency = get_company_currency(filters["company"]) + else: + company = get_default_company() + currency = get_company_currency(company) + columns = [ { "label": _("Posting Date"), @@ -287,47 +323,25 @@ def get_columns(filters): "width": 180 }, { - "label": _("Debit"), + "label": _("Debit ({0})".format(currency)), "fieldname": "debit", "fieldtype": "Float", "width": 100 }, { - "label": _("Credit"), + "label": _("Credit ({0})".format(currency)), "fieldname": "credit", "fieldtype": "Float", "width": 100 }, { - "label": _("Balance (Dr - Cr)"), + "label": _("Balance ({0})".format(currency)), "fieldname": "balance", "fieldtype": "Float", "width": 130 } ] - if filters.get("show_in_account_currency"): - columns.extend([ - { - "label": _("Debit") + " (" + filters.account_currency + ")", - "fieldname": "debit_in_account_currency", - "fieldtype": "Float", - "width": 100 - }, - { - "label": _("Credit") + " (" + filters.account_currency + ")", - "fieldname": "credit_in_account_currency", - "fieldtype": "Float", - "width": 100 - }, - { - "label": _("Balance") + " (" + filters.account_currency + ")", - "fieldname": "balance_in_account_currency", - "fieldtype": "Data", - "width": 100 - } - ]) - columns.extend([ { "label": _("Voucher Type"), diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index bcac2dffa43..a02c592edd7 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -1,6 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt + frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); @@ -24,4 +25,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "Check" } ); -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py new file mode 100644 index 00000000000..4490398d0d0 --- /dev/null +++ b/erpnext/accounts/report/utils.py @@ -0,0 +1,128 @@ +import frappe +from erpnext import get_company_currency, get_default_company +from erpnext.setup.utils import get_exchange_rate +from frappe.utils import cint + +__exchange_rates = {} +P_OR_L_ACCOUNTS = list( + sum(frappe.get_list('Account', fields=['name'], or_filters=[{'root_type': 'Income'}, {'root_type': 'Expense'}], as_list=True), ()) +) + + +def get_currency(filters): + """ + Returns a dictionary containing currency information. The keys of the dict are + - company: The company for which we are fetching currency information. if no + company is specified, it will fallback to the default company. + - company currency: The functional currency of the said company. + - presentation currency: The presentation currency to use. Only currencies that + have been used for transactions will be allowed. + - report date: The report date. + :param filters: Report filters + :type filters: dict + + :return: str - Currency + """ + company = get_appropriate_company(filters) + company_currency = get_company_currency(company) + presentation_currency = filters['presentation_currency'] if filters.get('presentation_currency') else company_currency + report_date = filters.get('to_date') or filters.get('to_fiscal_year') + + currency_map = dict(company=company, company_currency=company_currency, presentation_currency=presentation_currency, report_date=report_date) + + return currency_map + + +def convert(value, from_, to, date): + """ + convert `value` from `from_` to `to` on `date` + :param value: Amount to be converted + :param from_: Currency of `value` + :param to: Currency to convert to + :param date: exchange rate as at this date + :return: Result of converting `value` + """ + rate = get_rate_as_at(date, from_, to) + converted_value = value / (rate or 1) + return converted_value + + +def get_rate_as_at(date, from_currency, to_currency): + """ + Gets exchange rate as at `date` for `from_currency` - `to_currency` exchange rate. + This calls `get_exchange_rate` so that we can get the correct exchange rate as per + the user's Accounts Settings. + It is made efficient by memoising results to `__exchange_rates` + :param date: exchange rate as at this date + :param from_currency: Base currency + :param to_currency: Quote currency + :return: Retrieved exchange rate + """ + rate = __exchange_rates.get('{0}-{1}@{2}'.format(from_currency, to_currency, date)) + if not rate: + rate = get_exchange_rate(from_currency, to_currency, date) or 1 + __exchange_rates['{0}-{1}@{2}'.format(from_currency, to_currency, date)] = rate + + return rate + + +def is_p_or_l_account(account_name): + """ + Check if the given `account name` is an `Account` with `root_type` of either 'Income' + or 'Expense'. + :param account_name: + :return: Boolean + """ + return account_name in P_OR_L_ACCOUNTS + + +def convert_to_presentation_currency(gl_entries, currency_info): + """ + Take a list of GL Entries and change the 'debit' and 'credit' values to currencies + in `currency_info`. + :param gl_entries: + :param currency_info: + :return: + """ + converted_gl_list = [] + presentation_currency = currency_info['presentation_currency'] + company_currency = currency_info['company_currency'] + + for entry in gl_entries: + account = entry['account'] + debit = cint(entry['debit']) + credit = cint(entry['credit']) + debit_in_account_currency = cint(entry['debit_in_account_currency']) + credit_in_account_currency = cint(entry['credit_in_account_currency']) + account_currency = entry['account_currency'] + + if account_currency != presentation_currency or (account_currency == presentation_currency and not is_p_or_l_account(account)): + value = debit or credit + + date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date'] + + converted_value = convert(value, presentation_currency, company_currency, date) + + if entry.get('debit'): + entry['debit'] = converted_value + else: + entry['credit'] = converted_value + + elif account_currency == presentation_currency: + if entry.get('debit'): + entry['debit'] = debit_in_account_currency + else: + entry['credit'] = credit_in_account_currency + + converted_gl_list.append(entry) + + return converted_gl_list + + +def get_appropriate_company(filters): + if filters.get('company'): + company = filters['company'] + else: + company = get_default_company() + + return company diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 1eeb8965f05..29e3999f0a4 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -98,6 +98,16 @@ function get_filters(){ ], "default": "Monthly", "reqd": 1 + }, + // Note: + // If you are modifying this array such that the presentation_currency object + // is no longer the last object, please make adjustments in cash_flow.js + // accordingly. + { + "fieldname": "presentation_currency", + "label": __("Currency"), + "fieldtype": "Select", + "options": erpnext.get_presentation_currency_list() } ] } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 6841354f47a..d50fa195db1 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -13,6 +13,12 @@ $.extend(erpnext, { return frappe.boot.sysdefaults.currency; }, + get_presentation_currency_list: () => { + const docs = frappe.boot.docs; + const currency_list = docs.filter(d => d.doctype === ":Currency").map(d => d.name); + return currency_list; + }, + toggle_naming_series: function() { if(cur_frm.fields_dict.naming_series) { cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false);