Przeglądaj źródła

[feature] [wip] added Auto Email Report

version-14
Rushabh Mehta 9 lat temu
rodzic
commit
303e7460c2
13 zmienionych plików z 614 dodań i 18 usunięć
  1. +3
    -3
      frappe/core/doctype/report/report.py
  2. +1
    -1
      frappe/core/doctype/report/test_report.py
  3. +1
    -1
      frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.js
  4. +10
    -8
      frappe/desk/query_report.py
  5. +0
    -0
      frappe/email/doctype/auto_email_report/__init__.py
  6. +63
    -0
      frappe/email/doctype/auto_email_report/auto_email_report.js
  7. +426
    -0
      frappe/email/doctype/auto_email_report/auto_email_report.json
  8. +86
    -0
      frappe/email/doctype/auto_email_report/auto_email_report.py
  9. +12
    -0
      frappe/email/doctype/auto_email_report/test_auto_email_report.py
  10. +4
    -0
      frappe/hooks.py
  11. +2
    -2
      frappe/public/js/frappe/ui/dialog.js
  12. +4
    -1
      frappe/public/js/frappe/ui/field_group.js
  13. +2
    -2
      frappe/public/js/frappe/views/reports/query_report.js

+ 3
- 3
frappe/core/doctype/report/report.py Wyświetl plik

@@ -50,13 +50,13 @@ class Report(Document):
make_boilerplate("controller.py", self, {"name": self.name})
make_boilerplate("controller.js", self, {"name": self.name})

def get_data(self, filters=None, limit=None):
def get_data(self, filters=None, limit=None, user=None):
'''Run the report'''
out = []

if self.report_type in ('Query Report', 'Script Report'):
# query and script reports
data = frappe.desk.query_report.run(self.name, filters=filters)
data = frappe.desk.query_report.run(self.name, filters=filters, user=user)
out.append([d.split(':')[0] for d in data.get('columns')])
out += data.get('result')
else:
@@ -74,7 +74,7 @@ class Report(Document):
order_by += ', ' + _format(params.get('sort_by_next').split('.')) + ' ' + params.get('sort_order_next')

result = frappe.get_list(self.ref_doctype, fields = [_format([c[1], c[0]]) for c in columns],
filters=filters, order_by = order_by, as_list=True, limit=limit)
filters=filters, order_by = order_by, as_list=True, limit=limit, user=user)

meta = frappe.get_meta(self.ref_doctype)



+ 1
- 1
frappe/core/doctype/report/test_report.py Wyświetl plik

@@ -22,7 +22,7 @@ class TestReport(unittest.TestCase):
data = report.get_data(filters={'user': 'Administrator', 'doctype': 'DocType'})
self.assertEquals(data[0][0], 'Name')
self.assertEquals(data[0][1], 'Module')
self.assertTrue('Auto Email Report' in [d[0] for d in data])
self.assertTrue('User' in [d[0] for d in data])

