ソースを参照

[complete] auto email report

version-14
Rushabh Mehta 8年前
コミット
541d8be95f
13個のファイルの変更189行の追加60行の削除
  1. +3
    -3
      frappe/__init__.py
  2. +1
    -0
      frappe/core/doctype/report/report.py
  3. +20
    -0
      frappe/email/doctype/auto_email_report/auto_email_report.js
  4. +2
    -2
      frappe/email/doctype/auto_email_report/auto_email_report.json
  5. +61
    -22
      frappe/email/doctype/auto_email_report/auto_email_report.py
  6. +1
    -1
      frappe/model/document.py
  7. +5
    -5
      frappe/public/html/print_template.html
  8. +20
    -24
      frappe/public/js/frappe/views/reports/print_grid.html
  9. +2
    -0
      frappe/public/js/lib/microtemplate.js
  10. +22
    -0
      frappe/templates/includes/print_table.html
  11. +19
    -3
      frappe/utils/formatters.py
  12. +1
    -0
      frappe/utils/jinja.py
  13. +32
    -0
      frappe/utils/xlsutils.py

+ 3
- 3
frappe/__init__.py ファイルの表示

@@ -1128,13 +1128,13 @@ def get_test_records(doctype):
else: else:
return [] return []


def format_value(value, df, doc=None, currency=None):
def format_value(*args, **kwargs):
"""Format value with given field properties. """Format value with given field properties.


:param value: Value to be formatted. :param value: Value to be formatted.
:param df: DocField object with properties `fieldtype`, `options` etc."""
:param df: (Optional) DocField object with properties `fieldtype`, `options` etc."""
import frappe.utils.formatters import frappe.utils.formatters
return frappe.utils.formatters.format_value(value, df, doc, currency=currency)
return frappe.utils.formatters.format_value(*args, **kwargs)


def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None): def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None):
"""Get Print Format for given document. """Get Print Format for given document.


+ 1
- 0
frappe/core/doctype/report/report.py ファイルの表示

@@ -83,6 +83,7 @@ class Report(Document):


return out return out



@Document.whitelist @Document.whitelist
def toggle_disable(self, disable): def toggle_disable(self, disable):
self.db_set("disabled", cint(disable)) self.db_set("disabled", cint(disable))

+ 20
- 0
frappe/email/doctype/auto_email_report/auto_email_report.js ファイルの表示

