From 7057cbf9e57621133d438aaa2e028f2a6c2077aa Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 2 Dec 2011 15:15:38 +0530 Subject: [PATCH] Started with email digest --- .../doctype/email_digest/email_digest.css | 10 + .../doctype/email_digest/email_digest.js | 69 +++++ .../doctype/email_digest/email_digest.py | 204 ++++++++++++ .../doctype/email_digest/email_digest.txt | 290 ++++++++++++++++-- 4 files changed, 548 insertions(+), 25 deletions(-) create mode 100644 erpnext/setup/doctype/email_digest/email_digest.css create mode 100644 erpnext/setup/doctype/email_digest/email_digest.js create mode 100644 erpnext/setup/doctype/email_digest/email_digest.py diff --git a/erpnext/setup/doctype/email_digest/email_digest.css b/erpnext/setup/doctype/email_digest/email_digest.css new file mode 100644 index 0000000000..0654dec254 --- /dev/null +++ b/erpnext/setup/doctype/email_digest/email_digest.css @@ -0,0 +1,10 @@ +table.profile-list { + text-align: left; + margin: auto; + line-height: 250%; +} + +div.dialog-div { + text-align: 'center'; + width: 100%; +} diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js new file mode 100644 index 0000000000..a84072196d --- /dev/null +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -0,0 +1,69 @@ +cur_frm.cscript.refresh = function(doc, dt, dn) { + cur_frm.add_custom_button('Execute Now', function() { + $c_obj(make_doclist(dt, dn), 'get_standard_data', '', function(r, rt) { + if(r.exc) { + msgprint(r.exc); + } else { + console.log(arguments); + } + }); + }, 1); +} + +cur_frm.cscript['Add Recipients'] = function(doc, dt, dn) { + // Get profile list + $c_obj(make_doclist(dt, dn), 'get_profiles', '', function(r, rt) { + if(r.exc) { + msgprint(r.exc); + } else { + // Open a dialog and display checkboxes against email addresses + doc = locals[dt][dn]; + var d = new wn.widgets.Dialog({ + title: 'Add Recipients', + width: 400 + }); + var dialog_div = $a(d.body, 'div', 'dialog-div', '', ''); + var tab = make_table(dialog_div, r.profile_list.length+2, 2, '', ['15%', '85%']); + tab.className = 'profile-list'; + var add_or_update = 'Add'; + $.each(r.profile_list, function(i, v) { + var check = $a_input($td(tab, i+1, 0), 'checkbox'); + check.value = v.name; + if(v.checked==1) { + check.checked = 1; + add_or_update = 'Update'; + } + var profile = $a($td(tab, i+1, 1), 'span', '', '', v.name); + profile.onclick = function() { check.checked = !check.checked; } + }); + + // Display add recipients button + if(r.profile_list.length>15) { + $btn($td(tab, 0, 1), add_or_update + ' Recipients', function() { + cur_frm.cscript.add_to_rec_list(doc, tab, r.profile_list.length); + }); + } + $btn($td(tab, r.profile_list.length+1, 1), add_or_update + ' Recipients', function() { + cur_frm.cscript.add_to_rec_list(doc, tab, r.profile_list.length); + }); + + cur_frm.rec_dialog = d; + d.show(); + } + }); +} + +cur_frm.cscript.add_to_rec_list = function(doc, tab, length) { + // add checked profiles to list of recipients + var rec_list = []; + for(var i = 1; i <= length; i++) { + var input = $($td(tab, i, 0)).find('input'); + if(input.is(':checked')) { + rec_list.push(input.attr('value')); + } + } + doc.recipient_list = rec_list.join('\n'); + 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 new file mode 100644 index 0000000000..0167a151a8 --- /dev/null +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -0,0 +1,204 @@ +import webnotes + +class DocType: + def __init__(self, doc, doclist=[]): + self.doc, self.doclist = doc, doclist + + + def get_profiles(self): + """ + Get a 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) + 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 + webnotes.response['profile_list'] = profile_list + + + def get_standard_data(self): + """ + Executes standard queries + """ + res = {} + query_dict = { + + 'invoiced_amount': self.generate_gle_query({ + 'field': 'debit', + 'type': 'Customer', + }), + + 'payables': self.generate_gle_query({ + 'field': 'credit', + 'type': 'Supplier', + }), + + 'collections': self.generate_gle_query({ + 'field': 'credit', + 'type': 'Customer', + 'against': 'Bank or Cash' + }), + + 'payments': self.generate_gle_query({ + 'field': 'debit', + 'type': 'Supplier', + 'against': 'Bank or Cash' + }), + + 'income': self.generate_gle_query({ + 'debit_or_credit': 'Credit' + }), + + 'expenses_booked': self.generate_gle_query({ + 'debit_or_credit': 'Debit' + }), + + 'bank_balance': self.generate_gle_query({ + 'bank_balance': None + }), + + 'new_leads': """""", + + 'new_inquiries': """""", + + 'new_quotations': "", + + 'new_orders': "", + + 'stock_below_rl': """""", + + 'new_transactions': """""" + + } + + result = {} + + for query in query_dict.keys(): + if query_dict[query]: + webnotes.msgprint(query) + res = webnotes.conn.sql(query_dict[query], as_dict=1, debug=1) + if query == 'income': + res[0]['value'] = float(res[0]['credit'] - res[0]['debit']) + elif query == 'expenses_booked': + res[0]['value'] = float(res[0]['debit'] - res[0]['credit']) + elif query == 'bank_balance': + for r in res: + r['value'] = float(r['debit'] - r['credit']) + webnotes.msgprint(query) + webnotes.msgprint(res) + result[query] = (res and res[0]) and res[0] or None + + return result + + + def generate_gle_query(self, args): + """ + Returns generated query string + """ + start_date = '2011-11-01' + end_date = '2011-11-30' + args.update({ + 'start_date': start_date, + 'end_date': end_date, + 'company': self.doc.company, + 'select': None, + 'where': None + }) + + + self.evaluate_query_conditions(args) + + query = """ + SELECT + %(select)s, + COUNT(*) AS 'count' + FROM + `tabGL Entry` gle, + `tabAccount` ac + WHERE + gle.company = '%(company)s' AND + gle.account = ac.name AND + ac.docstatus < 2 AND + IFNULL(gle.is_cancelled, 'No') = 'No' AND + %(where)s AND + gle.posting_date <= '%(end_date)s'""" % args + + if 'group_by' in args.keys(): + query = query + args['group_by'] + + return query + + + def evaluate_query_conditions(self, args): + """ + Modify query according to type of information required based on args passed + """ + # If collections or payments + if 'against' in args.keys(): + if args['against'] == 'Bank or Cash': + bc_account_list = webnotes.conn.sql(""" + SELECT name + FROM `tabAccount` + WHERE account_type = 'Bank or Cash'""", as_list=1) + args['reg'] = '(' + '|'.join([ac[0] for ac in bc_account_list]) + ')' + args['where'] = """ + ac.master_type = '%(type)s' AND + gle.against REGEXP '%(reg)s' AND + gle.posting_date >= '%(start_date)s'""" % args + + # If income or expenses_booked + elif 'debit_or_credit' in args.keys(): + args['select'] = """ + SUM(IFNULL(gle.debit, 0)) AS 'debit', + SUM(IFNULL(gle.credit, 0)) AS 'credit'""" + + args['where'] = """ + ac.is_pl_account = 'Yes' AND + ac.debit_or_credit = '%(debit_or_credit)s' AND + gle.posting_date >= '%(start_date)s'""" % args + + elif 'bank_balance' in args.keys(): + args['select'] = "ac.name AS 'name', SUM(IFNULL(debit, 0)) AS 'debit', SUM(IFNULL(credit, 0)) AS 'credit'" + args['where'] = "ac.account_type = 'Bank or Cash'" + args['group_by'] = "GROUP BY ac.name" + + # For everything else + else: + args['where'] = """ + ac.master_type = '%(type)s' AND + gle.posting_date >= '%(start_date)s'""" % args + + if not args['select']: + args['select'] = "SUM(IFNULL(gle.%(field)s, 0)) AS '%(field)s'" % args + + + def get(self): + """ + * Execute Query + * Prepare Email Body from Print Format + """ + pass + + + def execute_queries(self): + """ + * If standard==1, execute get_standard_data + * If standard==0, execute python code in custom_code field + """ + pass + + + def send(self): + """ + * Execute get method + * Send email to recipients + """ + pass diff --git a/erpnext/setup/doctype/email_digest/email_digest.txt b/erpnext/setup/doctype/email_digest/email_digest.txt index 026caa7d6e..d6fa85a3df 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.txt +++ b/erpnext/setup/doctype/email_digest/email_digest.txt @@ -3,24 +3,25 @@ # These values are common in all dictionaries { - 'creation': '2011-07-27 14:23:09', + 'creation': '2011-11-28 13:11:56', 'docstatus': 0, - 'modified': '2011-07-27 17:32:27', + 'modified': '2011-12-02 10:58:22', 'modified_by': 'Administrator', 'owner': 'Administrator' }, # These values are common for all DocType { - '_last_update': '1311760331', + '_last_update': '1322803627', + 'autoname': 'Prompt', 'colour': 'White:FFF', 'doctype': 'DocType', - 'issingle': 1, + 'document_type': 'System', 'module': 'Setup', 'name': '__common__', 'section_style': 'Simple', 'show_in_menu': 0, - 'version': 4 + 'version': 45 }, # These values are common for all DocField @@ -29,23 +30,18 @@ 'name': '__common__', 'parent': 'Email Digest', 'parentfield': 'fields', - 'parenttype': 'DocType', - 'permlevel': 0 + 'parenttype': 'DocType' }, # These values are common for all DocPerm { - 'create': 1, 'doctype': 'DocPerm', - 'idx': 1, 'name': '__common__', 'parent': 'Email Digest', 'parentfield': 'permissions', 'parenttype': 'DocType', - 'permlevel': 0, 'read': 1, - 'role': 'Administrator', - 'write': 1 + 'role': 'System Manager' }, # DocType, Email Digest @@ -56,34 +52,278 @@ # DocPerm { - 'doctype': 'DocPerm' + 'create': 1, + 'doctype': 'DocPerm', + 'permlevel': 0, + 'write': 1 + }, + + # DocPerm + { + 'doctype': 'DocPerm', + 'permlevel': 1 }, # DocField { 'doctype': 'DocField', - 'fieldtype': 'HTML', - 'idx': 1, - 'label': 'Body' + 'fieldtype': 'Section Break', + 'label': 'Settings', + 'permlevel': 0 }, # DocField { 'doctype': 'DocField', - 'fieldname': 'content_config', + 'fieldtype': 'Column Break', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'enabled', + 'fieldtype': 'Check', + 'label': 'Enabled', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'company', + 'fieldtype': 'Select', + 'label': 'For Company', + 'options': 'link:Company', + 'permlevel': 0, + 'reqd': 1 + }, + + # DocField + { + 'allow_on_submit': 0, + 'doctype': 'DocField', + 'fieldname': 'frequency', + 'fieldtype': 'Select', + 'label': 'How frequently?', + 'options': '\nDaily\nWeekly\nMonthly', + 'permlevel': 0, + 'reqd': 1 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'send_time', + 'fieldtype': 'Select', + 'label': 'At what time?', + 'options': '\n1 AM\n2 AM\n3 AM\n4 AM\n5 AM\n6 AM\n7 AM\n8 AM\n9 AM\n10 AM\n11 AM\nNoon\n1 PM\n2 PM\n3 PM\n4 PM\n5 PM\n6 PM\n7 PM\n8 PM\n9 PM\n10 PM\n11 PM\nMidnight', + 'permlevel': 0, + 'reqd': 1 + }, + + # DocField + { + 'default': '1', + 'doctype': 'DocField', + 'fieldname': 'use_standard', + 'fieldtype': 'Check', + 'label': 'Use standard?', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'print_format', + 'fieldtype': 'Select', + 'label': 'Email Template', + 'options': "link:Print Format\ndoc_type='Email Digest'", + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldtype': 'Column Break', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldtype': 'Button', + 'label': 'Add Recipients', + 'permlevel': 0, + 'trigger': 'Client' + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'recipient_list', 'fieldtype': 'Text', - 'hidden': 1, - 'idx': 2, - 'label': 'Content Config' + 'label': 'Recipients', + 'permlevel': 1, + 'reqd': 1 }, # DocField { + 'depends_on': 'eval:doc.use_standard', 'doctype': 'DocField', - 'fieldname': 'email_config', - 'fieldtype': 'Text', - 'hidden': 1, - 'idx': 3, - 'label': 'Email Config' + 'fieldtype': 'Section Break', + 'label': 'Select Digest Content', + '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', + 'doctype': 'DocField', + 'fieldname': 'new_leads', + 'fieldtype': 'Check', + 'label': 'New Leads', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'new_inquiries', + 'fieldtype': 'Check', + 'label': 'New Inquiries', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'new_quotations', + 'fieldtype': 'Check', + 'label': 'New Quotations', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'new_orders', + 'fieldtype': 'Check', + 'label': 'New Orders', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'stock_below_rl', + 'fieldtype': 'Check', + 'label': 'Stock Items below re-order level', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'new_transactions', + 'fieldtype': 'Check', + 'label': 'New Transactions', + 'permlevel': 0 + }, + + # DocField + { + 'depends_on': 'eval:!doc.use_standard', + 'doctype': 'DocField', + 'fieldtype': 'Section Break', + 'label': 'Enter Custom Code', + 'permlevel': 0 + }, + + # DocField + { + 'default': '\n', + 'depends_on': 'eval:!doc.use_standard', + 'doctype': 'DocField', + 'fieldname': 'custom_code', + 'fieldtype': 'Code', + 'label': 'Custom Python Code', + 'permlevel': 0 } ] \ No newline at end of file