From 633edbef46043a2a8645d693d812ddd0b8aa9eb7 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 8 Dec 2011 19:34:01 +0530 Subject: [PATCH] Email Digest - ready to be deployed --- erpnext/patches/deploy_email_digest.py | 75 +++++ .../doctype/email_digest/email_digest.js | 20 +- .../doctype/email_digest/email_digest.py | 302 ++++++++++++++++-- .../doctype/email_digest/email_digest.txt | 161 +++++----- erpnext/setup/page/setup/setup.js | 3 +- 5 files changed, 450 insertions(+), 111 deletions(-) create mode 100644 erpnext/patches/deploy_email_digest.py diff --git a/erpnext/patches/deploy_email_digest.py b/erpnext/patches/deploy_email_digest.py new file mode 100644 index 00000000000..1dd97e32498 --- /dev/null +++ b/erpnext/patches/deploy_email_digest.py @@ -0,0 +1,75 @@ +import webnotes + +def execute(): + """ + * Reload email_digest doctype + * Create default email digest + """ + from webnotes.modules.module_manager import reload_doc + + # Minor fix in print_format doctype + #reload_doc('core', 'doctype', 'print_format') + + reload_doc('setup', 'doctype', 'email_digest') + + global create_default_email_digest + create_default_email_digest() + + +def create_default_email_digest(): + """ + * Weekly Digest + * For all companies + * Recipients: System Managers + * Full content + * Disabled by default + """ + from webnotes.model.doc import Document + companies_list = webnotes.conn.sql("SELECT company_name FROM `tabCompany`", as_list=1) + global get_system_managers + system_managers = get_system_managers() + for company in companies_list: + if company and company[0]: + edigest = Document('Email Digest') + edigest.name = "Default Weekly Digest - " + company[0] + edigest.company = company[0] + edigest.frequency = 'Weekly' + edigest.recipient_list = system_managers + edigest.new_leads = 1 + edigest.new_enquiries = 1 + edigest.new_quotations = 1 + edigest.new_sales_orders = 1 + edigest.new_purchase_orders = 1 + edigest.new_transactions = 1 + edigest.payables = 1 + edigest.payments = 1 + edigest.expenses_booked = 1 + edigest.invoiced_amount = 1 + edigest.collections = 1 + edigest.income = 1 + edigest.bank_balance = 1 + exists = webnotes.conn.sql("""\ + SELECT name FROM `tabEmail Digest` + WHERE name = %s""", edigest.name) + if (exists and exists[0]) and exists[0][0]: + continue + else: + edigest.save(1) + + +def get_system_managers(): + """ + Returns a string of system managers' email addresses separated by \n + """ + system_managers_list = webnotes.conn.sql("""\ + SELECT DISTINCT p.name + FROM tabUserRole ur, tabProfile p + WHERE + ur.parent = p.name AND + ur.role='System Manager' AND + p.docstatus<2 AND + p.enabled=1 AND + p.name not in ('Administrator', 'Guest')""", as_list=1) + + return "\n".join([sysman[0] for sysman in system_managers_list]) + diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index 4d533ce10cc..98abbb131ec 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -1,10 +1,18 @@ cur_frm.cscript.refresh = function(doc, dt, dn) { - cur_frm.add_custom_button('Execute Now', function() { + cur_frm.add_custom_button('View Now', function() { $c_obj(make_doclist(dt, dn), 'get', '', function(r, rt) { if(r.exc) { msgprint(r.exc); } else { - console.log(arguments); + //console.log(arguments); + var d = new wn.widgets.Dialog({ + title: 'Email Digest: ' + dn, + width: 800 + }); + + $a(d.body, 'div', '', '', r['message'][1]); + + d.show(); } }); }, 1); @@ -13,7 +21,8 @@ cur_frm.cscript.refresh = function(doc, dt, dn) { if(r.exc) { msgprint(r.exc); } else { - console.log(arguments); + //console.log(arguments); + msgprint('Message Sent'); } }); }, 1); @@ -42,6 +51,9 @@ cur_frm.cscript['Add Recipients'] = function(doc, dt, dn) { check.checked = 1; add_or_update = 'Update'; } + if(v.enabled==0) { + v.name = "" + v.name + " (disabled user)" + } var profile = $a($td(tab, i+1, 1), 'span', '', '', v.name); //profile.onclick = function() { check.checked = !check.checked; } }); @@ -72,7 +84,7 @@ cur_frm.cscript.add_to_rec_list = function(doc, tab, length) { } } doc.recipient_list = rec_list.join('\n'); - console.log(doc.recipient_list); + //console.log(doc.recipient_list); cur_frm.rec_dialog.hide(); cur_frm.refresh_fields(); } diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 35dcf5ba63a..e4e12584e28 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -111,7 +111,7 @@ class DocType: for query in query_dict.keys(): if self.doc.fields[query]: #webnotes.msgprint(query) - res = webnotes.conn.sql(query_dict[query], as_dict=1, debug=1) + res = webnotes.conn.sql(query_dict[query], as_dict=1) if query == 'income': for r in res: r['value'] = float(r['credit'] - r['debit']) @@ -120,7 +120,7 @@ class DocType: r['value'] = float(r['debit'] - r['credit']) #webnotes.msgprint(query) #webnotes.msgprint(res) - result[query] = (res and res[0]) and res[0] or None + result[query] = (res and len(res)==1) and res[0] or (res and res or None) #webnotes.msgprint(result) return result @@ -180,7 +180,7 @@ class DocType: elif args['type'] == 'bank_balance': query = """ SELECT - ac.name AS 'name', + 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 @@ -191,7 +191,7 @@ class DocType: ac.account_type = 'Bank or Cash' AND %(end_date_condition)s GROUP BY - ac.name""" % args + ac.account_name""" % args return query @@ -251,11 +251,11 @@ class DocType: elif self.doc.frequency == 'Weekly': if self.sending: - start_date = today - timedelta(weeks=1) - end_date = today - timedelta(days=1) + 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(weeks=1) + end_date = start_date + timedelta(days=6) else: import calendar @@ -316,8 +316,8 @@ class DocType: * Prepare Email Body from Print Format """ result, email_body = self.execute_queries() - webnotes.msgprint(result) - webnotes.msgprint(email_body) + #webnotes.msgprint(result) + #webnotes.msgprint(email_body) return result, email_body @@ -327,7 +327,7 @@ class DocType: * If standard==0, execute python code in custom_code field """ result = {} - if self.doc.use_standard==1: + if int(self.doc.use_standard)==1: result = self.get_standard_data() email_body = self.get_standard_body(result) else: @@ -338,17 +338,6 @@ class DocType: return result, email_body - def get_standard_body(self, result): - """ - Generate email body depending on the result - """ - return """ -
- Invoiced Amount: %(invoiced_amount)s
- Payables: %(payables)s
-
""" % result - - def execute_custom_code(self, doc): """ Execute custom python code @@ -363,14 +352,22 @@ class DocType: """ self.sending = True result, email_body = self.get() - # TODO: before sending, check if user is disabled or not + 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=self.doc.recipient_list.split("\n"), - sender='anand@erpnext.com', + recipients=recipient_list, + sender='notifications+email_digest@erpnext.com', reply_to='support@erpnext.com', - subject='Digest', + subject=self.doc.frequency + ' Digest', msg=email_body, from_defs=1 ) @@ -389,9 +386,11 @@ class DocType: 'event': 'setup.doctype.email_digest.email_digest.send' } from webnotes.utils.scheduler import Scheduler + print "before scheduler" sch = Scheduler() sch.connect() + if self.doc.enabled == 1: # Create scheduler entry res = sch.conn.sql(""" @@ -412,6 +411,7 @@ class DocType: else: # delete scheduler entry sch.clear(args['db_name'], args['event']) + print "after on update" def get_next_sending(self): @@ -448,7 +448,7 @@ class DocType: str_date = formatdate(str(res['app_dt'].date())) str_time = res['app_dt'].time().strftime('%I:%M') - self.doc.next_send = str_date + " at " + str_time + self.doc.next_send = str_date + " at about " + str_time return res @@ -478,9 +478,257 @@ class DocType: 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): + if type(args['body']) == type(''): + table_body = """\ + + """ + \ + args['body'] + \ + """\ + + """ + + elif type(args['body'] == type([])): + body_rows = [] + for rows in args['body']: + for r in rows: + body_rows.append("""\ + + """ \ + + r + """\ + + """) + + body_rows.append("
") + + table_body = "" + "".join(body_rows) + "" + + table_head = """\ + + """ \ + + args['head'] + """\ + + """ + + return "" \ + + table_head \ + + table_body \ + + "
" + + currency_amount_str = "%s %s" + + body_dict = { + + 'invoiced_amount': { + 'table': 'invoiced_amount' in result and table({ + 'head': 'Invoiced Amount', + 'body': currency_amount_str \ + % (currency, fmt_money(result['invoiced_amount']['debit'])) + }), + 'idx': 300 + }, + + 'payables': { + 'table': 'payables' in result and table({ + 'head': 'Payables', + 'body': currency_amount_str \ + % (currency, fmt_money(result['payables']['credit'])) + }), + 'idx': 200 + }, + + 'collections': { + 'table': 'collections' in result and table({ + 'head': 'Collections', + 'body': currency_amount_str \ + % (currency, fmt_money(result['collections']['credit'])) + }), + 'idx': 301 + }, + + 'payments': { + 'table': 'payments' in result and table({ + 'head': 'Payments', + 'body': currency_amount_str \ + % (currency, fmt_money(result['payments']['debit'])) + }), + 'idx': 201 + }, + + 'income': { + 'table': 'income' in result and table({ + 'head': 'Income', + 'body': currency_amount_str \ + % (currency, fmt_money(result['income']['value'])) + }), + 'idx': 302 + }, + + 'expenses_booked': { + 'table': 'expenses_booked' in result and table({ + 'head': 'Expenses Booked', + 'body': currency_amount_str \ + % (currency, fmt_money(result['expenses_booked']['value'])) + }), + 'idx': 202 + }, + + 'bank_balance': { + 'table': 'bank_balance' in result and table({ + 'head': 'Bank Balance', + 'body': [ + [ + "%s" % bank['name'], + currency_amount_str % (currency, fmt_money(bank['value'])) + ] for bank in result['bank_balance'] + ] + }), + 'idx': 400 + }, + + 'new_leads': { + 'table': 'new_leads' in result and table({ + 'head': 'New Leads', + 'body': '%s' % result['new_leads']['count'] + }), + 'idx': 100 + }, + + 'new_enquiries': { + 'table': 'new_enquiries' in result and table({ + 'head': 'New Enquiries', + 'body': '%s' % result['new_enquiries']['count'] + }), + 'idx': 101 + }, + + 'new_quotations': { + 'table': 'new_quotations' in result and table({ + 'head': 'New Quotations', + 'body': '%s' % result['new_quotations']['count'] + }), + 'idx': 102 + }, + + 'new_sales_orders': { + 'table': 'new_sales_orders' in result and table({ + 'head': 'New Sales Orders', + 'body': '%s' % result['new_sales_orders']['count'] + }), + 'idx': 103 + }, + + 'new_purchase_orders': { + 'table': 'new_purchase_orders' in result and table({ + 'head': 'New Purchase Orders', + 'body': '%s' % result['new_purchase_orders']['count'] + }), + 'idx': 104 + }, + + 'new_transactions': { + 'table': 'new_transactions' in result and table({ + 'head': 'New Transactions', + 'body': '%s' % result['new_transactions']['count'] + }), + 'idx': 105 + } + + #'stock_below_rl': + } + + table_list = [] + + # Sort these keys depending on idx value + bd_keys = sorted(body_dict, key=lambda x: body_dict[x]['idx']) + + + for k in bd_keys: + if self.doc.fields[k]: + table_list.append(body_dict[k]['table']) + + result = [] + + i = 0 + result = [] + op_len = len(table_list) + while(True): + if i>=op_len: + break + elif (op_len - i) == 1: + result.append("""\ + + %s + + """ % (table_list[i])) + else: + result.append("""\ + + %s + %s + """ % (table_list[i], table_list[i+1])) + + i = i + 2 + + 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(result) + """\ +