@@ -20,6 +20,26 @@ frappe.ui.form.on('Auto Email Report', {
frm.trigger('show_filters'); frm.trigger('show_filters');
} }
} }
if(!frm.is_new()) {
frm.add_custom_button(__('Download'), function() {
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/frappe.email.doctype.auto_email_report.auto_email_report.download?"
+"name="+encodeURIComponent(frm.doc.name)));
if(!w) {
msgprint(__("Please enable pop-ups")); return;
}
});
frm.add_custom_button(__('Send Now'), function() {
frappe.call({
method: 'frappe.email.doctype.auto_email_report.auto_email_report.send_now',
args: {name: frm.doc.name},
callback: function() {
msgprint(__('Scheduled to send'));
}
});
});
}
}, },
show_filters: function(frm) { show_filters: function(frm) {
var wrapper = $(frm.get_field('filters_display').wrapper); var wrapper = $(frm.get_field('filters_display').wrapper);


+ 2
- 2
frappe/email/doctype/auto_email_report/auto_email_report.json ファイルの表示

@@ -294,7 +294,7 @@
"label": "Format", "label": "Format",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "HTML\nXLS\nCSV",
"options": "XLS\nHTML\nCSV",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -369,7 +369,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-09-01 05:36:00.898683",
"modified": "2016-09-14 02:00:21.618956",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Email", "module": "Email",
"name": "Auto Email Report", "name": "Auto Email Report",


+ 61
- 22
frappe/email/doctype/auto_email_report/auto_email_report.py ファイルの表示

@@ -7,6 +7,8 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
import frappe.utils import frappe.utils
from frappe.utils.xlsutils import get_xls
from frappe.utils.csvutils import to_csv


max_reports_per_user = 3 max_reports_per_user = 3


@@ -20,16 +22,16 @@ class AutoEmailReport(Document):


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


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


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


def validate_report_count(self): def validate_report_count(self):
'''check that there are only 3 enabled reports per user''' '''check that there are only 3 enabled reports per user'''
@@ -37,48 +39,85 @@ class AutoEmailReport(Document):
if count > max_reports_per_user: if count > max_reports_per_user:
frappe.throw(_('Only {0} emailed reports are allowed per user').format(max_reports_per_user)) frappe.throw(_('Only {0} emailed reports are allowed per user').format(max_reports_per_user))


def send(self):
def get_report_content(self):
'''Returns file in for the report in given format'''
report = frappe.get_doc('Report', self.report) report = frappe.get_doc('Report', self.report)
data = report.get_data(limit=500, user = self.user, filters = self.filters)
raw = report.get_data(limit=500, user = self.user, filters = self.filters)

if self.format == 'HTML':
return self.get_html_table(raw)

elif self.format == 'XLS':
return get_xls(raw)

elif self.format == 'CSV':
return to_csv(raw)

else:
frappe.throw(_('Invalid Output Format'))

def get_html_table(self, data):
return frappe.render_template('frappe/templates/includes/print_table.html', {
'headings': data[0],
'data': data[1:]
})


def get_file_name(self):
return "{0}.{1}".format(self.report.replace(" ", "-").replace("/", "-"), self.format.lower())

def send(self):
data = self.get_report_content()
message = '<p>{0}</p>'.format(_('{0} generated on {1}').format(self.name, message = '<p>{0}</p>'.format(_('{0} generated on {1}').format(self.name,
frappe.utils.format_datetime(frappe.utils.now_datetime()))) 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())
if self.format=='HTML':
message += '<hr>' + data
else:
attachments = [{
'fname': self.get_file_name(),
'fcontent': data
}]


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


def get_html_table(self, data):
return ''
@frappe.whitelist()
def download(name):
'''Download report locally'''
auto_email_report = frappe.get_doc('Auto Email Report', name)
auto_email_report.check_permission()
data = auto_email_report.get_report_content()


def get_csv(self, data):
return
frappe.local.response.filecontent = data
frappe.local.response.type = "download"
frappe.local.response.filename = auto_email_report.get_file_name()


def get_xls(self, data):
return
@frappe.whitelist()
def send_now(name):
'''Send Auto Email report now'''
auto_email_report = frappe.get_doc('Auto Email Report', name)
auto_email_report.check_permission()
auto_email_report.send()


def send_daily(): def send_daily():
'''Check reports to be sent daily''' '''Check reports to be sent daily'''
now = frappe.utils.now_datetime() now = frappe.utils.now_datetime()
for report in frappe.get_all('Auto Email Report', {'enabled': 1, 'frequency': ('in', ('Daily', 'Weekly'))}):
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) auto_email_report = frappe.get_doc('Auto Email Report', report.name)

# if not correct weekday, skip
if auto_email_report.frequency=='Weekly': if auto_email_report.frequency=='Weekly':
# if not correct weekday, skip
if now.weekday()!={'Monday':0,'Tuesday':1,'Wednesday':2, if now.weekday()!={'Monday':0,'Tuesday':1,'Wednesday':2,
'Thursday':3,'Friday':4,'Saturday':5,'Sunday':6}[auto_email_report.weekday]: 'Thursday':3,'Friday':4,'Saturday':5,'Sunday':6}[auto_email_report.weekday]:
continue continue


auto_email_report.send()
auto_email_report.send()



def send_monthly(): def send_monthly():
'''Check reports to be sent monthly''' '''Check reports to be sent monthly'''


+ 1
- 1
frappe/model/document.py ファイルの表示

@@ -136,7 +136,7 @@ class Document(BaseDocument):
self.latest = frappe.get_doc(self.doctype, self.name) self.latest = frappe.get_doc(self.doctype, self.name)
return self.latest return self.latest


def check_permission(self, permtype, permlabel=None):
def check_permission(self, permtype='read', permlabel=None):
"""Raise `frappe.PermissionError` if not permitted""" """Raise `frappe.PermissionError` if not permitted"""
if not self.has_permission(permtype): if not self.has_permission(permtype):
self.raise_no_permission_to(permlabel or permtype) self.raise_no_permission_to(permlabel or permtype)


+ 5
- 5
frappe/public/html/print_template.html ファイルの表示

@@ -6,18 +6,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<title>{%= title %}</title>
<link href="{%= frappe.urllib.get_base_url() %}/assets/frappe/css/bootstrap.css" rel="stylesheet">
<title>{{ title }}</title>
<link href="{{ base_url }}/assets/frappe/css/bootstrap.css" rel="stylesheet">
<link type="text/css" rel="stylesheet" <link type="text/css" rel="stylesheet"
href="{%= frappe.urllib.get_base_url() %}/assets/frappe/css/font-awesome.css">
href="{{ base_url }}/assets/frappe/css/font-awesome.css">
<style> <style>
{%= frappe.boot.print_css %}
{{ print_css }}
</style> </style>
</head> </head>
<body> <body>
<div class="print-format-gutter"> <div class="print-format-gutter">
<div class="print-format"> <div class="print-format">
{%= content %}
{{ content }}
</div> </div>
</div> </div>
</body> </body>


+ 20
- 24
frappe/public/js/frappe/views/reports/print_grid.html ファイルの表示

@@ -1,41 +1,37 @@
<!-- title --> <!-- title -->
{% if(title) { %}
<h2>{%= __(title) %}</h2>
{% if title %}
<h2>{{ __(title) }}</h2>
<hr> <hr>
{% } %}
{{ endif }}
<table class="table table-bordered"> <table class="table table-bordered">
<!-- heading --> <!-- heading -->
<thead> <thead>
<tr> <tr>
{% var columns_length = columns.length;
for (var i=0; i < columns_length; i++) {
var col = columns[i]; %}
{% if(col.name && col._id !== "_check") { %}
<th style="min-width: {%= col.minWidth %}px"
{% if(col.docfield && in_list(["Float", "Currency", "Int"],
col.docfield.fieldtype)) { %}
{% for col in columns %}
{% if col.name && col._id !== "_check" %}
<th style="min-width: {{ col.minWidth }}px"
{% if col.docfield && in_list(["Float", "Currency", "Int"], col.docfield.fieldtype) %}
class="text-right" class="text-right"
{% } %}>{%= __(col.name) %}</th>
{% } %}
{% } %}
{% endif %}>{{ __(col.name) }}</th>
{% endif %}
{% endfor %}
</tr> </tr>
</thead> </thead>
<!-- body --> <!-- body -->
<tbody> <tbody>
{% for (var i=0, l= data.length; i < l; i++) {
var row = data[i]; %}
{% for row in data %}
<tr> <tr>
{% for(var c=0; c < columns_length; c++) { var col = columns[c];%}
{% if(col.name && col._id !== "_check") { %}
{% for col in columns %}
{% if col.name && col._id !== "_check" %}
{% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %} {% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %}
<td>{%= col.formatter
<td>{{ col.formatter
? col.formatter(i, c, value, col, row, true) ? col.formatter(i, c, value, col, row, true)
: value %}</td>
{% } %}
{% } %}
: value }}</td>
{% endif %}
{% endfor %}
</tr> </tr>
{% } %}
{% endfor %}
</tbody> </tbody>
</table> </table>

+ 2
- 0
frappe/public/js/lib/microtemplate.js ファイルの表示

@@ -96,6 +96,8 @@ frappe.render_grid = function(opts) {
} }


// render HTML wrapper page // render HTML wrapper page
opts.base_url = frappe.urllib.get_base_url();
opts.print_cess = frappe.boot.print_css;
var html = frappe.render_template("print_template", opts); var html = frappe.render_template("print_template", opts);


var w = window.open(); var w = window.open();


+ 22
- 0
frappe/templates/includes/print_table.html ファイルの表示

@@ -0,0 +1,22 @@
<table cellpadding=2px cellspacing=0 border=1px style='width:100%; border-collapse:collapse;'>
<thead>
<tr>
{% for col in headings %}
<th>
{{ col }}
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
{% for val in row %}
<td>
{{ frappe.format(val) }}
</td>
{% endfor %}
</td>
{% endfor %}
</tbody>
</table>

+ 19
- 3
frappe/utils/formatters.py ファイルの表示

@@ -3,13 +3,29 @@


from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import datetime
from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime
from frappe.model.meta import get_field_currency, get_field_precision from frappe.model.meta import get_field_currency, get_field_precision
import re import re


def format_value(value, df, doc=None, currency=None, translated=False):
# Convert dict to object if necessary
if (isinstance(df, dict)):
def format_value(value, df=None, doc=None, currency=None, translated=False):
'''Format value based on given fieldtype, document reference, currency reference.
If docfield info (df) is not given, it will try and guess based on the datatype of the value'''
if not df:
df = frappe._dict()
if isinstance(value, datetime.datetime):
df.fieldtype = 'Datetime'
elif isinstance(value, datetime.date):
df.fieldtype = 'Date'
elif isinstance(value, int):
df.fieldtype = 'Int'
elif isinstance(value, float):
df.fieldtype = 'Float'
else:
df.fieldtype = 'Data'

elif (isinstance(df, dict)):
# Convert dict to object if necessary
df = frappe._dict(df) df = frappe._dict(df)


if value is None: if value is None:


+ 1
- 0
frappe/utils/jinja.py ファイルの表示

@@ -78,6 +78,7 @@ def get_allowed_functions_for_jenv():
"frappe": { "frappe": {
"_": frappe._, "_": frappe._,
"get_url": frappe.utils.get_url, "get_url": frappe.utils.get_url,
'format': frappe.format_value,
"format_value": frappe.format_value, "format_value": frappe.format_value,
"format_date": frappe.utils.data.global_date_format, "format_date": frappe.utils.data.global_date_format,
"form_dict": getattr(frappe.local, 'form_dict', {}), "form_dict": getattr(frappe.local, 'form_dict', {}),


+ 32
- 0
frappe/utils/xlsutils.py ファイルの表示

@@ -0,0 +1,32 @@
from __future__ import unicode_literals

import frappe, xlwt, StringIO, datetime
from frappe import _

def get_xls(data):
'''Convert data to xls'''
stream = StringIO.StringIO()
workbook = xlwt.Workbook()
sheet = workbook.add_sheet(_("Sheet 1"))

# formats
dateformat = xlwt.easyxf(num_format_str=
(frappe.defaults.get_global_default("date_format") or "yyyy-mm-dd"))
bold = xlwt.easyxf('font: bold 1')

for i, row in enumerate(data):
for j, val in enumerate(row):
f = None
if isinstance(val, (datetime.datetime, datetime.date)):
f = dateformat
if i==0:
f = bold

if f:
sheet.write(i, j, val, f)
else:
sheet.write(i, j, val)

workbook.save(stream)
stream.seek(0)
return stream.read()

読み込み中…
キャンセル
保存