Merge branch 'master' of github.com:webnotes/erpnext into edigest
diff --git a/erpnext/patches/deploy_email_digest.py b/erpnext/patches/deploy_email_digest.py
new file mode 100644
index 0000000..1dd97e3
--- /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.css b/erpnext/setup/doctype/email_digest/email_digest.css
new file mode 100644
index 0000000..0654dec
--- /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 0000000..98abbb1
--- /dev/null
+++ b/erpnext/setup/doctype/email_digest/email_digest.js
@@ -0,0 +1,90 @@
+cur_frm.cscript.refresh = function(doc, dt, dn) {
+	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);
+				var d = new wn.widgets.Dialog({
+					title: 'Email Digest: ' + dn,
+					width: 800
+				});
+
+				$a(d.body, 'div', '', '', r['message'][1]);
+
+				d.show();
+			}
+		});	
+	}, 1);
+	cur_frm.add_custom_button('Send Now', function() {
+		$c_obj(make_doclist(dt, dn), 'send', '', function(r, rt) {
+			if(r.exc) {
+				msgprint(r.exc);
+			} else {
+				//console.log(arguments);
+				msgprint('Message Sent');
+			}
+		});	
+	}, 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';
+				}
+				if(v.enabled==0) {
+					v.name = "<span style='color: red'>" + v.name + " (disabled user)</span>"
+				}
+				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 0000000..e4e1258
--- /dev/null
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -0,0 +1,734 @@
+import webnotes
+
+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
+		"""
+		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({
+				'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'
+			}),
+
+			'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': 'Enquiry'
+			}),
+
+			'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': 'Receivable Voucher',
+				'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]:
+				#webnotes.msgprint(query)
+				res = webnotes.conn.sql(query_dict[query], as_dict=1)
+				if query == 'income':
+					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 and res or None)
+
+		#webnotes.msgprint(result)
+		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()
+			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'] == '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()
+
+		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':
+				args['company_condition'] = ''
+			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
+			})
+
+
+	def get_start_end_dates(self):
+		"""
+			Returns start and end date depending on the frequency of email digest
+		"""
+		from datetime import datetime, date, timedelta
+		today = datetime.now().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
+				creation >= '%(start_date)s' AND
+				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)
+		
+		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
+		"""
+		self.sending = True
+		result, email_body = self.get()
+		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',
+				reply_to='support@erpnext.com',
+				subject=self.doc.frequency + ' Digest',
+				msg=email_body,
+				from_defs=1
+			)
+		except Exception, e:
+			webnotes.msgprint('There was a problem in sending your email. Please contact support@erpnext.com')
+			#webnotes.errprint(webnotes.getTraceback())
+
+
+	def on_update(self):
+		"""
+
+		"""
+		import webnotes
+		args = {
+			'db_name': webnotes.conn.get_value('Control Panel', '', 'account_id'),
+			'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("""
+				SELECT * FROM Event
+				WHERE
+					db_name = %(db_name)s AND
+					event = %(event)s
+			""", args)
+
+			if not (res and res[0]):
+				args['next_execution'] = self.get_next_execution()
+				
+				sch.conn.sql("""
+					INSERT INTO	Event (db_name, event, `interval`, next_execution, recurring)
+					VALUES (%(db_name)s, %(event)s, 86400, %(next_execution)s, 1)
+				""", args)
+
+		else:
+			# delete scheduler entry
+			sch.clear(args['db_name'], args['event'])
+		print "after on update"
+	
+
+	def get_next_sending(self):
+		"""
+
+		"""
+		# Get TimeZone
+		# Get System TimeZone
+		import time
+		from pytz import timezone
+		import datetime
+		import webnotes.defs
+		cp = webnotes.model.doc.Document('Control Panel','Control Panel')
+		app_tz = timezone(cp.time_zone)
+		server_tz = timezone(getattr(webnotes.defs, 'system_timezone'))
+		
+		start_date, end_date = self.get_start_end_dates()
+		
+		new_date = end_date + datetime.timedelta(days=1)
+		new_time = datetime.time(hour=6)
+
+		naive_dt = datetime.datetime.combine(new_date, new_time)
+		app_dt = app_tz.localize(naive_dt)
+		server_dt = server_tz.normalize(app_dt.astimezone(server_tz))
+
+		res = {
+			'app_dt': app_dt.replace(tzinfo=None),
+			'app_tz': app_tz,
+			'server_dt': server_dt.replace(tzinfo=None),
+			'server_tz': server_tz
+		}
+
+		from webnotes.utils import formatdate
+		str_date = formatdate(str(res['app_dt'].date()))
+		str_time = res['app_dt'].time().strftime('%I:%M')
+
+		self.doc.next_send = str_date + " at about " + str_time
+
+		return res
+
+
+	def get_next_execution(self):
+		"""
+
+		"""
+		from datetime import datetime, timedelta
+		dt_args = self.get_next_sending()
+		server_dt = dt_args['server_dt']
+		now_dt = datetime.now(dt_args['server_tz'])
+		if now_dt.time() <= server_dt.time():
+			next_date = now_dt.date()
+		else:
+			next_date = now_dt.date() + timedelta(days=1)
+
+		next_time = server_dt.time()
+
+		return datetime.combine(next_date, next_time)
+
+
+	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):
+			if type(args['body']) == type(''):
+				table_body = """\
+					<tbody><tr>
+						<td style='padding: 5px; font-size: 24px; \
+						font-weight: bold; background: #F7F7F5'>""" + \
+					args['body'] + \
+					"""\
+						</td>
+					</tr></tbody>"""
+
+			elif type(args['body'] == type([])):
+				body_rows = []
+				for rows in args['body']:
+					for r in rows:
+						body_rows.append("""\
+							<tr>
+								<td style='padding: 5px; font-size: 24px; \
+								font-weight: bold; background: #F7F7F5'>""" \
+								+ r + """\
+								</td>
+							</tr>""")
+
+					body_rows.append("<tr><td style='background: #F7F7F5'><br></td></tr>")
+
+				table_body = "<tbody>" + "".join(body_rows) + "</tbody>"
+
+			table_head = """\
+				<thead><tr>
+					<td style='padding: 5px; background: #D8D8D4; font-size: 16px; font-weight: bold'>""" \
+					+ args['head'] + """\
+					</td>
+				</tr></thead>"""
+
+			return "<table style='border-collapse: collapse; width: 100%;'>" \
+				+ table_head \
+				+ table_body \
+				+ "</table>"
+
+		currency_amount_str = "<span style='color: grey; font-size: 12px'>%s</span> %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': [
+						[
+							"<span style='font-size: 16px; font-weight: normal'>%s</span>" % 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("""\
+					<tr>
+						<td style='width: 50%%; vertical-align: top;'>%s</td>
+						<td></td>
+					</tr>""" % (table_list[i]))
+			else:
+				result.append("""\
+					<tr>
+						<td style='width: 50%%; vertical-align: top;'>%s</td>
+						<td>%s</td>
+					</tr>""" % (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 = """
+			<div style='width: 100%%'>
+				<div style='padding: 10px; margin: auto; text-align: center; line-height: 80%%'>
+					<p style='font-weight: bold; font-size: 24px'>%s</p>
+					<p style='font-size: 16px; color: grey'>%s</p>
+					<p style='font-size: 20px; font-weight: bold'>%s</p>
+				</div>
+				<table cellspacing=15 style='width: 100%%'>""" \
+					% ((self.doc.frequency + " Digest"), \
+						digest_daterange, self.doc.company) \
+				+ "".join(result) + """\
+				</table><br><p></p>
+			</div>"""
+
+		return email_body
+
+
+def send():
+	"""
+
+	"""
+	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 026caa7..82bd7bc 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-08 19:21:26',
 		'modified_by': 'Administrator',
 		'owner': 'Administrator'
 	},
 
 	# These values are common for all DocType
 	{
-		'_last_update': '1311760331',
+		'_last_update': '1323352149',
+		'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': 78
 	},
 
 	# 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,290 @@
 
 	# DocPerm
 	{
-		'doctype': 'DocPerm'
+		'cancel': 1,
+		'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': 'Text',
+		'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
+	{
+		'depends_on': 'eval:doc.enabled',
+		'doctype': 'DocField',
+		'fieldname': 'next_send',
+		'fieldtype': 'Data',
+		'label': 'Next email will be sent on:',
+		'permlevel': 1
+	},
+
+	# DocField
+	{
+		'default': '1',
+		'doctype': 'DocField',
+		'fieldname': 'use_standard',
+		'fieldtype': 'Check',
 		'hidden': 1,
-		'idx': 2,
-		'label': 'Content Config'
+		'label': 'Use standard?',
+		'permlevel': 0,
+		'search_index': 0
 	},
 
 	# DocField
 	{
 		'doctype': 'DocField',
-		'fieldname': 'email_config',
+		'fieldtype': 'Column Break',
+		'permlevel': 0
+	},
+
+	# DocField
+	{
+		'doctype': 'DocField',
+		'fieldtype': 'Button',
+		'label': 'Add Recipients',
+		'permlevel': 0,
+		'trigger': 'Client'
+	},
+
+	# DocField
+	{
+		'description': 'Note: Email will not be sent to disabled users',
+		'doctype': 'DocField',
+		'fieldname': 'recipient_list',
 		'fieldtype': 'Text',
+		'label': 'Recipients',
+		'permlevel': 1,
+		'reqd': 1
+	},
+
+	# DocField
+	{
+		'depends_on': 'eval:doc.use_standard',
+		'doctype': 'DocField',
+		'fieldtype': 'Section Break',
+		'label': 'Select Digest Content',
+		'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_enquiries',
+		'fieldtype': 'Check',
+		'label': 'New Enquiries',
+		'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_sales_orders',
+		'fieldtype': 'Check',
+		'label': 'New Sales Orders',
+		'permlevel': 0
+	},
+
+	# DocField
+	{
+		'depends_on': 'eval:doc.use_standard',
+		'doctype': 'DocField',
+		'fieldname': 'new_purchase_orders',
+		'fieldtype': 'Check',
+		'label': 'New Purchase Orders',
+		'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',
+		'fieldname': 'payables',
+		'fieldtype': 'Check',
+		'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,
-		'idx': 3,
-		'label': 'Email Config'
+		'label': 'Stock Items below re-order level',
+		'permlevel': 0
+	},
+
+	# DocField
+	{
+		'depends_on': 'eval:!doc.use_standard',
+		'doctype': 'DocField',
+		'fieldtype': 'Section Break',
+		'label': 'Enter Custom Code',
+		'permlevel': 0
+	},
+
+	# DocField
+	{
+		'depends_on': 'eval:!doc.use_standard',
+		'doctype': 'DocField',
+		'fieldname': 'custom_code',
+		'fieldtype': 'Code',
+		'label': 'Custom Python Code',
+		'permlevel': 0
+	},
+
+	# DocField
+	{
+		'depends_on': 'eval:!doc.use_standard',
+		'doctype': 'DocField',
+		'fieldname': 'email_template',
+		'fieldtype': 'Code',
+		'label': 'Email Template',
+		'permlevel': 0
 	}
 ]
\ No newline at end of file
diff --git a/erpnext/setup/page/setup/setup.js b/erpnext/setup/page/setup/setup.js
index 92ba9db..f536c70 100644
--- a/erpnext/setup/page/setup/setup.js
+++ b/erpnext/setup/page/setup/setup.js
@@ -161,6 +161,7 @@
     ['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 @@
     ['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'],