# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt, getdate, formatdate, cstr from erpnext.accounts.report.financial_statements import filter_accounts, filter_out_zero_value_rows from erpnext.accounts.report.trial_balance.trial_balance import validate_filters value_fields = ("income", "expense", "gross_profit_loss") def execute(filters=None): based_on = filters.based_on.replace(' ', '_').lower() validate_filters(filters) accounts = get_accounts_data(based_on, filters.company) data = get_data(accounts, filters, based_on) columns = get_columns(filters) return columns, data def get_accounts_data(based_on, company): if based_on == 'cost_center': return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt from `tabCost Center` where company=%s order by lft""", company, as_dict=True) else: return frappe.get_all('Project', fields = ["name"], filters = {'company': company}) def get_data(accounts, filters, based_on): if not accounts: return None accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) gl_entries_by_account = {} set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, based_on, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) total_row = calculate_values(accounts, gl_entries_by_account, filters) accumulate_values_into_parents(accounts, accounts_by_name) data = prepare_data(accounts, filters, total_row, parent_children_map, based_on) data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values")) return data def calculate_values(accounts, gl_entries_by_account, filters): init = { "income": 0.0, "expense": 0.0, "gross_profit_loss": 0.0 } total_row = { "cost_center": None, "account_name": "'" + _("Total") + "'", "warn_if_negative": True, "income": 0.0, "expense": 0.0, "gross_profit_loss": 0.0 } for d in accounts: d.update(init.copy()) # add opening for entry in gl_entries_by_account.get(d.name, []): if cstr(entry.is_opening) != "Yes": if entry.type == 'Income': d["income"] += flt(entry.credit) - flt(entry.debit) if entry.type == 'Expense': d["expense"] += flt(entry.debit) - flt(entry.credit) d["gross_profit_loss"] = d.get("income") - d.get("expense") total_row["income"] += d["income"] total_row["expense"] += d["expense"] total_row["gross_profit_loss"] = total_row.get("income") - total_row.get("expense") return total_row def accumulate_values_into_parents(accounts, accounts_by_name): for d in reversed(accounts): if d.parent_account: for key in value_fields: accounts_by_name[d.parent_account][key] += d[key] def prepare_data(accounts, filters, total_row, parent_children_map, based_on): data = [] company_currency = frappe.db.get_value("Company", filters.company, "default_currency") for d in accounts: has_value = False row = { "account_name": d.account_name or d.name, "account": d.name, "parent_account": d.parent_account, "indent": d.indent, "fiscal_year": filters.fiscal_year, "currency": company_currency, "based_on": based_on } for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) if abs(row[key]) >= 0.005: # ignore zero values has_value = True row["has_value"] = has_value data.append(row) data.extend([{},total_row]) return data def get_columns(filters): return [ { "fieldname": "account", "label": _(filters.based_on), "fieldtype": "Link", "options": filters.based_on, "width": 300 }, { "fieldname": "income", "label": _("Income"), "fieldtype": "Currency", "options": "currency", "width": 120 }, { "fieldname": "expense", "label": _("Expense"), "fieldtype": "Currency", "options": "currency", "width": 120 }, { "fieldname": "gross_profit_loss", "label": _("Gross Profit / Loss"), "fieldtype": "Currency", "options": "currency", "width": 120 }, { "fieldname": "currency", "label": _("Currency"), "fieldtype": "Link", "options": "Currency", "hidden": 1 } ] def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_by_account, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" additional_conditions = [] if ignore_closing_entries: additional_conditions.append("and ifnull(voucher_type, '')!='Period Closing Voucher'") if from_date: additional_conditions.append("and posting_date >= %(from_date)s") gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, is_opening, (select root_type from `tabAccount` where name = account) as type from `tabGL Entry` where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s and {based_on} is not null order by {based_on}, posting_date""".format(additional_conditions="\n".join(additional_conditions), based_on= based_on), { "company": company, "from_date": from_date, "to_date": to_date }, as_dict=True) for entry in gl_entries: gl_entries_by_account.setdefault(entry.based_on, []).append(entry) return gl_entries_by_account