# test standard report with child table
user_activity_report = '''


+ 1
- 1
frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.js Wyświetl plik

@@ -20,7 +20,7 @@ frappe.query_reports["Permitted Documents For User"] = {
return {
"query": "frappe.core.report.permitted_documents_for_user.permitted_documents_for_user.query_doctypes",
"filters": {
"user": frappe.query_report.filters_by_name.user.get_value()
"user": frappe.query_report_filters_by_name.user.get_value()
}
}
}


+ 10
- 8
frappe/desk/query_report.py Wyświetl plik

@@ -60,8 +60,10 @@ def get_script(report_name):
}

@frappe.whitelist()
def run(report_name, filters=None):
def run(report_name, filters=None, user=None):
report = get_report_doc(report_name)
if not user:
user = frappe.session.user

if not filters:
filters = []
@@ -97,7 +99,7 @@ def run(report_name, filters=None):
chart = res[3]

if report.apply_user_permissions and result:
result = get_filtered_data(report.ref_doctype, columns, result)
result = get_filtered_data(report.ref_doctype, columns, result, user)

if cint(report.add_total_row) and result:
result = add_total_row(result, columns)
@@ -158,14 +160,14 @@ def add_total_row(result, columns):
result.append(total_row)
return result

def get_filtered_data(ref_doctype, columns, data):
def get_filtered_data(ref_doctype, columns, data, user):
result = []
linked_doctypes = get_linked_doctypes(columns, data)
match_filters_per_doctype = get_user_match_filters(linked_doctypes, ref_doctype)
shared = frappe.share.get_shared(ref_doctype)
shared = frappe.share.get_shared(ref_doctype, user)
columns_dict = get_columns_dict(columns)

role_permissions = get_role_permissions(frappe.get_meta(ref_doctype))
role_permissions = get_role_permissions(frappe.get_meta(ref_doctype), user)
if_owner = role_permissions.get("if_owner", {}).get("report")

if match_filters_per_doctype:
@@ -174,14 +176,14 @@ def get_filtered_data(ref_doctype, columns, data):
if linked_doctypes.get(ref_doctype) and shared and row[linked_doctypes[ref_doctype]] in shared:
result.append(row)

elif has_match(row, linked_doctypes, match_filters_per_doctype, ref_doctype, if_owner, columns_dict):
elif has_match(row, linked_doctypes, match_filters_per_doctype, ref_doctype, if_owner, columns_dict, user):
result.append(row)
else:
result = list(data)

return result

def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner, columns_dict):
def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner, columns_dict, user):
"""Returns True if after evaluating permissions for each linked doctype
- There is an owner match for the ref_doctype
- `and` There is a user permission match for all linked doctypes
@@ -205,7 +207,7 @@ def has_match(row, linked_doctypes, doctype_match_filters, ref_doctype, if_owner
if doctype==ref_doctype and if_owner:
idx = linked_doctypes.get("User")
if (idx is not None
and row[idx]==frappe.session.user
and row[idx]==user
and columns_dict[idx]==columns_dict.get("owner")):
# owner match is true
matched_for_doctype = True


+ 0
- 0
frappe/email/doctype/auto_email_report/__init__.py Wyświetl plik


+ 63
- 0
frappe/email/doctype/auto_email_report/auto_email_report.js Wyświetl plik

@@ -0,0 +1,63 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Auto Email Report', {
refresh: function(frm) {
if(frm.doc.report_type !== 'Report Builder') {
if(frm.script_setup_for !== frm.doc.report) {
frappe.call({
method:"frappe.desk.query_report.get_script",
args: {
report_name: frm.doc.report
},
callback: function(r) {
frappe.dom.eval(r.message.script || "");
frm.script_setup_for = frm.doc.report;
frm.trigger('show_filters');
}
});
} else {
frm.trigger('show_filters');
}
}
},
show_filters: function(frm) {
var wrapper = $(frm.get_field('filters_display').wrapper);
wrapper.empty();
if(frm.doc.report_type !== 'Report Builder'
&& frappe.query_reports[frm.doc.report]
&& frappe.query_reports[frm.doc.report].filters) {

// make a table to show filters
var table = $('<table class="table table-bordered" style="cursor:pointer;"><thead>\
<tr><th style="width: 50%">'+__('Filter')+'</th><th>'+__('Value')+'</th></tr>\
</thead><tbody></tbody></table>').appendTo(wrapper);
$('<p class="text-muted small">' + __("Click table to edit") + '</p>').appendTo(wrapper);
var filters = JSON.parse(frm.doc.filters || '{}');
var report_filters = frappe.query_reports[frm.doc.report].filters;

report_filters.forEach(function(f) {
$('<tr><td>' + f.label + '</td><td>'+ frappe.format(filters[f.fieldname], f) +'</td></tr>')
.appendTo(table.find('tbody'));
});

table.on('click', function() {
var dialog = new frappe.ui.Dialog({
fields: report_filters,
primary_action: function() {
var values = this.get_values();
if(values) {
this.hide();
frm.set_value('filters', JSON.stringify(values));
frm.trigger('show_filters');
frappe.query_report_filters_by_name = null;
}
}
});
dialog.show();
dialog.set_values(filters);
frappe.query_report_filters_by_name = dialog.fields_dict;
})
}
}
});

+ 426
- 0
frappe/email/doctype/auto_email_report/auto_email_report.json Wyświetl plik

@@ -0,0 +1,426 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:report",
"beta": 0,
"creation": "2016-09-01 01:34:34.985457",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "report",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Report",
"length": 0,
"no_copy": 0,
"options": "Report",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "report_type",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Report Type",
"length": 0,
"no_copy": 0,
"options": "report.report_type",
"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,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "For User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Enabled",
"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,
"columns": 0,
"depends_on": "eval:doc.report_type !== 'Report Builder'",
"fieldname": "report_filters",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Report Filters",
"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,
"columns": 0,
"fieldname": "filters_display",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Filters Display",
"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,
"columns": 0,
"fieldname": "filters",
"fieldtype": "Text",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Filters",
"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,
"columns": 0,
"fieldname": "email_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Email Settings",
"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,
"columns": 0,
"fieldname": "frequency",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Frequency",
"length": 0,
"no_copy": 0,
"options": "Daily\nWeekly\nMonthly",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Monday",
"depends_on": "eval:doc.frequency=='Weekly'",
"fieldname": "day_of_week",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Day of Week",
"length": 0,
"no_copy": 0,
"options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
"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,
"columns": 0,
"fieldname": "format",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Format",
"length": 0,
"no_copy": 0,
"options": "HTML\nXLS\nCSV",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_to",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Email To",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Description",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-09-01 05:21:16.487736",
"modified_by": "Administrator",
"module": "Email",
"name": "Auto Email Report",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Report Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

+ 86
- 0
frappe/email/doctype/auto_email_report/auto_email_report.py Wyświetl plik

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
import frappe.utils

max_reports_per_user = 3

class AutoEmailReport(Document):
def autoname(self):
self.name = _(self.report)

def validate(self):
self.validate_report_count()
self.validate_emails()

def validate_emails(self):
'''Cleanup list of emails'''
if ',' in self.emails:
self.emails.replace(',', '\n')

valid = []
for email in self.emails.split():
if email:
frappe.utils.validate_email_add(email, True)
valid.append(email)

self.emails = '\n'.join(valid)

def validate_report_count(self):
'''check that there are only 3 enabled reports per user'''
count = frappe.db.sql('select count(*) from `tabAuto Email Report` where user=%s and enabled=1', self.user)[0][0]
if count > max_reports_per_user:
frappe.throw(_('Only {0} emailed reports are allowed per user').format(max_reports_per_user))

def send(self):
report = frappe.get_doc('Report', self.report)
data = report.get_data(limit=500, user = self.user, filters = self.filters)

message = '<p>{0}</p>'.format(_('{0} generated on {1}').format(self.name,
frappe.utils.format_datetime(frappe.utils.now_datetime())))
attachments = None

if self.report_format == 'HTML':
message += self.get_html_table(data)

if self.report_format == 'XLS':
attachments.append(self.get_xls())

frappe.sendmail(
recipients = self.emails.split(),
subject = self.name,
message = message,
attachments = attachments
)

def get_html_table(self, data):
return ''

def get_csv(self, data):
return

def get_xls(self, data):
return

def send_daily(self):
'''Check reports to be sent daily'''
now = frappe.utils.now_datetime()
for report in frappe.get_all('Auto Email Report', {'enabled': 1, 'frequency': ('in', ('Daily', 'Weekly'))}):
auto_email_report = frappe.get_doc('Auto Email Report', report.name)
if auto_email_report.frequency=='Weekly':
# if not correct weekday, skip
if now.weekday()!={'Monday':0,'Tuesday':1,'Wednesday':2,
'Thursday':3,'Friday':4,'Saturday':5,'Sunday':6}[auto_email_report.weekday]:
continue

auto_email_report.send()

def send_monthly(self):
'''Check reports to be sent monthly'''
for report in frappe.get_all('Auto Email Report', {'enabled': 1, 'frequency': 'Monthly'}):
frappe.get_doc('Auto Email Report', report.name).send()

+ 12
- 0
frappe/email/doctype/auto_email_report/test_auto_email_report.py Wyświetl plik

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest, json

# test_records = frappe.get_test_records('Auto Email Report')

class TestAutoEmailReport(unittest.TestCase):
pass

+ 4
- 0
frappe/hooks.py Wyświetl plik

@@ -142,12 +142,16 @@ scheduler_events = {
"frappe.utils.scheduler.disable_scheduler_on_expiry",
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant",
"frappe.limits.update_space_usage",
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily"
],
"daily_long": [
"frappe.integrations.doctype.dropbox_backup.dropbox_backup.take_backups_daily"
],
"weekly_long": [
"frappe.integrations.doctype.dropbox_backup.dropbox_backup.take_backups_weekly"
],
"monthly": [
"frappe.email.doctype.auto_email_report.auto_email_report.send_monthly"
]

}


+ 2
- 2
frappe/public/js/frappe/ui/dialog.js Wyświetl plik

@@ -50,7 +50,7 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({
cur_dialog = null;
}
}
me.onhide && me.onhide();
me.onhide && me.onhide.apply(me);
})
.on("shown.bs.modal", function() {
// focus on first input
@@ -86,7 +86,7 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({
.html(label)
.click(function() {
me.primary_action_fulfilled = true;
click();
click.apply(me);
});
},
make_head: function() {


+ 4
- 1
frappe/public/js/frappe/ui/field_group.js Wyświetl plik

@@ -12,6 +12,9 @@ frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({
f.fieldname = f.label.replace(/ /g, "_").toLowerCase();
}
})
if(this.values) {
this.set_values(this.values);
}
},
make: function() {
var me = this;
@@ -32,7 +35,7 @@ frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({
$(this.body).find('input').on('change', function() {
me.refresh_dependency();
})
$(this.body).find('select').on("change", function() {
me.refresh_dependency();
})


+ 2
- 2
frappe/public/js/frappe/views/reports/query_report.js Wyświetl plik

@@ -272,10 +272,10 @@ frappe.views.QueryReport = Class.extend({
frappe.route_options = null;
},
set_filters_by_name: function() {
this.filters_by_name = {};
frappe.query_report_filters_by_name = {};

for(var i in this.filters) {
this.filters_by_name[this.filters[i].df.fieldname] = this.filters[i];
frappe.query_report_filters_by_name[this.filters[i].df.fieldname] = this.filters[i];
}
},
refresh: function() {


Ładowanie…
Anuluj
Zapisz