improvements to email digest (#6323)
This commit is contained in:
parent
b8eef64e5d
commit
8d43b32c2c
@ -127,6 +127,83 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
# if bal is None, return 0
|
||||
return flt(bal)
|
||||
|
||||
def get_count_on(account, fieldname, date):
|
||||
|
||||
cond = []
|
||||
if date:
|
||||
cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date)))
|
||||
else:
|
||||
# get balance of all entries that exist
|
||||
date = nowdate()
|
||||
|
||||
try:
|
||||
year_start_date = get_fiscal_year(date, verbose=0)[1]
|
||||
except FiscalYearError:
|
||||
if getdate(date) > getdate(nowdate()):
|
||||
# if fiscal year not found and the date is greater than today
|
||||
# get fiscal year for today's date and its corresponding year start date
|
||||
year_start_date = get_fiscal_year(nowdate(), verbose=1)[1]
|
||||
else:
|
||||
# this indicates that it is a date older than any existing fiscal year.
|
||||
# hence, assuming balance as 0.0
|
||||
return 0.0
|
||||
|
||||
if account:
|
||||
acc = frappe.get_doc("Account", account)
|
||||
|
||||
if not frappe.flags.ignore_account_permission:
|
||||
acc.check_permission("read")
|
||||
|
||||
# for pl accounts, get balance within a fiscal year
|
||||
if acc.report_type == 'Profit and Loss':
|
||||
cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
|
||||
% year_start_date)
|
||||
|
||||
# different filter for group and ledger - improved performance
|
||||
if acc.is_group:
|
||||
cond.append("""exists (
|
||||
select name from `tabAccount` ac where ac.name = gle.account
|
||||
and ac.lft >= %s and ac.rgt <= %s
|
||||
)""" % (acc.lft, acc.rgt))
|
||||
|
||||
# If group and currency same as company,
|
||||
# always return balance based on debit and credit in company currency
|
||||
if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"):
|
||||
in_account_currency = False
|
||||
else:
|
||||
cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), ))
|
||||
|
||||
entries = frappe.db.sql("""
|
||||
SELECT name, posting_date, account, party_type, party,debit,credit,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher
|
||||
FROM `tabGL Entry` gle
|
||||
WHERE {0}""".format(" and ".join(cond)), as_dict=True)
|
||||
|
||||
count = 0
|
||||
for gle in entries:
|
||||
if fieldname not in ('invoiced_amount','payables'):
|
||||
count += 1
|
||||
else:
|
||||
dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit"
|
||||
cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit"
|
||||
select_fields = "ifnull(sum(credit-debit),0)" if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
|
||||
|
||||
if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
|
||||
(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)):
|
||||
payment_amount = frappe.db.sql("""
|
||||
SELECT {0}
|
||||
FROM `tabGL Entry` gle
|
||||
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
|
||||
and party = %(party)s and name != %(name)s""".format(select_fields),
|
||||
{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0]
|
||||
|
||||
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
|
||||
currency_precision = get_currency_precision() or 2
|
||||
if abs(flt(outstanding_amount)) > 0.1/10**currency_precision:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_ac(args=None):
|
||||
if not args:
|
||||
|
@ -264,7 +264,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Income / Expense",
|
||||
"label": "Profit & Loss",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -289,7 +289,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Income",
|
||||
"label": "New Income",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -314,32 +314,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Expense",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "",
|
||||
"fieldname": "bank_balance",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Bank Balance",
|
||||
"label": "New Expenses",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -387,7 +362,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Annual Expense",
|
||||
"label": "Annual Expenses",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -413,7 +388,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Receivables / Payables",
|
||||
"label": "Balance Sheet",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -426,6 +401,56 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "",
|
||||
"fieldname": "bank_balance",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Bank Balance",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "credit_balance",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Credit Balance",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -476,6 +501,280 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Operations",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "New Sales Orders",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "pending_sales_orders",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Pending Sales Orders",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "purchase_order",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "New Purchase Orders",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "pending_purchase_orders",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Pending Purchase Orders",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_operation",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "new_quotations",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "New Quotations",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "pending_quotations",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Pending Quotations",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "issue",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Open Issues",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Open Projects",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -501,6 +800,131 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "tools",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Tools",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "calendar_events",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Upcoming Calendar Events",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "todo_list",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Open To Do",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "notifications",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Open Notifications",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_32",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": " ",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -538,7 +962,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2016-02-22 09:22:28.877187",
|
||||
"modified": "2016-06-23 11:38:50.729998",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Email Digest",
|
||||
|
@ -10,7 +10,7 @@ from datetime import timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from frappe.core.doctype.user.user import STANDARD_USERS
|
||||
import frappe.desk.notifications
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.utils import get_balance_on, get_count_on, get_currency_precision
|
||||
|
||||
user_specific_content = ["calendar_events", "todo_list"]
|
||||
|
||||
@ -77,10 +77,20 @@ class EmailDigest(Document):
|
||||
self.set_title(context)
|
||||
self.set_style(context)
|
||||
self.set_accounting_cards(context)
|
||||
|
||||
context.events = self.get_calendar_events()
|
||||
context.todo_list = self.get_todo_list()
|
||||
context.notifications = self.get_notifications()
|
||||
|
||||
if self.get("calendar_events"):
|
||||
context.events, context.event_count = self.get_calendar_events()
|
||||
if self.get("todo_list"):
|
||||
context.todo_list = self.get_todo_list()
|
||||
context.todo_count = self.get_todo_count()
|
||||
if self.get("notifications"):
|
||||
context.notifications = self.get_notifications()
|
||||
if self.get("issue"):
|
||||
context.issue_list = self.get_issue_list()
|
||||
context.issue_count = self.get_issue_count()
|
||||
if self.get("project"):
|
||||
context.project_list = self.get_project_list()
|
||||
context.project_count = self.get_project_count()
|
||||
|
||||
quote = get_random_quote()
|
||||
context.quote = {"text": quote[0], "author": quote[1]}
|
||||
@ -136,14 +146,16 @@ class EmailDigest(Document):
|
||||
from frappe.desk.doctype.event.event import get_events
|
||||
events = get_events(self.future_from_date.strftime("%Y-%m-%d"),
|
||||
self.future_to_date.strftime("%Y-%m-%d")) or []
|
||||
|
||||
|
||||
event_count = 0
|
||||
for i, e in enumerate(events):
|
||||
e.starts_on_label = format_time(e.starts_on)
|
||||
e.ends_on_label = format_time(e.ends_on) if e.ends_on else None
|
||||
e.date = formatdate(e.starts)
|
||||
e.link = get_url_to_form("Event", e.name)
|
||||
event_count += 1
|
||||
|
||||
return events
|
||||
return events, event_count
|
||||
|
||||
def get_todo_list(self, user_id=None):
|
||||
"""Get to-do list"""
|
||||
@ -159,14 +171,67 @@ class EmailDigest(Document):
|
||||
t.link = get_url_to_form("ToDo", t.name)
|
||||
|
||||
return todo_list
|
||||
|
||||
def get_todo_count(self, user_id=None):
|
||||
"""Get count of Todo"""
|
||||
if not user_id:
|
||||
user_id = frappe.session.user
|
||||
|
||||
return frappe.db.sql("""select count(*) from `tabToDo`
|
||||
where status='Open' and (owner=%s or assigned_by=%s)""",
|
||||
(user_id, user_id))[0][0]
|
||||
|
||||
def get_issue_list(self, user_id=None):
|
||||
"""Get issue list"""
|
||||
if not user_id:
|
||||
user_id = frappe.session.user
|
||||
|
||||
meta = frappe.get_meta("Issue")
|
||||
role_permissions = frappe.permissions.get_role_permissions(meta, user_id)
|
||||
if not role_permissions.get("read"):
|
||||
return None
|
||||
|
||||
issue_list = frappe.db.sql("""select *
|
||||
from `tabIssue` where status in ("Replied","Open")
|
||||
order by modified asc limit 10""", as_dict=True)
|
||||
|
||||
for t in issue_list:
|
||||
t.link = get_url_to_form("Issue", t.name)
|
||||
|
||||
return issue_list
|
||||
|
||||
def get_issue_count(self):
|
||||
"""Get count of Issue"""
|
||||
return frappe.db.sql("""select count(*) from `tabIssue`
|
||||
where status in ('Open','Replied') """)[0][0]
|
||||
|
||||
def get_project_list(self, user_id=None):
|
||||
"""Get project list"""
|
||||
if not user_id:
|
||||
user_id = frappe.session.user
|
||||
|
||||
project_list = frappe.db.sql("""select *
|
||||
from `tabProject` where status='Open' and project_type='External'
|
||||
order by modified asc limit 10""", as_dict=True)
|
||||
|
||||
for t in project_list:
|
||||
t.link = get_url_to_form("Issue", t.name)
|
||||
|
||||
return project_list
|
||||
|
||||
def get_project_count(self):
|
||||
"""Get count of Project"""
|
||||
return frappe.db.sql("""select count(*) from `tabProject`
|
||||
where status='Open' and project_type='External'""")[0][0]
|
||||
|
||||
def set_accounting_cards(self, context):
|
||||
"""Create accounting cards if checked"""
|
||||
|
||||
cache = frappe.cache()
|
||||
context.cards = []
|
||||
for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
|
||||
"invoiced_amount", "payables", "bank_balance"):
|
||||
for key in ("income", "expenses_booked", "income_year_to_date","expense_year_to_date",
|
||||
"new_quotations","pending_quotations","sales_order","purchase_order","pending_sales_orders","pending_purchase_orders",
|
||||
"invoiced_amount", "payables", "bank_balance", "credit_balance"):
|
||||
if self.get(key):
|
||||
cache_key = "email_digest:card:{0}:{1}".format(self.company, key)
|
||||
card = cache.get(cache_key)
|
||||
@ -187,9 +252,25 @@ class EmailDigest(Document):
|
||||
card.diff = "+" + str(card.diff)
|
||||
card.gain = True
|
||||
|
||||
card.last_value = self.fmt_money(card.last_value)
|
||||
if key == "credit_balance":
|
||||
card.last_value = card.last_value * -1
|
||||
card.last_value = self.fmt_money(card.last_value,False if key in ("bank_balance", "credit_balance") else True)
|
||||
|
||||
card.value = self.fmt_money(card.value)
|
||||
|
||||
if card.billed_value:
|
||||
card.billed = int(flt(card.billed_value) / card.value * 100)
|
||||
card.billed = "% Billed " + str(card.billed)
|
||||
|
||||
if card.delivered_value:
|
||||
card.delivered = int(flt(card.delivered_value) / card.value * 100)
|
||||
if key == "pending_sales_orders":
|
||||
card.delivered = "% Delivered " + str(card.delivered)
|
||||
else:
|
||||
card.delivered = "% Received " + str(card.delivered)
|
||||
|
||||
if key =="credit_balance":
|
||||
card.value = card.value *-1
|
||||
card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
|
||||
|
||||
cache.setex(cache_key, card, 24 * 60 * 60)
|
||||
|
||||
@ -197,37 +278,45 @@ class EmailDigest(Document):
|
||||
|
||||
def get_income(self):
|
||||
"""Get income for given period"""
|
||||
income, past_income = self.get_period_amounts(self.get_root_type_accounts("income"))
|
||||
income, past_income, count = self.get_period_amounts(self.get_root_type_accounts("income"),'income')
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("income"),
|
||||
"value": income,
|
||||
"last_value": past_income
|
||||
"last_value": past_income,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_income_year_to_date(self):
|
||||
"""Get income to date"""
|
||||
return self.get_year_to_date_balance("income")
|
||||
return self.get_year_to_date_balance("income", "income")
|
||||
|
||||
def get_expense_year_to_date(self):
|
||||
"""Get income to date"""
|
||||
return self.get_year_to_date_balance("expense")
|
||||
return self.get_year_to_date_balance("expense","expenses_booked")
|
||||
|
||||
def get_year_to_date_balance(self, root_type):
|
||||
def get_year_to_date_balance(self, root_type, fieldname):
|
||||
"""Get income to date"""
|
||||
balance = 0.0
|
||||
count = 0
|
||||
|
||||
for account in self.get_root_type_accounts(root_type):
|
||||
balance += get_balance_on(account, date = self.future_to_date)
|
||||
count += get_count_on(account, fieldname, date = self.future_to_date)
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label(root_type + "_year_to_date"),
|
||||
"value": balance
|
||||
"value": balance,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_bank_balance(self):
|
||||
# account is of type "Bank" or "Cash"
|
||||
return self.get_type_balance('bank_balance', 'Bank')
|
||||
# account is of type "Bank" and root_type is Asset
|
||||
return self.get_type_balance('bank_balance', 'Bank', root_type='Asset')
|
||||
|
||||
def get_credit_balance(self):
|
||||
# account is of type "Bank" and root_type is Liability
|
||||
return self.get_type_balance('credit_balance', 'Bank', root_type='Liability')
|
||||
|
||||
def get_payables(self):
|
||||
return self.get_type_balance('payables', 'Payable')
|
||||
@ -236,41 +325,62 @@ class EmailDigest(Document):
|
||||
return self.get_type_balance('invoiced_amount', 'Receivable')
|
||||
|
||||
def get_expenses_booked(self):
|
||||
expense, past_expense = self.get_period_amounts(self.get_root_type_accounts("expense"))
|
||||
expense, past_expense, count = self.get_period_amounts(self.get_root_type_accounts("expense"), 'expenses_booked')
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("expenses_booked"),
|
||||
"value": expense,
|
||||
"last_value": past_expense
|
||||
"last_value": past_expense,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_period_amounts(self, accounts):
|
||||
def get_period_amounts(self, accounts, fieldname):
|
||||
"""Get amounts for current and past periods"""
|
||||
balance = past_balance = 0.0
|
||||
count = 0
|
||||
for account in accounts:
|
||||
balance += (get_balance_on(account, date = self.future_to_date)
|
||||
- get_balance_on(account, date = self.future_from_date))
|
||||
|
||||
count += (get_count_on(account,fieldname, date = self.future_to_date )
|
||||
- get_count_on(account,fieldname, date = self.future_from_date))
|
||||
|
||||
past_balance += (get_balance_on(account, date = self.past_to_date)
|
||||
- get_balance_on(account, date = self.past_from_date))
|
||||
|
||||
return balance, past_balance
|
||||
return balance, past_balance, count
|
||||
|
||||
def get_type_balance(self, fieldname, account_type):
|
||||
accounts = [d.name for d in \
|
||||
frappe.db.get_all("Account", filters={"account_type": account_type,
|
||||
def get_type_balance(self, fieldname, account_type, root_type=None):
|
||||
|
||||
if root_type:
|
||||
accounts = [d.name for d in \
|
||||
frappe.db.get_all("Account", filters={"account_type": account_type,
|
||||
"company": self.company, "is_group": 0, "root_type": root_type})]
|
||||
else:
|
||||
accounts = [d.name for d in \
|
||||
frappe.db.get_all("Account", filters={"account_type": account_type,
|
||||
"company": self.company, "is_group": 0})]
|
||||
|
||||
balance = prev_balance = 0.0
|
||||
count = 0
|
||||
for account in accounts:
|
||||
balance += get_balance_on(account, date=self.future_from_date)
|
||||
prev_balance += get_balance_on(account, date=self.past_from_date)
|
||||
|
||||
return {
|
||||
'label': self.meta.get_label(fieldname),
|
||||
'value': balance,
|
||||
'last_value': prev_balance
|
||||
}
|
||||
balance += get_balance_on(account, date=self.future_to_date)
|
||||
count += get_count_on(account, fieldname, date=self.future_to_date)
|
||||
prev_balance += get_balance_on(account, date=self.past_to_date)
|
||||
|
||||
if fieldname in ("bank_balance","credit_balance"):
|
||||
return {
|
||||
'label': self.meta.get_label(fieldname),
|
||||
'value': balance,
|
||||
'last_value': prev_balance }
|
||||
else:
|
||||
return {
|
||||
'label': self.meta.get_label(fieldname),
|
||||
'value': balance,
|
||||
'last_value': prev_balance,
|
||||
'count': count
|
||||
}
|
||||
|
||||
|
||||
def get_root_type_accounts(self, root_type):
|
||||
if not root_type in self._accounts:
|
||||
@ -279,6 +389,83 @@ class EmailDigest(Document):
|
||||
"company": self.company, "is_group": 0})]
|
||||
return self._accounts[root_type]
|
||||
|
||||
def get_purchase_order(self):
|
||||
|
||||
return self.get_summary_of_doc("Purchase Order","purchase_order")
|
||||
|
||||
def get_sales_order(self):
|
||||
|
||||
return self.get_summary_of_doc("Sales Order","sales_order")
|
||||
|
||||
def get_pending_purchase_orders(self):
|
||||
|
||||
return self.get_summary_of_pending("Purchase Order","pending_purchase_orders","per_received")
|
||||
|
||||
def get_pending_sales_orders(self):
|
||||
|
||||
return self.get_summary_of_pending("Sales Order","pending_sales_orders","per_delivered")
|
||||
|
||||
def get_new_quotations(self):
|
||||
|
||||
return self.get_summary_of_doc("Quotation","new_quotations")
|
||||
|
||||
def get_pending_quotations(self):
|
||||
|
||||
return self.get_summary_of_pending_quotations("pending_quotations")
|
||||
|
||||
def get_summary_of_pending(self, doc_type, fieldname, getfield):
|
||||
|
||||
value, count, billed_value, delivered_value = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*),
|
||||
ifnull(sum(grand_total*per_billed/100),0), ifnull(sum(grand_total*{0}/100),0) from `tab{1}`
|
||||
where (transaction_date <= %(to_date)s)
|
||||
and status not in ('Closed','Cancelled', 'Completed') """.format(getfield, doc_type),
|
||||
{"to_date": self.future_to_date})[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label(fieldname),
|
||||
"value": value,
|
||||
"billed_value": billed_value,
|
||||
"delivered_value": delivered_value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_summary_of_pending_quotations(self, fieldname):
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tabQuotation`
|
||||
where (transaction_date <= %(to_date)s)
|
||||
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.future_to_date})[0]
|
||||
|
||||
last_value = frappe.db.sql("""select ifnull(sum(grand_total),0) from `tabQuotation`
|
||||
where (transaction_date <= %(to_date)s)
|
||||
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.past_to_date})[0][0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label(fieldname),
|
||||
"value": value,
|
||||
"last_value": last_value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_summary_of_doc(self, doc_type, fieldname):
|
||||
|
||||
value = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0]
|
||||
count = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[1]
|
||||
|
||||
last_value =self.get_total_on(doc_type, self.past_from_date, self.past_to_date)[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label(fieldname),
|
||||
"value": value,
|
||||
"last_value": last_value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_total_on(self, doc_type, from_date, to_date):
|
||||
|
||||
return frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tab{0}`
|
||||
where (transaction_date between %(from_date)s and %(to_date)s) and status not in ('Cancelled')""".format(doc_type),
|
||||
{"from_date": from_date, "to_date": to_date})[0]
|
||||
|
||||
def get_from_to_date(self):
|
||||
today = now_datetime().date()
|
||||
|
||||
@ -332,8 +519,11 @@ class EmailDigest(Document):
|
||||
def onload(self):
|
||||
self.get_next_sending()
|
||||
|
||||
def fmt_money(self, value):
|
||||
return fmt_money(abs(value), currency = self.currency)
|
||||
def fmt_money(self, value,absol=True):
|
||||
if absol:
|
||||
return fmt_money(abs(value), currency = self.currency)
|
||||
else:
|
||||
return fmt_money(value, currency=self.currency)
|
||||
|
||||
def send():
|
||||
now_date = now_datetime().date()
|
||||
|
@ -1,10 +1,20 @@
|
||||
{% macro show_card(card) %}
|
||||
<div style="width: 50%; float:left; min-height: 80px; padding-top: 20px;">
|
||||
<h6 style="color: {{ text_muted }}; font-size: 12px; margin-bottom: 0px; margin-top: 0px;">{{ card.label }}</h6>
|
||||
<h6 style="color: {{ text_muted }}; font-size: 12px; margin-bottom: 0px; margin-top: 0px;">{{ card.label }}
|
||||
{% if card.count %}
|
||||
<span class="badge">({{ card.count }})</span>
|
||||
{% endif %}</h6>
|
||||
<h4 style="margin-top: 7px; font-size: 16px; margin-botom: 5px;">{{ card.value }}</h4>
|
||||
{% if card.diff %}
|
||||
<p style="color: {{ text_muted }}; font-size: 12px; margin-top: 0px;">{{ card.diff }}%</p>
|
||||
{% endif %}
|
||||
{% if card.billed %}
|
||||
<p style="color: {{ text_muted }}; font-size: 12px; margin-top: 0px;">{{ card.billed }}%</p>
|
||||
{% endif %}
|
||||
{% if card.delivered %}
|
||||
<p style="color: {{ text_muted }}; font-size: 12px; margin-top: 0px;">{{ card.delivered }}%</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@ -31,13 +41,58 @@
|
||||
<div style="clear: both"></div>
|
||||
{% endif %}
|
||||
|
||||
<!-- issue list -->
|
||||
{% if issue_list %}
|
||||
<h4 style="{{ section_head }}">{{ _("Open Issues ") }}<span class="badge">({{ issue_count }})</span></h4>
|
||||
<div>
|
||||
{% for t in issue_list %}
|
||||
<div style="{{ line_item }}">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>
|
||||
<a style="{{ link_css }}" href="{{ t.link }}">{{ _(t.subject) }}</a>
|
||||
</td>
|
||||
<td style="width: 25%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ _(t.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- project list -->
|
||||
{% if project_list %}
|
||||
<h4 style="{{ section_head }}">{{ _("Open Projects ") }}<span class="badge">({{ project_count }})</span></h4>
|
||||
<div>
|
||||
{% for t in project_list %}
|
||||
<div style="{{ line_item }}">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>
|
||||
<a style="{{ link_css }}" href="{{ t.link }}">{{ _(t.project_name) }}</a>
|
||||
</td>
|
||||
<td style="width: 25%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ _(t.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if events or todo_list or notifications %}
|
||||
<h1 style="{{ h1 }}">{{ _("Pending Activities") }}</h1>
|
||||
|
||||
<!-- events -->
|
||||
{% if events %}
|
||||
<h4 style="{{ section_head }}">{{ _("Upcoming Events") }}</h4>
|
||||
<h4 style="{{ section_head }}">{{ _("Upcoming Calendar Events ") }}<span class="badge">({{ event_count }})</span></h4>
|
||||
<div>
|
||||
{% for e in events %}
|
||||
{% if loop.index==1 or events[loop.index-1].date != e.date %}
|
||||
@ -69,7 +124,7 @@
|
||||
|
||||
<!-- todo list -->
|
||||
{% if todo_list %}
|
||||
<h4 style="{{ section_head }}">{{ _("To Do List") }}</h4>
|
||||
<h4 style="{{ section_head }}">{{ _("Open To Do ") }}<span class="badge">({{ todo_count }})</span></h4>
|
||||
<div>
|
||||
{% for t in todo_list %}
|
||||
<div style="{{ line_item }}">
|
||||
|
Loading…
x
Reference in New Issue
Block a user