From 609b25bcdb708804438473a0f727a966d4411b2f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Sat, 15 Sep 2012 22:11:23 +0530 Subject: [PATCH] refactor email digest --- .../doctype/email_digest/email_digest.js | 4 +- .../doctype/email_digest/email_digest.py | 961 ++++++------------ .../doctype/email_digest/email_digest.txt | 285 +++--- 3 files changed, 455 insertions(+), 795 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index 9d8d5b5db7..0ff57363d7 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -22,7 +22,7 @@ cur_frm.cscript.refresh = function(doc, dt, dn) { cur_frm.add_custom_button('View Now', function() { doc = locals[dt][dn]; if(doc.__unsaved != 1) { - $c_obj(make_doclist(dt, dn), 'get', '', function(r, rt) { + $c_obj(make_doclist(dt, dn), 'get_digest_msg', '', function(r, rt) { if(r.exc) { msgprint(err_msg); console.log(r.exc); @@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, dt, dn) { width: 800 }); - $a(d.body, 'div', '', '', r['message'][1]); + $a(d.body, 'div', '', '', r['message']); d.show(); } diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index da18c54233..aa3f24521f 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -16,697 +16,330 @@ from __future__ import unicode_literals import webnotes -import webnotes.utils +from webnotes.utils import fmt_money, formatdate, now_datetime, cstr +from datetime import timedelta +from dateutil.relativedelta import relativedelta + +content_sequence = ["income_year_to_date", "bank_balance", + "income", "expenses_booked", "collections", "payments", + "invoiced_amount", "payables", + "new_leads", "new_enquiries", "new_quotations", "new_sales_orders", + "new_delivery_notes", "new_purchase_requests", "new_supplier_quotations", + "new_purchase_orders", "new_purchase_receipts", "new_stock_entries", + "new_support_tickets", "new_communications", "new_projects"] class DocType: def __init__(self, doc, doclist=[]): self.doc, self.doclist = doc, doclist - self.sending = False - def get_profiles(self): - """ - Get a list of profiles - """ + """get list of profiles""" import webnotes profile_list = webnotes.conn.sql(""" - SELECT name, enabled FROM tabProfile - WHERE docstatus=0 AND name NOT IN ('Administrator', 'Guest') - ORDER BY enabled DESC, name ASC""", as_dict=1) + select name, enabled from tabProfile + where docstatus=0 and name not in ('Administrator', 'Guest') + order by enabled desc, name asc""", as_dict=1) + if self.doc.recipient_list: recipient_list = self.doc.recipient_list.split("\n") else: recipient_list = [] for p in profile_list: - if p['name'] in recipient_list: p['checked'] = 1 - else: p['checked'] = 0 + p["checked"] = p["name"] in recipient_list and 1 or 0 + webnotes.response['profile_list'] = profile_list - - - def get_standard_data(self): - """ - Executes standard queries - """ - res = {} - query_dict = { - - 'invoiced_amount': self.generate_gle_query({ - 'type': 'invoiced_amount', - 'field': 'debit', - 'master_type': 'Customer', - }), - - 'payables': self.generate_gle_query({ - 'type': 'payables', - 'field': 'credit', - 'master_type': 'Supplier', - }), - - 'collections': self.generate_gle_query({ - 'type': 'collections', - 'field': 'credit', - 'master_type': 'Customer', - }), - - 'payments': self.generate_gle_query({ - 'type': 'payments', - 'field': 'debit', - 'master_type': 'Supplier', - }), - - 'income': self.generate_gle_query({ - 'type': 'income', - 'debit_or_credit': 'Credit' - }), - - 'income_year_to_date': self.generate_gle_query({ - 'type': 'income_year_to_date', - 'debit_or_credit': 'Credit' - }), - - 'expenses_booked': self.generate_gle_query({ - 'type': 'expenses_booked', - 'debit_or_credit': 'Debit' - }), - - 'bank_balance': self.generate_gle_query({ - 'type': 'bank_balance' - }), - - 'new_leads': self.generate_new_type_query({ - 'type': 'new_leads', - 'doctype': 'Lead' - }), - - 'new_enquiries': self.generate_new_type_query({ - 'type': 'new_enquiries', - 'doctype': 'Opportunity' - }), - - 'new_quotations': self.generate_new_type_query({ - 'type': 'new_quotations', - 'doctype': 'Quotation', - 'sum_col': 'grand_total' - }), - - 'new_sales_orders': self.generate_new_type_query({ - 'type': 'new_sales_orders', - 'doctype': 'Sales Invoice', - 'sum_col': 'grand_total' - }), - - 'new_purchase_orders': self.generate_new_type_query({ - 'type': 'new_purchase_orders', - 'doctype': 'Purchase Order', - 'sum_col': 'grand_total' - }), - - 'new_transactions': self.generate_new_type_query({ - 'type': 'new_transactions', - 'doctype': 'Feed' - }), - - 'stock_below_rl': "" - } - - result = {} - - for query in query_dict.keys(): - if self.doc.fields[query] and query_dict[query]: - #webnotes.msgprint(query) - res = webnotes.conn.sql(query_dict[query], as_dict=1) - if query in ['income', 'income_year_to_date']: - for r in res: - r['value'] = float(r['credit'] - r['debit']) - elif query in ['expenses_booked', 'bank_balance']: - for r in res: - r['value'] = float(r['debit'] - r['credit']) - #webnotes.msgprint(query) - #webnotes.msgprint(res) - result[query] = res and (len(res)==1 and res[0]) or (res or None) - if result[query] is None: - del result[query] - - return result - - - def generate_gle_query(self, args): - """ - Returns generated query string based 'tabGL Entry' and 'tabAccount' - """ - self.process_args(args) - - query = None - - if args['type'] in ['invoiced_amount', 'payables']: - query = """ - SELECT - IFNULL(SUM(IFNULL(gle.%(field)s, 0)), 0) AS '%(field)s', - %(common_select)s - FROM - %(common_from)s - WHERE - %(common_where)s AND - ac.master_type = '%(master_type)s' AND - %(start_date_condition)s AND - %(end_date_condition)s""" % args - - elif args['type'] in ['collections', 'payments']: - args['bc_accounts_regex'] = self.get_bc_accounts_regex() - if args['bc_accounts_regex']: - query = """ - SELECT - IFNULL(SUM(IFNULL(gle.%(field)s, 0)), 0) AS '%(field)s', - %(common_select)s - FROM - %(common_from)s - WHERE - %(common_where)s AND - ac.master_type = '%(master_type)s' AND - gle.against REGEXP '%(bc_accounts_regex)s' AND - %(start_date_condition)s AND - %(end_date_condition)s""" % args - - elif args['type'] in ['income', 'expenses_booked']: - query = """ - SELECT - IFNULL(SUM(IFNULL(gle.debit, 0)), 0) AS 'debit', - IFNULL(SUM(IFNULL(gle.credit, 0)), 0) AS 'credit', - %(common_select)s - FROM - %(common_from)s - WHERE - %(common_where)s AND - ac.is_pl_account = 'Yes' AND - ac.debit_or_credit = '%(debit_or_credit)s' AND - %(start_date_condition)s AND - %(end_date_condition)s""" % args - - elif args['type'] == 'income_year_to_date': - query = """ - SELECT - IFNULL(SUM(IFNULL(gle.debit, 0)), 0) AS 'debit', - IFNULL(SUM(IFNULL(gle.credit, 0)), 0) AS 'credit', - %(common_select)s - FROM - %(common_from)s - WHERE - %(common_where)s AND - ac.is_pl_account = 'Yes' AND - ac.debit_or_credit = '%(debit_or_credit)s' AND - %(fiscal_start_date_condition)s AND - %(end_date_condition)s""" % args - - elif args['type'] == 'bank_balance': - query = """ - SELECT - ac.account_name AS 'name', - IFNULL(SUM(IFNULL(gle.debit, 0)), 0) AS 'debit', - IFNULL(SUM(IFNULL(gle.credit, 0)), 0) AS 'credit', - %(common_select)s - FROM - %(common_from)s - WHERE - %(common_where)s AND - ac.account_type = 'Bank or Cash' AND - %(end_date_condition)s - GROUP BY - ac.account_name""" % args - - return query - - - def process_args(self, args): - """ - Adds common conditions in dictionary "args" - """ - start_date, end_date = self.get_start_end_dates() - fiscal_year = webnotes.utils.get_defaults()['fiscal_year'] - fiscal_start_date = webnotes.conn.get_value('Fiscal Year', fiscal_year, - 'year_start_date') - - if 'new' in args['type']: - args.update({ - 'company': self.doc.company, - 'start_date': start_date, - 'end_date': end_date, - 'sum_if_reqd': '' - }) - if args['type'] in ['new_quotations', 'new_sales_orders', 'new_purchase_orders']: - args['sum_if_reqd'] = "IFNULL(SUM(IFNULL(%(sum_col)s, 0)), 0) AS '%(sum_col)s'," % args - - if args['type'] == 'new_transactions': - # tabFeed doesn't have company column - # using this arg to set condition of feed_type as null - # so that comments, logins and assignments are not counted - args['company_condition'] = "feed_type IS NULL AND" - else: - args['company_condition'] = "company = '%(company)s' AND" % args - - else: - args.update({ - 'common_select': "COUNT(*) AS 'count'", - - 'common_from': "`tabGL Entry` gle, `tabAccount` ac", - - 'common_where': """ - gle.company = '%s' AND - gle.account = ac.name AND - ac.docstatus < 2 AND - IFNULL(gle.is_cancelled, 'No') = 'No'""" % self.doc.company, - - 'start_date_condition': "gle.posting_date >= '%s'" % start_date, - - 'end_date_condition': "gle.posting_date <= '%s'" % end_date, - - 'fiscal_start_date_condition': "gle.posting_date >= '%s'" % fiscal_start_date - }) - - - def get_start_end_dates(self): - """ - Returns start and end date depending on the frequency of email digest - """ - from datetime import datetime, date, timedelta - from webnotes.utils import now_datetime - today = now_datetime().date() - year, month, day = today.year, today.month, today.day - - if self.doc.frequency == 'Daily': - if self.sending: - start_date = end_date = today - timedelta(days=1) - else: - start_date = end_date = today - - elif self.doc.frequency == 'Weekly': - if self.sending: - start_date = today - timedelta(days=today.weekday(), weeks=1) - end_date = start_date + timedelta(days=6) - else: - start_date = today - timedelta(days=today.weekday()) - end_date = start_date + timedelta(days=6) - - else: - import calendar - - if self.sending: - if month == 1: - year = year - 1 - prev_month = 12 - else: - prev_month = month - 1 - start_date = date(year, prev_month, 1) - last_day = calendar.monthrange(year, prev_month)[1] - end_date = date(year, prev_month, last_day) - else: - start_date = date(year, month, 1) - last_day = calendar.monthrange(year, month)[1] - end_date = date(year, month, last_day) - - return start_date, end_date - - - def generate_new_type_query(self, args): - """ - Returns generated query string for calculating new transactions created - """ - self.process_args(args) - - query = """ - SELECT - %(sum_if_reqd)s - COUNT(*) AS 'count' - FROM - `tab%(doctype)s` - WHERE - docstatus < 2 AND - %(company_condition)s - DATE(creation) >= '%(start_date)s' AND - DATE(creation) <= '%(end_date)s'""" % args - - return query - - def get_bc_accounts_regex(self): - """ - Returns a regular expression of 'Bank or Cash' type account list - """ - bc_account_list = webnotes.conn.sql(""" - SELECT name - FROM `tabAccount` - WHERE account_type = 'Bank or Cash'""", as_list=1) - - if bc_account_list: - return '(' + '|'.join([ac[0] for ac in bc_account_list]) + ')' - - - def get(self): - """ - * Execute Query - * Prepare Email Body from Print Format - """ - result, email_body = self.execute_queries() - #webnotes.msgprint(result) - #webnotes.msgprint(email_body) - return result, email_body - - - def execute_queries(self): - """ - * If standard==1, execute get_standard_data - * If standard==0, execute python code in custom_code field - """ - result = {} - if int(self.doc.use_standard)==1: - result = self.get_standard_data() - email_body = self.get_standard_body(result) - else: - result, email_body = self.execute_custom_code(self.doc) - - #webnotes.msgprint(result) - - return result, email_body - - - def execute_custom_code(self, doc): - """ - Execute custom python code - """ - pass - - def send(self): - """ - * Execute get method - * Send email to recipients - """ - if not self.doc.recipient_list: return - - self.sending = True - result, email_body = self.get() + # send email only to enabled users + valid_users = [p[0] for p in webnotes.conn.sql("""select name from `tabProfile` + where enabled=1""")] + recipients = filter(lambda r: r in valid_users, + self.doc.recipient_list.split("\n")) - recipient_list = self.doc.recipient_list.split("\n") - - # before sending, check if user is disabled or not - # do not send if disabled - profile_list = webnotes.conn.sql("SELECT name, enabled FROM tabProfile", as_dict=1) - for profile in profile_list: - if profile['name'] in recipient_list and profile['enabled'] == 0: - del recipient_list[recipient_list.index(profile['name'])] - from webnotes.utils.email_lib import sendmail - try: - sendmail( - recipients=recipient_list, - sender='notifications+email_digest@erpnext.com', - subject=self.doc.frequency + ' Digest', - msg=email_body - ) - except Exception, e: - webnotes.msgprint('There was a problem in sending your email. Please contact support@erpnext.com') - webnotes.errprint(webnotes.getTraceback()) + sendmail(recipients=recipients, subject=(self.doc.frequency + " Digest"), + sender="ERPNext Notifications ", + msg=self.get_digest_msg()) + + def get_digest_msg(self): + """""" + self.from_date, self.to_date = self.get_from_to_date() + self.currency = webnotes.conn.get_value("Company", self.doc.company, + "default_currency") + + out = [] + for ctype in content_sequence: + if self.doc.fields.get(ctype) and hasattr(self, "get_"+ctype): + # appends [value, html] + out.append(getattr(self, "get_"+ctype)()) + + return self.get_msg_html(out) + + def get_msg_html(self, out): + with_value = "\n".join([o[1] for o in out if o[0]]) + + # seperate out no value items + no_value = [o[1] for o in out if not o[0]] + if no_value: + no_value = """

No Updates For:


""" + "\n".join(no_value) + + date = self.doc.frequency == "Daily" and formatdate(self.from_date) or \ + "%s to %s" % (formatdate(self.from_date), formatdate(self.to_date)) + + msg = """

%(digest)s

+

%(date)s

+

%(company)s

+
+ %(with_value)s + %(no_value)s""" % { + "digest": self.doc.frequency + " Digest", + "date": date, + "company": self.doc.company, + "with_value": with_value, + "no_value": no_value or "" + } + + return msg + + def get_income_year_to_date(self): + return self.get_income(webnotes.conn.get_defaults("year_start_date"), + "Income Year To Date") + + def get_bank_balance(self): + # account is of type "Bank or Cash" + accounts = dict([[a["name"], [a["account_name"], 0]] for a in self.get_accounts() + if a["account_type"]=="Bank or Cash"]) + ackeys = accounts.keys() + + for gle in self.get_gl_entries(None, self.to_date): + if gle["account"] in ackeys: + accounts[gle["account"]][1] += gle["debit"] - gle["credit"] + + # build html + out = self.get_html("Bank/Cash Balance", "", "") + for ac in ackeys: + out += "\n" + self.get_html(accounts[ac][0], self.currency, + fmt_money(accounts[ac][1]), style="margin-left: 17px") + return sum((accounts[ac][1] for ac in ackeys)), out + + def get_income(self, from_date=None, label=None): + # account is PL Account and Credit type account + accounts = [a["name"] for a in self.get_accounts() + if a["is_pl_account"]=="Yes" and a["debit_or_credit"]=="Credit"] + + income = 0 + for gle in self.get_gl_entries(from_date or self.from_date, self.to_date): + if gle["account"] in accounts: + income += gle["credit"] - gle["debit"] + + return income, self.get_html(label or "Income", self.currency, fmt_money(income)) + + def get_expenses_booked(self): + # account is PL Account and Debit type account + accounts = [a["name"] for a in self.get_accounts() + if a["is_pl_account"]=="Yes" and a["debit_or_credit"]=="Debit"] + + expense = 0 + for gle in self.get_gl_entries(self.from_date, self.to_date): + if gle["account"] in accounts: + expense += gle["debit"] - gle["credit"] + + return expense, self.get_html("Expenses", self.currency, fmt_money(expense)) + + def get_collections(self): + return self.get_party_total("Customer", "credit", "Collections") + + def get_payments(self): + return self.get_party_total("Supplier", "debit", "Payments") + + def get_party_total(self, party_type, gle_field, label): + import re + # account is of master_type Customer or Supplier + accounts = [a["name"] for a in self.get_accounts() + if a["master_type"]==party_type] + # account is "Bank or Cash" + bc_accounts = [a["name"] for a in self.get_accounts() + if a["account_type"]=="Bank or Cash"] + bc_regex = re.compile("(%s)" % "|".join(bc_accounts)) + + total = 0 + for gle in self.get_gl_entries(self.from_date, self.to_date): + # check that its made against a bank or cash account + if gle["account"] in accounts and gle["against"] and \ + bc_regex.findall(gle["against"]): + total += gle[gle_field] + + return total, self.get_html(label, self.currency, fmt_money(total)) + + def get_invoiced_amount(self): + # aka receivables + return self.get_booked_total("Customer", "debit", "Receivables") + + def get_payables(self): + return self.get_booked_total("Supplier", "credit", "Payables") + + def get_booked_total(self, party_type, gle_field, label): + # account is of master_type Customer or Supplier + accounts = [a["name"] for a in self.get_accounts() + if a["master_type"]==party_type] + + total = 0 + for gle in self.get_gl_entries(self.from_date, self.to_date): + if gle["account"] in accounts: + total += gle[gle_field] + + return total, self.get_html(label, self.currency, fmt_money(total)) + + def get_new_leads(self): + return self.get_new_count("Lead", "New Leads") + + def get_new_enquiries(self): + return self.get_new_count("Opportunity", "New Opportunities") + + def get_new_quotations(self): + return self.get_new_sum("Quotation", "New Quotations", "grand_total") + + def get_new_sales_orders(self): + return self.get_new_sum("Sales Order", "New Sales Orders", "grand_total") + + def get_new_delivery_notes(self): + return self.get_new_sum("Delivery Note", "New Delivery Notes", "grand_total") + + def get_new_purchase_requests(self): + return self.get_new_count("Purchase Request", "New Purchase Requests") + + def get_new_supplier_quotations(self): + return self.get_new_sum("Supplier Quotation", "New Supplier Quotations", + "grand_total") + + def get_new_purchase_orders(self): + return self.get_new_sum("Purchase Order", "New Purchase Orders", "grand_total") + + def get_new_purchase_receipts(self): + return self.get_new_sum("Purchase Receipt", "New Purchase Receipts", + "grand_total") + + def get_new_stock_entries(self): + return self.get_new_sum("Stock Entry", "New Stock Entries", "total_amount") + + def get_new_support_tickets(self): + return self.get_new_count("Support Ticket", "New Support Tickets", False) + + def get_new_communications(self): + return self.get_new_count("Communication", "New Communications", False) + + def get_new_projects(self): + return self.get_new_count("Project", "New Projects", False) + + def get_new_count(self, doctype, label, filter_by_company=True): + if filter_by_company: + company = """and company="%s" """ % self.doc.company + else: + company = "" + count = webnotes.conn.sql("""select count(*) from `tab%s` + where docstatus < 2 %s and + date(creation)>=%s and date(creation)<=%s""" % (doctype, company, "%s", "%s"), + (self.from_date, self.to_date)) + count = count and count[0][0] or 0 + + return count, self.get_html(label, None, count) + + def get_new_sum(self, doctype, label, sum_field): + count_sum = webnotes.conn.sql("""select count(*), sum(ifnull(`%s`, 0)) + from `tab%s` where docstatus < 2 and company = %s and + date(creation)>=%s and date(creation)<=%s""" % (sum_field, doctype, "%s", + "%s", "%s"), (self.doc.company, self.from_date, self.to_date)) + count, total = count_sum and count_sum[0] or (0, 0) + + return count, self.get_html(label, self.currency, + "%s - (%s)" % (fmt_money(total), cstr(count))) + + def get_html(self, label, currency, value, style=None): + """get html output""" + return """

+ %(label)s: + + %(currency)s%(value)s +

""" % { + "style": style or "", + "label": label, + "currency": currency and (currency+" ") or "", + "value": value + } + + def get_gl_entries(self, from_date=None, to_date=None): + """get valid GL Entries filtered by company and posting date""" + if from_date==self.from_date and to_date==self.to_date and \ + hasattr(self, "gl_entries"): + return self.gl_entries + + gl_entries = webnotes.conn.sql("""select `account`, + ifnull(credit, 0) as credit, ifnull(debit, 0) as debit, `against` + from `tabGL Entry` + where company=%s and ifnull(is_cancelled, "No")="No" and + posting_date <= %s %s""" % ("%s", "%s", + from_date and "and posting_date>='%s'" % from_date or ""), + (self.doc.company, to_date or self.to_date), as_dict=1) + + # cache if it is the normal cases + if from_date==self.from_date and to_date==self.to_date: + self.gl_entries = gl_entries + + return gl_entries + + def get_accounts(self): + if not hasattr(self, "accounts"): + self.accounts = webnotes.conn.sql("""select name, is_pl_account, + debit_or_credit, account_type, account_name, master_type + from `tabAccount` where company=%s and docstatus < 2""", + (self.doc.company,), as_dict=1) + return self.accounts + + def get_from_to_date(self): + today = now_datetime().date() + + # decide from date based on email digest frequency + if self.doc.frequency == "Daily": + # from date, to_date is yesterday + from_date = to_date = today - timedelta(days=1) + elif self.doc.frequency == "Weekly": + # from date is the previous week's monday + from_date = today - timedelta(days=today.weekday(), weeks=1) + # to date is sunday i.e. the previous day + to_date = from_date + timedelta(days=6) + else: + # from date is the 1st day of the previous month + from_date = today - relativedelta(days=today.day-1, months=1) + # to date is the last day of the previous month + to_date = today - relativedelta(days=today.day) + + return from_date, to_date def get_next_sending(self): - import datetime - - start_date, end_date = self.get_start_end_dates() - - send_date = end_date + datetime.timedelta(days=1) - - from webnotes.utils import formatdate - str_date = formatdate(str(send_date)) - - self.doc.next_send = str_date + " at midnight" + from_date, to_date = self.get_from_to_date() + send_date = to_date + timedelta(days=1) + + if self.doc.frequency == "Daily": + next_send_date = send_date + timedelta(days=1) + elif self.doc.frequency == "Weekly": + next_send_date = send_date + timedelta(weeks=1) + else: + next_send_date = send_date + relativedelta(months=1) + self.doc.next_send = formatdate(next_send_date) + " at midnight" + return send_date - - + def onload(self): - """ - - """ self.get_next_sending() - - def get_standard_body(self, result): - """ - Generate email body depending on the result - """ - from webnotes.utils import fmt_money - from webnotes.model.doc import Document - company = Document('Company', self.doc.company) - currency = company.default_currency - - def table(args): - table_body = "" - - if isinstance(args['body'], basestring): - return """

%(head)s: %(body)s

""" % args - else: - return ("""

%(head)s:

""" % args) +\ - "".join(map(lambda b: "

%s

" % b, args['body'])) - - - currency_amount_str = "%s %s" - - body_dict = { - - 'invoiced_amount': { - 'table': result.get('invoiced_amount') and \ - table({ - 'head': 'Invoiced Amount', - 'body': currency_amount_str \ - % (currency, fmt_money(result['invoiced_amount'].get('debit'))) - }), - 'idx': 300, - 'value': result.get('invoiced_amount') and result['invoiced_amount'].get('debit') - }, - - 'payables': { - 'table': result.get('payables') and \ - table({ - 'head': 'Payables', - 'body': currency_amount_str \ - % (currency, fmt_money(result['payables'].get('credit'))) - }), - 'idx': 200, - 'value': result.get('payables') and result['payables'].get('credit') - }, - - 'collections': { - 'table': result.get('collections') and \ - table({ - 'head': 'Collections', - 'body': currency_amount_str \ - % (currency, fmt_money(result['collections'].get('credit'))) - }), - 'idx': 301, - 'value': result.get('collections') and result['collections'].get('credit') - }, - - 'payments': { - 'table': result.get('payments') and \ - table({ - 'head': 'Payments', - 'body': currency_amount_str \ - % (currency, fmt_money(result['payments'].get('debit'))) - }), - 'idx': 201, - 'value': result.get('payments') and result['payments'].get('debit') - }, - - 'income': { - 'table': result.get('income') and \ - table({ - 'head': 'Income', - 'body': currency_amount_str \ - % (currency, fmt_money(result['income'].get('value'))) - }), - 'idx': 302, - 'value': result.get('income') and result['income'].get('value') - }, - - 'income_year_to_date': { - 'table': result.get('income_year_to_date') and \ - table({ - 'head': 'Income Year To Date', - 'body': currency_amount_str \ - % (currency, fmt_money(result['income_year_to_date'].get('value'))) - }), - 'idx': 303, - 'value': result.get('income_year_to_date') and \ - result['income_year_to_date'].get('value') - }, - - 'expenses_booked': { - 'table': result.get('expenses_booked') and \ - table({ - 'head': 'Expenses Booked', - 'body': currency_amount_str \ - % (currency, fmt_money(result['expenses_booked'].get('value'))) - }), - 'idx': 202, - 'value': result.get('expenses_booked') and result['expenses_booked'].get('value') - }, - - 'bank_balance': { - 'table': result.get('bank_balance') and \ - table({ - 'head': 'Bank / Cash Balance', - 'body': [(bank['name'] + ": " \ - + currency_amount_str % \ - (currency, fmt_money(bank.get('value'))) + '') - for bank in (isinstance(result['bank_balance'], list) and \ - result['bank_balance'] or \ - [result['bank_balance']]) - ] - }), - 'idx': 0, - 'value': 0.1 - }, - - 'new_leads': { - 'table': result.get('new_leads') and \ - table({ - 'head': 'New Leads', - 'body': '%s' % result['new_leads'].get('count') - }), - 'idx': 100, - 'value': result.get('new_leads') and result['new_leads'].get('count') - }, - - 'new_enquiries': { - 'table': result.get('new_enquiries') and \ - table({ - 'head': 'New Enquiries', - 'body': '%s' % result['new_enquiries'].get('count') - }), - 'idx': 101, - 'value': result.get('new_enquiries') and result['new_enquiries'].get('count') - }, - - 'new_quotations': { - 'table': result.get('new_quotations') and \ - table({ - 'head': 'New Quotations', - 'body': '%s' % result['new_quotations'].get('count') - }), - 'idx': 102, - 'value': result.get('new_quotations') and result['new_quotations'].get('count') - }, - - 'new_sales_orders': { - 'table': result.get('new_sales_orders') and \ - table({ - 'head': 'New Sales Orders', - 'body': '%s' % result['new_sales_orders'].get('count') - }), - 'idx': 103, - 'value': result.get('new_sales_orders') and result['new_sales_orders'].get('count') - }, - - 'new_purchase_orders': { - 'table': result.get('new_purchase_orders') and \ - table({ - 'head': 'New Purchase Orders', - 'body': '%s' % result['new_purchase_orders'].get('count') - }), - 'idx': 104, - 'value': result.get('new_purchase_orders') and \ - result['new_purchase_orders'].get('count') - }, - - 'new_transactions': { - 'table': result.get('new_transactions') and \ - table({ - 'head': 'New Transactions', - 'body': '%s' % result['new_transactions'].get('count') - }), - 'idx': 105, - 'value': result.get('new_transactions') and result['new_transactions'].get('count') - } - - #'stock_below_rl': - } - - table_list = [] - - # Sort these keys depending on idx value - bd_keys = sorted(body_dict, key=lambda x: \ - (-webnotes.utils.flt(body_dict[x]['value']), body_dict[x]['idx'])) - - new_section = False - - def set_new_section(new_section): - if not new_section: - table_list.append("

No Updates For:


") - new_section = True - return new_section - - for k in bd_keys: - if self.doc.fields[k]: - if k in result: - if not body_dict[k].get('value') and not new_section: - new_section = set_new_section(new_section) - table_list.append(body_dict[k]['table']) - elif k in ['collections', 'payments']: - new_section = set_new_section(new_section) - table_list.append(\ - "

[" + \ - k.capitalize() + \ - "]
Missing: Account of type 'Bank or Cash'\ -

") - elif k=='bank_balance': - new_section = set_new_section(new_section) - table_list.append(\ - "

[" + \ - "Bank Balance" + \ - "]
Alert: GL Entry not found for Account of type 'Bank or Cash'\ -

") - - - from webnotes.utils import formatdate - start_date, end_date = self.get_start_end_dates() - digest_daterange = self.doc.frequency=='Daily' \ - and formatdate(str(start_date)) \ - or (formatdate(str(start_date)) + " to " + (formatdate(str(end_date)))) - - email_body = """ -

%s

-

%s

-

%s

-
- """ \ - % ((self.doc.frequency + " Digest"), \ - digest_daterange, self.doc.company) \ - + "".join(table_list) + """\ -

- """ - - return email_body - - def send(): - """ - - """ - edigest_list = webnotes.conn.sql(""" - SELECT name FROM `tabEmail Digest` - WHERE enabled=1 and docstatus<2 - """, as_list=1) - from webnotes.model.code import get_obj - from webnotes.utils import now_datetime - now_date = now_datetime().date() - for ed in edigest_list: - if ed[0]: - ed_obj = get_obj('Email Digest', ed[0]) - ed_obj.sending = True - send_date = ed_obj.get_next_sending() - #webnotes.msgprint([ed[0], now_date, send_date]) - - if (now_date == send_date): - ed_obj.send() + for ed in webnotes.conn.sql("""select name from `tabEmail Digest` + where enabled=1 and docstatus<2""", as_list=1): + ed_obj = get_obj('Email Digest', ed[0]) + if (now_date == ed_obj.get_next_sending()): + ed_obj.send() diff --git a/erpnext/setup/doctype/email_digest/email_digest.txt b/erpnext/setup/doctype/email_digest/email_digest.txt index 7aa39ca4e9..9de8c2842f 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.txt +++ b/erpnext/setup/doctype/email_digest/email_digest.txt @@ -3,11 +3,11 @@ # These values are common in all dictionaries { - 'creation': '2012-07-03 13:30:54', - 'docstatus': 0, - 'modified': '2012-07-12 16:29:08', - 'modified_by': u'Administrator', - 'owner': u'Administrator' + u'creation': '2012-07-12 23:29:44', + u'docstatus': 0, + u'modified': '2012-09-15 19:34:37', + u'modified_by': u'Administrator', + u'owner': u'Administrator' }, # These values are common for all DocType @@ -16,10 +16,10 @@ 'autoname': u'Prompt', 'colour': u'White:FFF', 'description': u'Send regular summary reports via Email.', - 'doctype': 'DocType', + u'doctype': u'DocType', 'document_type': u'System', 'module': u'Setup', - 'name': '__common__', + u'name': u'__common__', 'section_style': u'Simple', 'show_in_menu': 0, 'version': 1 @@ -27,8 +27,8 @@ # These values are common for all DocField { - 'doctype': u'DocField', - 'name': '__common__', + u'doctype': u'DocField', + u'name': u'__common__', 'parent': u'Email Digest', 'parentfield': u'fields', 'parenttype': u'DocType' @@ -36,8 +36,8 @@ # These values are common for all DocPerm { - 'doctype': u'DocPerm', - 'name': '__common__', + u'doctype': u'DocPerm', + u'name': u'__common__', 'parent': u'Email Digest', 'parentfield': u'permissions', 'parenttype': u'DocType', @@ -47,28 +47,13 @@ # DocType, Email Digest { - 'doctype': 'DocType', - 'name': u'Email Digest' - }, - - # DocPerm - { - 'cancel': 1, - 'create': 1, - 'doctype': u'DocPerm', - 'permlevel': 0, - 'write': 1 - }, - - # DocPerm - { - 'doctype': u'DocPerm', - 'permlevel': 1 + u'doctype': u'DocType', + u'name': u'Email Digest' }, # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'settings', 'fieldtype': u'Section Break', 'label': u'Email Digest Settings', @@ -77,7 +62,7 @@ # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'column_break0', 'fieldtype': u'Column Break', 'permlevel': 0 @@ -85,7 +70,7 @@ # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'enabled', 'fieldtype': u'Check', 'label': u'Enabled', @@ -94,7 +79,7 @@ # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'company', 'fieldtype': u'Select', 'label': u'For Company', @@ -106,11 +91,11 @@ # DocField { 'allow_on_submit': 0, - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'frequency', 'fieldtype': u'Select', 'label': u'How frequently?', - 'options': u'\nDaily\nWeekly\nMonthly', + 'options': u'Daily\nWeekly\nMonthly', 'permlevel': 0, 'reqd': 1 }, @@ -118,7 +103,7 @@ # DocField { 'depends_on': u'eval:doc.enabled', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'next_send', 'fieldtype': u'Data', 'label': u'Next email will be sent on:', @@ -127,19 +112,7 @@ # DocField { - 'default': u'1', - 'doctype': u'DocField', - 'fieldname': u'use_standard', - 'fieldtype': u'Check', - 'hidden': 1, - 'label': u'Use standard?', - 'permlevel': 0, - 'search_index': 0 - }, - - # DocField - { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'column_break1', 'fieldtype': u'Column Break', 'permlevel': 0 @@ -148,7 +121,7 @@ # DocField { 'description': u'Note: Email will not be sent to disabled users', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'recipient_list', 'fieldtype': u'Text', 'label': u'Recipients', @@ -158,7 +131,7 @@ # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'addremove_recipients', 'fieldtype': u'Button', 'label': u'Add/Remove Recipients', @@ -169,9 +142,8 @@ # DocField { 'colour': u'White:FFF', - 'depends_on': u'eval:doc.use_standard', 'description': u'Check all the items below that you want to send in this digest.', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'select_digest_content', 'fieldtype': u'Section Break', 'label': u'Select Digest Content', @@ -180,8 +152,8 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + 'colour': u'White:FFF', + u'doctype': u'DocField', 'fieldname': u'new_leads', 'fieldtype': u'Check', 'label': u'New Leads', @@ -190,8 +162,7 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'new_enquiries', 'fieldtype': u'Check', 'label': u'New Enquiries', @@ -200,8 +171,7 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'new_quotations', 'fieldtype': u'Check', 'label': u'New Quotations', @@ -210,8 +180,7 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'new_sales_orders', 'fieldtype': u'Check', 'label': u'New Sales Orders', @@ -220,8 +189,34 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', + 'fieldname': u'new_delivery_notes', + 'fieldtype': u'Check', + 'label': u'New Delivery Notes', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'new_purchase_requests', + 'fieldtype': u'Check', + 'label': u'New Purchase Requests', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'new_supplier_quotations', + 'fieldtype': u'Check', + 'label': u'New Supplier Quotations', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', 'fieldname': u'new_purchase_orders', 'fieldtype': u'Check', 'label': u'New Purchase Orders', @@ -230,27 +225,52 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'new_transactions', + u'doctype': u'DocField', + 'fieldname': u'new_purchase_receipts', 'fieldtype': u'Check', - 'label': u'New Transactions', + 'label': u'New Purchase Receipts', 'permlevel': 0 }, # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'payables', + u'doctype': u'DocField', + 'fieldname': u'new_stock_entries', 'fieldtype': u'Check', - 'label': u'Payables', + 'label': u'New Stock Entries', 'permlevel': 0 }, # DocField { - 'doctype': u'DocField', + u'doctype': u'DocField', + 'fieldname': u'new_support_tickets', + 'fieldtype': u'Check', + 'label': u'New Support Tickets', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'new_communications', + 'fieldtype': u'Check', + 'label': u'New Communications', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'new_projects', + 'fieldtype': u'Check', + 'label': u'New Projects', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', 'fieldname': u'cb1', 'fieldtype': u'Column Break', 'permlevel': 0 @@ -258,58 +278,7 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'payments', - 'fieldtype': u'Check', - 'label': u'Payments', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'expenses_booked', - 'fieldtype': u'Check', - 'label': u'Expenses Booked', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'invoiced_amount', - 'fieldtype': u'Check', - 'label': u'Invoiced Amount (Receivables)', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'collections', - 'fieldtype': u'Check', - 'label': u'Collections', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', - 'fieldname': u'income', - 'fieldtype': u'Check', - 'label': u'Income', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'income_year_to_date', 'fieldtype': u'Check', 'label': u'Income Year to Date', @@ -318,8 +287,7 @@ # DocField { - 'depends_on': u'eval:doc.use_standard', - 'doctype': u'DocField', + u'doctype': u'DocField', 'fieldname': u'bank_balance', 'fieldtype': u'Check', 'label': u'Bank Balance', @@ -328,11 +296,70 @@ # DocField { - 'doctype': u'DocField', - 'fieldname': u'stock_below_rl', + u'doctype': u'DocField', + 'fieldname': u'income', 'fieldtype': u'Check', - 'hidden': 1, - 'label': u'Stock Items below re-order level', + 'label': u'Income', 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'expenses_booked', + 'fieldtype': u'Check', + 'label': u'Expenses Booked', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'collections', + 'fieldtype': u'Check', + 'label': u'Collections', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'payments', + 'fieldtype': u'Check', + 'label': u'Payments', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'invoiced_amount', + 'fieldtype': u'Check', + 'label': u'Receivables', + 'permlevel': 0 + }, + + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'payables', + 'fieldtype': u'Check', + 'label': u'Payables', + 'permlevel': 0 + }, + + # DocPerm + { + 'cancel': 1, + 'create': 1, + u'doctype': u'DocPerm', + 'permlevel': 0, + 'write': 1 + }, + + # DocPerm + { + u'doctype': u'DocPerm', + 'permlevel': 1 } ] \ No newline at end of file