+
""" + + return email_body + + def send(): """ """ - pass + edigest_list = webnotes.conn.sql(""" + SELECT name FROM `tabEmail Digest` + WHERE enabled=1 + """, as_list=1) + from webnotes.model.code import get_obj + from datetime import datetime, timedelta + now = datetime.now() + now_date = now.date() + now_time = (now + timedelta(hours=2)).time() + + for ed in edigest_list: + if ed[0]: + ed_obj = get_obj('Email Digest', ed[0]) + ed_obj.sending = True + dt_dict = ed_obj.get_next_sending() + send_date = dt_dict['server_dt'].date() + send_time = dt_dict['server_dt'].time() + + if (now_date == send_date) and (send_time <= now_time): + #webnotes.msgprint('sending ' + ed_obj.doc.name) + ed_obj.send() + #else: + # webnotes.msgprint('not sending ' + ed_obj.doc.name) diff --git a/erpnext/setup/doctype/email_digest/email_digest.txt b/erpnext/setup/doctype/email_digest/email_digest.txt index af50c744072..82bd7bc98ba 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.txt +++ b/erpnext/setup/doctype/email_digest/email_digest.txt @@ -5,14 +5,14 @@ { 'creation': '2011-11-28 13:11:56', 'docstatus': 0, - 'modified': '2011-12-06 18:49:23', + 'modified': '2011-12-08 19:21:26', 'modified_by': 'Administrator', 'owner': 'Administrator' }, # These values are common for all DocType { - '_last_update': '1323177411', + '_last_update': '1323352149', 'autoname': 'Prompt', 'colour': 'White:FFF', 'doctype': 'DocType', @@ -21,7 +21,7 @@ 'name': '__common__', 'section_style': 'Simple', 'show_in_menu': 0, - 'version': 66 + 'version': 78 }, # These values are common for all DocField @@ -52,6 +52,7 @@ # DocPerm { + 'cancel': 1, 'create': 1, 'doctype': 'DocPerm', 'permlevel': 0, @@ -113,6 +114,7 @@ # DocField { + 'depends_on': 'eval:doc.enabled', 'doctype': 'DocField', 'fieldname': 'next_send', 'fieldtype': 'Data', @@ -128,7 +130,8 @@ 'fieldtype': 'Check', 'hidden': 1, 'label': 'Use standard?', - 'permlevel': 0 + 'permlevel': 0, + 'search_index': 0 }, # DocField @@ -149,6 +152,7 @@ # DocField { + 'description': 'Note: Email will not be sent to disabled users', 'doctype': 'DocField', 'fieldname': 'recipient_list', 'fieldtype': 'Text', @@ -166,76 +170,6 @@ 'permlevel': 0 }, - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'invoiced_amount', - 'fieldtype': 'Check', - 'label': 'Invoiced Amount (Receivables)', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'payables', - 'fieldtype': 'Check', - 'label': 'Payables', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'income', - 'fieldtype': 'Check', - 'label': 'Income', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'expenses_booked', - 'fieldtype': 'Check', - 'label': 'Expenses Booked', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'collections', - 'fieldtype': 'Check', - 'label': 'Collections', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'payments', - 'fieldtype': 'Check', - 'label': 'Payments', - 'permlevel': 0 - }, - - # DocField - { - 'depends_on': 'eval:doc.use_standard', - 'doctype': 'DocField', - 'fieldname': 'bank_balance', - 'fieldtype': 'Check', - 'label': 'Bank Balance', - 'permlevel': 0 - }, - # DocField { 'depends_on': 'eval:doc.use_standard', @@ -290,9 +224,9 @@ { 'depends_on': 'eval:doc.use_standard', 'doctype': 'DocField', - 'fieldname': 'stock_below_rl', + 'fieldname': 'new_transactions', 'fieldtype': 'Check', - 'label': 'Stock Items below re-order level', + 'label': 'New Transactions', 'permlevel': 0 }, @@ -300,9 +234,79 @@ { 'depends_on': 'eval:doc.use_standard', 'doctype': 'DocField', - 'fieldname': 'new_transactions', + 'fieldname': 'payables', 'fieldtype': 'Check', - 'label': 'New Transactions', + 'label': 'Payables', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'payments', + 'fieldtype': 'Check', + 'label': 'Payments', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'expenses_booked', + 'fieldtype': 'Check', + 'label': 'Expenses Booked', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'invoiced_amount', + 'fieldtype': 'Check', + 'label': 'Invoiced Amount (Receivables)', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'collections', + 'fieldtype': 'Check', + 'label': 'Collections', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'income', + 'fieldtype': 'Check', + 'label': 'Income', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'bank_balance', + 'fieldtype': 'Check', + 'label': 'Bank Balance', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'stock_below_rl', + 'fieldtype': 'Check', + 'hidden': 1, + 'label': 'Stock Items below re-order level', 'permlevel': 0 }, @@ -317,7 +321,6 @@ # DocField { - 'default': '\n', 'depends_on': 'eval:!doc.use_standard', 'doctype': 'DocField', 'fieldname': 'custom_code', diff --git a/erpnext/setup/page/setup/setup.js b/erpnext/setup/page/setup/setup.js index 92ba9db025c..f536c701c64 100644 --- a/erpnext/setup/page/setup/setup.js +++ b/erpnext/setup/page/setup/setup.js @@ -161,6 +161,7 @@ SetupData = function(cnty){ ['Custom Field',1,'Custom Field','dt'+NEWLINE+'label'+NEWLINE+'fieldtype'+NEWLINE+'options','Add and manage custom fields on forms'], ['Email Settings',3,'Email Settings','','Outgoing email server and address'], ['Notification Settings',3,'Notification Control','','Automatic emails set at selected events'], + ['Email Digest', 1, 'Email Digest', '', 'Schedule Daily / Weekly / Monthly Summary e-mails'], ['Company',1,'Company','id'+NEWLINE+'is_active'+NEWLINE+'email','Manage list of companies'], ['Fiscal Year',1,'Fiscal Year','id'+NEWLINE+'company'+NEWLINE+'is_active'+NEWLINE+'year','Manage list of fiscal years'], ['Personalize',3,'Personalize','','Set your banner'], @@ -168,7 +169,7 @@ SetupData = function(cnty){ ['Import Data',2,'Import Data','','Import data from CSV files'], ['Manage Users',2,'My Company','','Add / remove users and manage their roles'], ['Web Forms',2,'Webforms','', 'Code to embed forms in yor website'], - ['Permissions Manager',2,'Permission Engine','', 'Manage all permissions from one tool (beta)'], + ['Permissions Manager',2,'Permission Engine','', 'Manage all permissions from one tool'], //['Property Setter',1,'Property Setter','', 'Customize properties of a Form (DocType) or Field'], ['Customize Form View',3,'DocLayer','', 'Customize properties of a Form (DocType) or Field'], ['Print Formats', 1, 'Print Format', '', 'Manage Print Formats'],