From cb86d597b7fe6a5b46a6cc8eb3ddaa740f183894 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 22 Jul 2014 19:02:11 +0530 Subject: [PATCH 1/3] Report: Trial Balance --- erpnext/accounts/page/trial_balance/README.md | 1 - .../accounts/page/trial_balance/__init__.py | 1 - .../page/trial_balance/trial_balance.js | 78 ------- .../page/trial_balance/trial_balance.json | 23 -- .../report/balance_sheet/balance_sheet.py | 11 +- .../accounts/report/financial_statements.html | 7 +- .../accounts/report/financial_statements.py | 21 +- .../profit_and_loss_statement.py | 9 +- .../accounts/report/trial_balance/__init__.py | 0 .../report/trial_balance/trial_balance.html | 1 + .../report/trial_balance/trial_balance.js | 65 ++++++ .../report/trial_balance/trial_balance.json | 17 ++ .../report/trial_balance/trial_balance.py | 216 ++++++++++++++++++ erpnext/config/accounts.py | 8 +- erpnext/patches.txt | 3 +- erpnext/public/js/financial_statements.js | 37 ++- 16 files changed, 341 insertions(+), 157 deletions(-) delete mode 100644 erpnext/accounts/page/trial_balance/README.md delete mode 100644 erpnext/accounts/page/trial_balance/__init__.py delete mode 100644 erpnext/accounts/page/trial_balance/trial_balance.js delete mode 100644 erpnext/accounts/page/trial_balance/trial_balance.json create mode 100644 erpnext/accounts/report/trial_balance/__init__.py create mode 100644 erpnext/accounts/report/trial_balance/trial_balance.html create mode 100644 erpnext/accounts/report/trial_balance/trial_balance.js create mode 100644 erpnext/accounts/report/trial_balance/trial_balance.json create mode 100644 erpnext/accounts/report/trial_balance/trial_balance.py diff --git a/erpnext/accounts/page/trial_balance/README.md b/erpnext/accounts/page/trial_balance/README.md deleted file mode 100644 index cba659d425..0000000000 --- a/erpnext/accounts/page/trial_balance/README.md +++ /dev/null @@ -1 +0,0 @@ -Period wise opening and closing balance of all transactions. \ No newline at end of file diff --git a/erpnext/accounts/page/trial_balance/__init__.py b/erpnext/accounts/page/trial_balance/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/accounts/page/trial_balance/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/accounts/page/trial_balance/trial_balance.js b/erpnext/accounts/page/trial_balance/trial_balance.js deleted file mode 100644 index 2edf82240e..0000000000 --- a/erpnext/accounts/page/trial_balance/trial_balance.js +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.require("assets/erpnext/js/account_tree_grid.js"); - -frappe.pages['trial-balance'].onload = function(wrapper) { - frappe.ui.make_app_page({ - parent: wrapper, - title: __('Trial Balance'), - single_column: true - }); - var TrialBalance = erpnext.AccountTreeGrid.extend({ - init: function(wrapper, title) { - var me = this; - this._super(wrapper, title); - - // period closing entry checkbox - this.wrapper.bind("make", function() { - $('
' + - __("With period closing entry") + '
') - .appendTo(me.wrapper) - .find("input").click(function() { me.refresh(); }); - }); - }, - - prepare_balances: function() { - // store value of with closing entry - this.with_period_closing_entry = this.wrapper - .find(".with_period_closing_entry input:checked").length; - this._super(); - this.add_total_debit_credit(); - }, - - update_balances: function(account, posting_date, v) { - // for period closing voucher, - // only consider them when adding "With Closing Entry is checked" - if(v.voucher_type === "Period Closing Voucher") { - if(this.with_period_closing_entry) { - this._super(account, posting_date, v); - } - } else { - this._super(account, posting_date, v); - } - }, - - add_total_debit_credit: function() { - var me = this; - - var total_row = { - company: me.company, - id: "Total Debit / Credit", - name: "Total Debit / Credit", - indent: 0, - opening_dr: "NA", - opening_cr: "NA", - debit: 0, - credit: 0, - checked: false, - }; - me.item_by_name[total_row.name] = total_row; - - $.each(this.data, function(i, account) { - if((account.group_or_ledger == "Ledger") || (account.rgt - account.lft == 1)) { - total_row["debit"] += account.debit; - total_row["credit"] += account.credit; - } - }); - - this.data.push(total_row); - } - }) - erpnext.trial_balance = new TrialBalance(wrapper, 'Trial Balance'); - - - wrapper.appframe.add_module_icon("Accounts") - -} diff --git a/erpnext/accounts/page/trial_balance/trial_balance.json b/erpnext/accounts/page/trial_balance/trial_balance.json deleted file mode 100644 index 72fb981f92..0000000000 --- a/erpnext/accounts/page/trial_balance/trial_balance.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "creation": "2013-01-27 16:30:52.000000", - "docstatus": 0, - "doctype": "Page", - "icon": "icon-table", - "idx": 1, - "modified": "2013-07-11 14:44:49.000000", - "modified_by": "Administrator", - "module": "Accounts", - "name": "trial-balance", - "owner": "Administrator", - "page_name": "trial-balance", - "roles": [ - { - "role": "Analytics" - }, - { - "role": "Accounts Manager" - } - ], - "standard": "Yes", - "title": "Trial Balance" -} \ No newline at end of file diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 4edc9b9515..384e15bef1 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -5,15 +5,14 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) def execute(filters=None): - process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity, from_beginning=True) - asset = get_data(filters.company, "Asset", "Debit", period_list, filters.depth) - liability = get_data(filters.company, "Liability", "Credit", period_list, filters.depth) - equity = get_data(filters.company, "Equity", "Credit", period_list, filters.depth) + asset = get_data(filters.company, "Asset", "Debit", period_list) + liability = get_data(filters.company, "Liability", "Credit", period_list) + equity = get_data(filters.company, "Equity", "Credit", period_list) provisional_profit_loss = get_provisional_profit_loss(asset, liability, equity, period_list) data = [] @@ -32,7 +31,7 @@ def get_provisional_profit_loss(asset, liability, equity, period_list): provisional_profit_loss = { "account_name": _("Provisional Profit / Loss (Credit)"), "account": None, - "is_profit_loss": True + "warn_if_negative": True } has_value = False diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html index 403e67e5bb..c0869445da 100644 --- a/erpnext/accounts/report/financial_statements.html +++ b/erpnext/accounts/report/financial_statements.html @@ -1,5 +1,5 @@ {% - if (report.columns.length > 6) { + if (report.columns.length > 8) { frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application.")); } %} @@ -17,11 +17,14 @@

{%= __(report.report_name) %}

{%= filters.company %}

{%= filters.fiscal_year %}

+{% if (filters.from_date) { %} +

{%= dateutil.str_to_user(filters.from_date) %} - {%= dateutil.str_to_user(filters.to_date) %}

+{% } %}
- + {% for(var i=2, l=report.columns.length; i{%= report.columns[i].label %} {% } %} diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 45d5b3a737..30a0fa700e 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -4,14 +4,9 @@ from __future__ import unicode_literals import frappe from frappe import _, _dict -from frappe.utils import (flt, cint, getdate, get_first_day, get_last_day, +from frappe.utils import (flt, getdate, get_first_day, get_last_day, add_months, add_days, formatdate) -def process_filters(filters): - filters.depth = cint(filters.depth) or 3 - if not filters.periodicity: - filters.periodicity = "Yearly" - def get_period_list(fiscal_year, periodicity, from_beginning=False): """Get a list of dict {"to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" @@ -76,13 +71,13 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): return period_list -def get_data(company, root_type, balance_must_be, period_list, depth, ignore_closing_entries=False): +def get_data(company, root_type, balance_must_be, period_list, ignore_closing_entries=False): accounts = get_accounts(company, root_type) if not accounts: return None - accounts, accounts_by_name = filter_accounts(accounts, depth) - gl_entries_by_account = get_gl_entries(company, root_type, period_list[0]["from_date"], period_list[-1]["to_date"], + accounts, accounts_by_name = filter_accounts(accounts) + gl_entries_by_account = get_gl_entries(company, period_list[0]["from_date"], period_list[-1]["to_date"], accounts[0].lft, accounts[0].rgt, ignore_closing_entries=ignore_closing_entries) calculate_values(accounts, gl_entries_by_account, period_list) @@ -127,8 +122,8 @@ def prepare_data(accounts, balance_must_be, period_list): "account": d.name, "parent_account": d.parent_account, "indent": flt(d.indent), - "year_start_date": year_start_date, - "year_end_date": year_end_date + "from_date": year_start_date, + "to_date": year_end_date } for period in period_list: if d.get(period.key): @@ -177,7 +172,7 @@ def get_accounts(company, root_type): return accounts -def filter_accounts(accounts, depth): +def filter_accounts(accounts, depth=10): parent_children_map = {} accounts_by_name = {} for d in accounts: @@ -204,7 +199,7 @@ def filter_accounts(accounts, depth): return filtered_accounts, accounts_by_name -def get_gl_entries(company, root_type, from_date, to_date, root_lft, root_rgt, ignore_closing_entries=False): +def get_gl_entries(company, from_date, to_date, root_lft, root_rgt, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" additional_conditions = [] 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 3a3123fc36..3df0c9b5d6 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 @@ -5,14 +5,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) def execute(filters=None): - process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity) - income = get_data(filters.company, "Income", "Credit", period_list, filters.depth, ignore_closing_entries=True) - expense = get_data(filters.company, "Expense", "Debit", period_list, filters.depth, ignore_closing_entries=True) + income = get_data(filters.company, "Income", "Credit", period_list, ignore_closing_entries=True) + expense = get_data(filters.company, "Expense", "Debit", period_list, ignore_closing_entries=True) net_profit_loss = get_net_profit_loss(income, expense, period_list) data = [] @@ -30,7 +29,7 @@ def get_net_profit_loss(income, expense, period_list): net_profit_loss = { "account_name": _("Net Profit / Loss"), "account": None, - "is_profit_loss": True + "warn_if_negative": True } for period in period_list: diff --git a/erpnext/accounts/report/trial_balance/__init__.py b/erpnext/accounts/report/trial_balance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/trial_balance/trial_balance.html b/erpnext/accounts/report/trial_balance/trial_balance.html new file mode 100644 index 0000000000..d4ae54d4f3 --- /dev/null +++ b/erpnext/accounts/report/trial_balance/trial_balance.html @@ -0,0 +1 @@ +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js new file mode 100644 index 0000000000..5050dba30f --- /dev/null +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -0,0 +1,65 @@ +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.require("assets/erpnext/js/financial_statements.js"); + +frappe.query_reports["Trial Balance"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("company"), + "reqd": 1 + }, + { + "fieldname": "fiscal_year", + "label": __("Fiscal Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1, + "on_change": function(query_report) { + var fiscal_year = query_report.get_values().fiscal_year; + if (!fiscal_year) { + return; + } + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + query_report.filters_by_name.from_date.set_input(fy.year_start_date); + query_report.filters_by_name.to_date.set_input(fy.year_end_date); + query_report.trigger_refresh(); + }); + } + }, + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.defaults.get_user_default("year_start_date"), + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.defaults.get_user_default("year_end_date"), + }, + { + "fieldname": "with_period_closing_entry", + "label": __("With Period Closing Entry"), + "fieldtype": "Check", + "default": 1 + }, + { + "fieldname": "show_zero_values", + "label": __("Show rows with zero values"), + "fieldtype": "Check" + }, + ], + "formatter": erpnext.financial_statements.formatter, + "tree": true, + "name_field": "account", + "parent_field": "parent_account", + "initial_depth": 3 +} diff --git a/erpnext/accounts/report/trial_balance/trial_balance.json b/erpnext/accounts/report/trial_balance/trial_balance.json new file mode 100644 index 0000000000..0dbb4dce83 --- /dev/null +++ b/erpnext/accounts/report/trial_balance/trial_balance.json @@ -0,0 +1,17 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2014-07-22 11:41:23.743564", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "is_standard": "Yes", + "modified": "2014-07-22 11:41:23.743564", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Trial Balance", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "Trial Balance", + "report_type": "Script Report" +} \ No newline at end of file diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py new file mode 100644 index 0000000000..dedbef9398 --- /dev/null +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -0,0 +1,216 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cint, flt, getdate, formatdate +from erpnext.accounts.report.financial_statements import filter_accounts, get_gl_entries + +value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit") + +def execute(filters): + validate_filters(filters) + data = get_data(filters) + columns = get_columns() + return columns, data + +def validate_filters(filters): + filters.year_start_date, filters.year_end_date = frappe.db.get_value("Fiscal Year", filters.fiscal_year, + ["year_start_date", "year_end_date"]) + filters.year_start_date = getdate(filters.year_start_date) + filters.year_end_date = getdate(filters.year_end_date) + + if not filters.from_date: + filters.from_date = filters.year_start_date + + if not filters.to_date: + filters.to_date = filters.year_end_date + + filters.from_date = getdate(filters.from_date) + filters.to_date = getdate(filters.to_date) + + if filters.from_date > filters.to_date: + frappe.throw(_("From Date cannot be greater than To Date")) + + if (filters.from_date < filters.year_start_date) or (filters.from_date > filters.year_end_date): + frappe.msgprint(_("From Date should be within the Fiscal Year. Assuming From Date = {0}")\ + .format(formatdate(filters.year_start_date))) + + filters.from_date = filters.year_start_date + + if (filters.to_date < filters.year_start_date) or (filters.to_date > filters.year_end_date): + frappe.msgprint(_("To Date should be within the Fiscal Year. Assuming To Date = {0}")\ + .format(formatdate(filters.year_end_date))) + filters.to_date = filters.year_end_date + +def get_data(filters): + accounts = frappe.db.sql("""select * from `tabAccount` where company=%s order by lft""", + filters.company, as_dict=True) + + if not accounts: + return None + + accounts, accounts_by_name = filter_accounts(accounts) + + min_lft, max_rgt = frappe.db.sql("""select min(lft), max(rgt) from `tabAccount` + where company=%s""", (filters.company,))[0] + + gl_entries_by_account = get_gl_entries(filters.company, None, filters.to_date, min_lft, max_rgt, + 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) + + return data + +def calculate_values(accounts, gl_entries_by_account, filters): + init = { + "opening_debit": 0.0, + "opening_credit": 0.0, + "debit": 0.0, + "credit": 0.0, + "closing_debit": 0.0, + "closing_credit": 0.0 + } + + total_row = { + "account": None, + "account_name": _("Total"), + "warn_if_negative": True, + "debit": 0.0, + "credit": 0.0 + } + + for d in accounts: + d.update(init.copy()) + + for entry in gl_entries_by_account.get(d.name, []): + posting_date = getdate(entry.posting_date) + + # opening + if posting_date < filters.from_date: + is_valid_opening = (d.root_type in ("Asset", "Liability", "Equity") or + (filters.year_start_date <= posting_date < filters.from_date)) + + if is_valid_opening: + d["opening_debit"] += flt(entry.debit) + d["opening_credit"] += flt(entry.credit) + + elif posting_date <= filters.to_date: + + if entry.is_opening == "Yes" and d.root_type in ("Asset", "Liability", "Equity"): + d["opening_debit"] += flt(entry.debit) + d["opening_credit"] += flt(entry.credit) + + else: + d["debit"] += flt(entry.debit) + d["credit"] += flt(entry.credit) + + total_row["debit"] += d["debit"] + total_row["credit"] += d["credit"] + + 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): + show_zero_values = cint(filters.show_zero_values) + data = [] + for i, d in enumerate(accounts): + has_value = False + row = { + "account_name": d.account_name, + "account": d.name, + "parent_account": d.parent_account, + "indent": d.indent, + "from_date": filters.from_date, + "to_date": filters.to_date + } + + prepare_opening_and_closing(d) + + for key in value_fields: + row[key] = d.get(key, 0.0) + if row[key]: + has_value = True + + if has_value or show_zero_values: + data.append(row) + + data.extend([{},total_row]) + + return data + +def get_columns(): + return [ + { + "fieldname": "account", + "label": _("Account"), + "fieldtype": "Link", + "options": "Account", + "width": 300 + }, + { + "fieldname": "opening_debit", + "label": _("Opening (Dr)"), + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "opening_credit", + "label": _("Opening (Cr)"), + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "debit", + "label": _("Debit"), + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "credit", + "label": _("Credit"), + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "closing_debit", + "label": _("Closing (Dr)"), + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "closing_credit", + "label": _("Closing (Cr)"), + "fieldtype": "Currency", + "width": 120 + } + ] + +def prepare_opening_and_closing(d): + d["closing_debit"] = d["opening_debit"] + d["debit"] + d["closing_credit"] = d["opening_credit"] + d["credit"] + + if d["closing_debit"] > d["closing_credit"]: + d["closing_debit"] -= d["closing_credit"] + d["closing_credit"] = 0.0 + + else: + d["closing_credit"] -= d["closing_debit"] + d["closing_debit"] = 0.0 + + if d["opening_debit"] > d["opening_credit"]: + d["opening_debit"] -= d["opening_credit"] + d["opening_credit"] = 0.0 + + else: + d["opening_credit"] -= d["opening_debit"] + d["opening_debit"] = 0.0 diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index f52cea9818..646d596387 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -166,10 +166,10 @@ def get_data(): "is_query_report": True, }, { - "type": "page", - "name": "trial-balance", - "label": _("Trial Balance"), - "icon": "icon-table" + "type": "report", + "name": "Trial Balance", + "doctype": "GL Entry", + "is_query_report": True, }, { "type": "report", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5ac1da151a..5c64213ccb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -70,4 +70,5 @@ erpnext.patches.v4_1.fix_sales_order_delivered_status erpnext.patches.v4_1.fix_delivery_and_billing_status execute:frappe.db.sql("update `tabAccount` set root_type='Liability' where root_type='Income' and report_type='Balance Sheet'") execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool") -execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool Detail") \ No newline at end of file +execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool Detail") +execute:frappe.delete_doc("Page", "trial-balance") #2014-07-22 diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 5e3ba0e116..9a1a05afaa 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -25,34 +25,21 @@ erpnext.financial_statements = { "options": "Yearly\nHalf-yearly\nQuarterly\nMonthly", "default": "Yearly", "reqd": 1 - }, - { - "fieldname": "depth", - "label": __("Depth"), - "fieldtype": "Select", - "options": "3\n4\n5", - "default": "3" } ], - "formatter": function(row, cell, value, columnDef, dataContext) { + "formatter": function(row, cell, value, columnDef, dataContext, default_formatter) { if (columnDef.df.fieldname=="account") { - var link = $("") - .text(dataContext.account_name) - .attr("onclick", "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(dataContext) + ")"); + value = dataContext.account_name; - var span = $("") - .css("padding-left", (cint(dataContext.indent) * 21) + "px") - .append(link); - - value = span.wrap("

").parent().html(); - - } else { - value = erpnext.financial_statements.default_formatter(row, cell, value, columnDef, dataContext); + columnDef.df.link_onclick = "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(dataContext) + ")"; + columnDef.df.is_tree = true; } + value = default_formatter(row, cell, value, columnDef, dataContext); + if (!dataContext.parent_account) { var $value = $(value).css("font-weight", "bold"); - if (dataContext.is_profit_loss && dataContext[columnDef.df.fieldname] < 0) { + if (dataContext.warn_if_negative && dataContext[columnDef.df.fieldname] < 0) { $value.addClass("text-danger"); } @@ -67,9 +54,13 @@ erpnext.financial_statements = { frappe.route_options = { "account": data.account, "company": frappe.query_report.filters_by_name.company.get_value(), - "from_date": data.year_start_date, - "to_date": data.year_end_date + "from_date": data.from_date, + "to_date": data.to_date }; frappe.set_route("query-report", "General Ledger"); - } + }, + "tree": true, + "name_field": "account", + "parent_field": "parent_account", + "initial_depth": 3 }; From de85a3af15393945891711587abfd0c25ab6e5e3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 22 Jul 2014 19:48:26 +0530 Subject: [PATCH 2/3] Newsletter: Bulk Send via worker --- .../support/doctype/newsletter/newsletter.py | 19 ++++++++---- erpnext/tasks.py | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 erpnext/tasks.py diff --git a/erpnext/support/doctype/newsletter/newsletter.py b/erpnext/support/doctype/newsletter/newsletter.py index 88040e21b4..aaf4ddf950 100644 --- a/erpnext/support/doctype/newsletter/newsletter.py +++ b/erpnext/support/doctype/newsletter/newsletter.py @@ -8,11 +8,12 @@ import frappe.utils from frappe.utils import cstr from frappe import throw, _ from frappe.model.document import Document +import erpnext.tasks class Newsletter(Document): def onload(self): if self.email_sent: - self.get("__onload").status_count = dict(frappe.db.sql("""select status, count(*) + self.get("__onload").status_count = dict(frappe.db.sql("""select status, count(name) from `tabBulk Email` where ref_doctype=%s and ref_docname=%s group by status""", (self.doctype, self.name))) or None @@ -28,7 +29,13 @@ class Newsletter(Document): throw(_("Newsletter has already been sent")) self.recipients = self.get_recipients() - self.send_bulk() + + if frappe.local.is_ajax: + # to avoid request timed out! + self.validate_send() + erpnext.tasks.send_newsletter.delay(frappe.local.site, self.name) + else: + self.send_bulk() frappe.msgprint(_("Scheduled to send to {0} recipients").format(len(self.recipients))) @@ -77,6 +84,10 @@ class Newsletter(Document): return email_list def send_bulk(self): + if not self.get("recipients"): + # in case it is called via worker + self.recipients = self.get_recipients() + self.validate_send() sender = self.send_from or frappe.utils.get_formatted_email(self.owner) @@ -98,10 +109,6 @@ class Newsletter(Document): if self.get("__islocal"): throw(_("Please save the Newsletter before sending")) - from frappe import conf - if (conf.get("status") or None) == "Trial": - throw(_("Newsletters is not allowed for Trial users")) - @frappe.whitelist() def get_lead_options(): return { diff --git a/erpnext/tasks.py b/erpnext/tasks.py new file mode 100644 index 0000000000..da2ab03ccc --- /dev/null +++ b/erpnext/tasks.py @@ -0,0 +1,29 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.celery_app import celery_task, task_logger + +@celery_task() +def send_newsletter(site, newsletter): + try: + frappe.connect(site=site) + doc = frappe.get_doc("Newsletter", newsletter) + doc.send_bulk() + + except: + frappe.db.rollback() + task_logger.warn(frappe.get_traceback()) + + # wasn't able to send emails :( + doc.db_set("email_sent", 0) + frappe.db.commit() + + raise + + else: + frappe.db.commit() + + finally: + frappe.destroy() From d1a2ce27509c77677a50e929a7ec1cfabfec2663 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 23 Jul 2014 12:51:17 +0530 Subject: [PATCH 3/3] [minor] is_ajax check in newsletter --- erpnext/support/doctype/newsletter/newsletter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/support/doctype/newsletter/newsletter.py b/erpnext/support/doctype/newsletter/newsletter.py index aaf4ddf950..57063bbc6a 100644 --- a/erpnext/support/doctype/newsletter/newsletter.py +++ b/erpnext/support/doctype/newsletter/newsletter.py @@ -30,7 +30,7 @@ class Newsletter(Document): self.recipients = self.get_recipients() - if frappe.local.is_ajax: + if getattr(frappe.local, "is_ajax", False): # to avoid request timed out! self.validate_send() erpnext.tasks.send_newsletter.delay(frappe.local.site, self.name)