@@ -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. | ||||
@@ -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,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); | ||||
@@ -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", | ||||
@@ -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''' | ||||
@@ -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) | ||||
@@ -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> | ||||
@@ -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> |
@@ -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(); | ||||
@@ -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> |
@@ -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: | ||||
@@ -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', {}), | ||||
@@ -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() |