This commit is contained in:
Rushabh Mehta 2012-09-17 10:05:46 +05:30
commit 40c1ac00bc
5 changed files with 460 additions and 799 deletions

View File

@ -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();
}

View File

@ -16,697 +16,331 @@
from __future__ import unicode_literals
import webnotes
import webnotes.utils
from webnotes.utils import fmt_money, formatdate, now_datetime, cstr, esc
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 <notifications+email_digest@erpnext.com>",
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 = """<hr><h4>No Updates For:</h4><br>""" + "\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 = """<h2>%(digest)s</h2>
<p style='color: grey'>%(date)s</p>
<h4>%(company)s</h4>
<hr>
%(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:
if accounts[ac][1]:
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 = [esc(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 """<p style="padding: 5px; %(style)s">
<span>%(label)s</span>:
<span style="font-weight: bold; font-size: 110%%">
<span style="color: grey">%(currency)s</span>%(value)s
</span></p>""" % {
"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 """<p>%(head)s: <span style='font-size: 110%%; font-weight: bold;'>%(body)s</span></p>""" % args
else:
return ("""<p>%(head)s:</p> """ % args) +\
"".join(map(lambda b: "<p style='margin-left: 17px;'>%s</p>" % b, args['body']))
currency_amount_str = "<span style='color: grey;'>%s</span> %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'] + ": <span style='font-size: 110%%; font-weight: bold;'>" \
+ currency_amount_str % \
(currency, fmt_money(bank.get('value'))) + '</span>')
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("<hr /><h4>No Updates For:</h4><br>")
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(\
"<p>[" + \
k.capitalize() + \
"]<br />Missing: Account of type 'Bank or Cash'\
</p>")
elif k=='bank_balance':
new_section = set_new_section(new_section)
table_list.append(\
"<p>[" + \
"Bank Balance" + \
"]<br />Alert: GL Entry not found for Account of type 'Bank or Cash'\
</p>")
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 = """
<h2>%s</h2>
<p style='color: grey'>%s</p>
<h4>%s</h4>
<hr>
""" \
% ((self.doc.frequency + " Digest"), \
digest_daterange, self.doc.company) \
+ "".join(table_list) + """\
<br><p></p>
"""
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()

View File

@ -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
}
]

View File

@ -847,13 +847,13 @@ wn.ui.AppFrame=Class.extend({init:function(parent,title){this.buttons={};this.$w
<span class="appframe-title"></span>\
<span class="close">&times;</span>\
</div>').appendTo(this.$w);this.$w.find('.close').click(function(){window.history.back();})
if(title)this.title(title);},title:function(txt){this.clear_breadcrumbs();this.add_breadcrumb(txt);},add_button:function(label,click,icon){this.add_toolbar();args={label:label,icon:''};if(icon){args.icon='<i class="icon '+icon+'"></i>';}
if(title)this.title(title);},title:function(txt){this.clear_breadcrumbs();this.add_breadcrumb(txt);},add_button:function(label,click,icon){this.make_toolbar();args={label:label,icon:''};if(icon){args.icon='<i class="icon '+icon+'"></i>';}
this.buttons[label]=$(repl('<button class="btn btn-small">\
%(icon)s %(label)s</button>',args)).click(click).appendTo(this.toolbar);return this.buttons[label];},add_help_button:function(txt){this.make_toolbar();$('<button class="btn btn-small" style="float:right;" button-type="help">\
<b>?</b></button>').data('help-text',txt).click(function(){msgprint($(this).data('help-text'),'Help');}).appendTo(this.toolbar);},clear_buttons:function(){this.toolbar&&this.toolbar.empty();},add_breadcrumb:function(html){if(!this.$breadcrumbs)
this.$breadcrumbs=$('</span>\
<span class="breadcrumb-area"></span>').appendTo(this.$titlebar);var crumb=$('<span>').html(html);if(!this.$breadcrumbs.find('span').length){crumb.addClass('appframe-title');}
crumb.appendTo(this.$breadcrumbs);},clear_breadcrumbs:function(){this.$breadcrumbs&&this.$breadcrumbs.empty();},add_toolbar:function(){if(!this.toolbar)
crumb.appendTo(this.$breadcrumbs);},clear_breadcrumbs:function(){this.$breadcrumbs&&this.$breadcrumbs.empty();},make_toolbar:function(){if(!this.toolbar)
this.$w.append('<div class="appframe-toolbar"></div>');this.toolbar=this.$w.find('.appframe-toolbar');},add_label:function(label){return $("<span class='label'>"+label+" </span>").appendTo(this.toolbar);},add_select:function(label,options){this.add_toolbar();return $("<select style='width: 160px;'>").add_options(options).appendTo(this.toolbar);},add_data:function(label){this.add_toolbar();return $("<input style='width: 100px;' placeholder='"+label+"'>").appendTo(this.toolbar);},add_date:function(label,date){this.add_toolbar();return $("<input style='width: 80px;'>").datepicker({dateFormat:sys_defaults.date_format.replace("yyyy","yy"),changeYear:true,}).val(dateutil.str_to_user(date)||"").appendTo(this.toolbar);},});wn.ui.make_app_page=function(opts){if(opts.single_column){$(opts.parent).html('<div class="layout-wrapper layout-wrapper-appframe">\
<div class="layout-appframe"></div>\
<div class="layout-main"></div>\

View File

@ -508,13 +508,13 @@ wn.ui.AppFrame=Class.extend({init:function(parent,title){this.buttons={};this.$w
<span class="appframe-title"></span>\
<span class="close">&times;</span>\
</div>').appendTo(this.$w);this.$w.find('.close').click(function(){window.history.back();})
if(title)this.title(title);},title:function(txt){this.clear_breadcrumbs();this.add_breadcrumb(txt);},add_button:function(label,click,icon){this.add_toolbar();args={label:label,icon:''};if(icon){args.icon='<i class="icon '+icon+'"></i>';}
if(title)this.title(title);},title:function(txt){this.clear_breadcrumbs();this.add_breadcrumb(txt);},add_button:function(label,click,icon){this.make_toolbar();args={label:label,icon:''};if(icon){args.icon='<i class="icon '+icon+'"></i>';}
this.buttons[label]=$(repl('<button class="btn btn-small">\
%(icon)s %(label)s</button>',args)).click(click).appendTo(this.toolbar);return this.buttons[label];},add_help_button:function(txt){this.make_toolbar();$('<button class="btn btn-small" style="float:right;" button-type="help">\
<b>?</b></button>').data('help-text',txt).click(function(){msgprint($(this).data('help-text'),'Help');}).appendTo(this.toolbar);},clear_buttons:function(){this.toolbar&&this.toolbar.empty();},add_breadcrumb:function(html){if(!this.$breadcrumbs)
this.$breadcrumbs=$('</span>\
<span class="breadcrumb-area"></span>').appendTo(this.$titlebar);var crumb=$('<span>').html(html);if(!this.$breadcrumbs.find('span').length){crumb.addClass('appframe-title');}
crumb.appendTo(this.$breadcrumbs);},clear_breadcrumbs:function(){this.$breadcrumbs&&this.$breadcrumbs.empty();},add_toolbar:function(){if(!this.toolbar)
crumb.appendTo(this.$breadcrumbs);},clear_breadcrumbs:function(){this.$breadcrumbs&&this.$breadcrumbs.empty();},make_toolbar:function(){if(!this.toolbar)
this.$w.append('<div class="appframe-toolbar"></div>');this.toolbar=this.$w.find('.appframe-toolbar');},add_label:function(label){return $("<span class='label'>"+label+" </span>").appendTo(this.toolbar);},add_select:function(label,options){this.add_toolbar();return $("<select style='width: 160px;'>").add_options(options).appendTo(this.toolbar);},add_data:function(label){this.add_toolbar();return $("<input style='width: 100px;' placeholder='"+label+"'>").appendTo(this.toolbar);},add_date:function(label,date){this.add_toolbar();return $("<input style='width: 80px;'>").datepicker({dateFormat:sys_defaults.date_format.replace("yyyy","yy"),changeYear:true,}).val(dateutil.str_to_user(date)||"").appendTo(this.toolbar);},});wn.ui.make_app_page=function(opts){if(opts.single_column){$(opts.parent).html('<div class="layout-wrapper layout-wrapper-appframe">\
<div class="layout-appframe"></div>\
<div class="layout-main"></div>\