Parcourir la source

Fixes to print

version-14
Anand Doshi il y a 11 ans
Parent
révision
7087be6559
20 fichiers modifiés avec 190 ajouts et 84 suppressions
  1. +3
    -3
      frappe/__init__.py
  2. +28
    -25
      frappe/core/doctype/communication/communication.py
  3. +3
    -1
      frappe/core/doctype/print_settings/print_settings.json
  4. +1
    -1
      frappe/patches.txt
  5. +13
    -2
      frappe/patches/v4_1/enable_print_as_pdf.py
  6. +9
    -9
      frappe/public/css/bootstrap.css
  7. +39
    -11
      frappe/public/js/frappe/form/print.js
  8. +17
    -7
      frappe/public/js/frappe/views/communication.js
  9. +1
    -1
      frappe/public/js/legacy/print_format.js
  10. +27
    -10
      frappe/templates/pages/print.py
  11. +1
    -1
      frappe/templates/print_formats/standard.html
  12. +4
    -4
      frappe/templates/print_formats/standard_macros.html
  13. +3
    -1
      frappe/templates/styles/classic.css
  14. +10
    -1
      frappe/templates/styles/standard.css
  15. +10
    -2
      frappe/utils/__init__.py
  16. +1
    -1
      frappe/utils/email_lib/email_body.py
  17. +1
    -1
      frappe/utils/formatters.py
  18. +16
    -1
      frappe/utils/pdf.py
  19. +1
    -1
      frappe/utils/response.py
  20. +2
    -1
      frappe/website/render.py

+ 3
- 3
frappe/__init__.py Voir le fichier

@@ -683,13 +683,13 @@ def format_value(value, df, doc=None):
return frappe.utils.formatters.format_value(value, df, doc)

def get_print_format(doctype, name, print_format=None, style=None, as_pdf=False):
from frappe.website.render import render_page
from frappe.website.render import build_page
local.form_dict.doctype = doctype
local.form_dict.name = name
local.form_dict.format = print_format
local.form_dict.name = name
local.form_dict.style = style

html = render_page("print")
html = build_page("print")

if as_pdf:
print_settings = db.get_singles_dict("Print Settings")


+ 28
- 25
frappe/core/doctype/communication/communication.py Voir le fichier

@@ -34,7 +34,7 @@ class Communication(Document):
@frappe.whitelist()
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
sender=None, recipients=None, communication_medium="Email", send_email=False,
print_html=None, attachments='[]', send_me_a_copy=False, set_lead=True, date=None):
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, set_lead=True, date=None):

if doctype and name and not frappe.has_permission(doctype, "email", name):
raise frappe.PermissionError("You are not allowed to send emails related to: {doctype} {name}".format(
@@ -42,12 +42,12 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =

_make(doctype=doctype, name=name, content=content, subject=subject, sent_or_received=sent_or_received,
sender=sender, recipients=recipients, communication_medium=communication_medium, send_email=send_email,
print_html=print_html, attachments=attachments, send_me_a_copy=send_me_a_copy, set_lead=set_lead,
print_html=print_html, print_format=print_format, attachments=attachments, send_me_a_copy=send_me_a_copy, set_lead=set_lead,
date=date)

def _make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
sender=None, recipients=None, communication_medium="Email", send_email=False,
print_html=None, attachments='[]', send_me_a_copy=False, set_lead=True, date=None):
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, set_lead=True, date=None):

# add to Communication
sent_via = None
@@ -89,7 +89,7 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received

if send_email:
d = comm
send_comm_email(d, name, sent_via, print_html, attachments, send_me_a_copy)
send_comm_email(d, name, sent_via, print_html, print_format, attachments, send_me_a_copy)

@frappe.whitelist()
def get_customer_supplier(args=None):
@@ -110,7 +110,7 @@ def get_customer_supplier(args=None):
}
return {}

def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', send_me_a_copy=False):
def send_comm_email(d, name, sent_via=None, print_html=None, print_format=None, attachments='[]', send_me_a_copy=False):
footer = None


@@ -130,26 +130,8 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s
if send_me_a_copy:
mail.cc.append(frappe.db.get_value("User", frappe.session.user, "email"))

if print_html:
print_html = scrub_urls(print_html)

print_settings = frappe.get_singles_dict("Print Settings")
send_print_as_pdf = cint(print_settings.send_print_as_pdf)

if send_print_as_pdf:
try:
options = {
'page-size': print_settings.pdf_page_size or 'A4'
}
mail.add_pdf_attachment(name.replace(' ','').replace('/','-') + '.pdf', print_html,
options=options)
except Exception:
frappe.msgprint(_("Error generating PDF, attachment sent as HTML"))
send_print_as_pdf = 0

if not send_print_as_pdf:
mail.add_attachment(name.replace(' ','').replace('/','-') + '.html',
print_html, 'text/html')
if print_html or print_format:
attach_print(mail, sent_via, print_html, print_format)

for a in json.loads(attachments):
try:
@@ -159,6 +141,27 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s

send(mail)

def attach_print(mail, sent_via, print_html, print_format):
name = sent_via.name
if not print_html and print_format:
print_html = frappe.get_print_format(sent_via.doctype, sent_via.name, print_format)

print_settings = frappe.db.get_singles_dict("Print Settings")
send_print_as_pdf = cint(print_settings.send_print_as_pdf)

if send_print_as_pdf:
try:
mail.add_pdf_attachment(name.replace(' ','').replace('/','-') + '.pdf', print_html)
except Exception:
frappe.msgprint(_("Error generating PDF, attachment sent as HTML"))
frappe.errprint(frappe.get_traceback())
send_print_as_pdf = 0

if not send_print_as_pdf:
print_html = scrub_urls(print_html)
mail.add_attachment(name.replace(' ','').replace('/','-') + '.html',
print_html, 'text/html')

def set_portal_link(sent_via, comm):
"""set portal link in footer"""



+ 3
- 1
frappe/core/doctype/print_settings/print_settings.json Voir le fichier

@@ -11,6 +11,7 @@
"permlevel": 0
},
{
"default": "1",
"description": "Send Email Print Attachments as PDF (Recommended)",
"fieldname": "send_print_as_pdf",
"fieldtype": "Check",
@@ -32,6 +33,7 @@
"permlevel": 0
},
{
"default": "",
"fieldname": "print_style",
"fieldtype": "Select",
"in_list_view": 1,
@@ -48,7 +50,7 @@
],
"icon": "icon-cog",
"issingle": 1,
"modified": "2014-07-17 08:08:27.291811",
"modified": "2014-07-23 04:59:45.626239",
"modified_by": "Administrator",
"module": "Core",
"name": "Print Settings",


+ 1
- 1
frappe/patches.txt Voir le fichier

@@ -41,7 +41,7 @@ execute:frappe.reset_perms("User") #2014-06-13
execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17
frappe.patches.v4_0.remove_user_owner_custom_field
execute:frappe.delete_doc("DocType", "Website Template")
execute:frappe.reload_doc('website', 'doctype', 'website_route') #20114-06-17
execute:frappe.reload_doc('website', 'doctype', 'website_route') #2014-06-17
execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20
frappe.patches.v4_1.enable_outgoing_email_settings
execute:frappe.db.sql("""update `tabSingles` set `value`=`doctype` where `field`='name'""") #2014-07-04


+ 13
- 2
frappe/patches/v4_1/enable_print_as_pdf.py Voir le fichier

@@ -6,10 +6,21 @@ import frappe

def execute():
frappe.reload_doc("core", "doctype", "print_settings")
frappe.db.set_value("Print Settings", "Print Settings", "print_style", "Modern")
print_settings = frappe.get_doc("Print Settings")
print_settings.print_style = "Modern"

try:
import pdfkit
except ImportError:
pass
else:
frappe.db.set_value("Print Settings", "Print Settings", "send_print_as_pdf", 1)
# if someone has already configured in Outgoing Email Settings
outgoing_email_settings = frappe.db.get_singles_dict("Outgoing Email Settings")
if "send_print_as_pdf" in outgoing_email_settings:
print_settings.send_print_as_pdf = outgoing_email_settings.send_print_as_pdf
print_settings.pdf_page_size = outgoing_email_settings.pdf_page_size

else:
print_settings.send_print_as_pdf = 1

print_settings.save()

+ 9
- 9
frappe/public/css/bootstrap.css Voir le fichier

@@ -189,9 +189,9 @@ th {
}
@media print {
* {
color: #000 !important;
/* color: #000 !important;*/
text-shadow: none !important;
background: transparent !important;
/* background: transparent !important;*/
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
@@ -211,7 +211,7 @@ th {
}
pre,
blockquote {
border: 1px solid #999;
/* border: 1px solid #999;*/

page-break-inside: avoid;
}
@@ -241,24 +241,24 @@ th {
.navbar {
display: none;
}
.table td,
/* .table td,
.table th {
background-color: #fff !important;
}
.btn > .caret,
*/ .btn > .caret,
.dropup > .btn > .caret {
border-top-color: #000 !important;
}
.label {
/* .label {
border: 1px solid #000;
}
.table {
*/ .table {
border-collapse: collapse !important;
}
.table-bordered th,
/*.table-bordered th,
.table-bordered td {
border: 1px solid #ddd !important;
}
}*/
}
@font-face {
font-family: 'Glyphicons Halflings';


+ 39
- 11
frappe/public/js/frappe/form/print.js Voir le fichier

@@ -17,6 +17,7 @@ frappe.ui.form.PrintPreview = Class.extend({
<div class="checkbox"><label><input type="checkbox" class="print-letterhead" checked/> Letterhead</label></div></div>\
<div class="col-xs-6 text-right" style="padding-top: 7px;">\
<a style="margin-right: 7px;" class="btn-print-preview text-muted small">Preview</a>\
<a style="margin-right: 7px;" class="btn-download-pdf text-muted small">Download PDF</a>\
<strong><a style="margin-right: 7px;" class="btn-print-print">Print</a></strong>\
<a class="close">×</a>\
</div>\
@@ -42,11 +43,13 @@ frappe.ui.form.PrintPreview = Class.extend({
.find(".print-preview-select")
.on("change", function() {
if(me.is_old_style()) {
me.wrapper.find(".btn-download-pdf").toggle(false);
me.preview_old_style();
} else {
me.wrapper.find(".btn-download-pdf").toggle(true);
me.preview();
}
})
});

this.wrapper.find(".btn-print-print").click(function() {
if(me.is_old_style()) {
@@ -63,6 +66,18 @@ frappe.ui.form.PrintPreview = Class.extend({
me.new_page_preview();
}
});

this.wrapper.find(".btn-download-pdf").click(function() {
if(!me.is_old_style()) {
var w = window.open("/api/method/frappe.templates.pages.print.download_pdf?"
+"doctype="+encodeURIComponent(me.frm.doc.doctype)
+"&name="+encodeURIComponent(me.frm.doc.name)
+"&format="+me.selected_format());
if(!w) {
msgprint(__("Please enable pop-ups")); return;
}
}
});
},
preview: function() {
var me = this;
@@ -102,14 +117,24 @@ frappe.ui.form.PrintPreview = Class.extend({
});
},
preview_old_style: function() {
var me = this;
this.with_old_style({
format: me.print_sel.val(),
callback: function(html) {
me.wrapper.find(".print-format").html('<div class="alert alert-warning">'
+__("Warning: This Print Format is in old style and cannot be generated via the API.")
+'</div>'
+ html);
},
no_letterhead: !this.with_letterhead(),
only_body: true,
no_heading: true
});
},
with_old_style: function(opts) {
var me = this;
frappe.require("/assets/js/print_format_v3.min.js");
_p.build(me.print_sel.val(), function(html) {
me.wrapper.find(".print-format").html('<div class="alert alert-warning">'
+__("Warning: This Print Format is in old style and cannot be generated via the API.")
+'</div>'
+ html);
}, !this.with_letterhead(), true, true);
_p.build(opts.format, opts.callback, opts.no_letterhead, opts.only_body, opts.no_heading);
},
print_old_style: function() {
frappe.require("/assets/js/print_format_v3.min.js");
@@ -124,11 +149,14 @@ frappe.ui.form.PrintPreview = Class.extend({
selected_format: function() {
return this.print_sel.val();
},
is_old_style: function() {
return this.get_print_format().print_format_type==="Client"
is_old_style: function(format) {
return this.get_print_format(format).print_format_type==="Client";
},
get_print_format: function() {
var format = this.selected_format();
get_print_format: function(format) {
if (!format) {
format = this.selected_format();
}

if(locals["Print Format"] && locals["Print Format"][format]) {
return locals["Print Format"][format]
} else {


+ 17
- 7
frappe/public/js/frappe/views/communication.js Voir le fichier

@@ -264,8 +264,8 @@ frappe.views.CommunicationComposer = Class.extend({
if (cur_frm) {
$(fields.select_print_format.input)
.empty()
.add_options(cur_frm.print_formats)
.val(cur_frm.print_formats[0]);
.add_options(cur_frm.print_preview.print_formats)
.val(cur_frm.print_preview.print_formats[0]);
} else {
$(fields.attach_document_print.wrapper).toggle(false);
}
@@ -324,20 +324,29 @@ frappe.views.CommunicationComposer = Class.extend({
})

if(form_values.attach_document_print) {
_p.build(form_values.select_print_format || "", function(print_format_html) {
me.send_email(btn, form_values, selected_attachments, print_format_html);
});
if (cur_frm.print_preview.is_old_style(form_values.select_print_format || "")) {
cur_frm.print_preview.with_old_style({
format: form_values.select_print_format,
callback: function(print_html) {
me.send_email(btn, form_values, selected_attachments, print_html);
}
});
} else {
me.send_email(btn, form_values, selected_attachments, null, form_values.select_print_format || "");
}

} else {
me.send_email(btn, form_values, selected_attachments);
}
});
},

send_email: function(btn, form_values, selected_attachments, print_html) {
send_email: function(btn, form_values, selected_attachments, print_html, print_format) {
var me = this;

if(!form_values.attach_document_print) {
print_html = "";
print_html = null;
print_format = null;
}

if(form_values.send_email) {
@@ -362,6 +371,7 @@ frappe.views.CommunicationComposer = Class.extend({
send_me_a_copy: form_values.send_me_a_copy,
send_email: form_values.send_email,
print_html: print_html,
print_format: print_format,
communication_medium: form_values.communication_medium,
sent_or_received: form_values.sent_or_received,
attachments: selected_attachments


+ 1
- 1
frappe/public/js/legacy/print_format.js Voir le fichier

@@ -86,7 +86,7 @@ $.extend(_p, {

dialog.onshow = function() {
var $print = dialog.fields_dict.print_format.$input;
$print.empty().add_options(cur_frm.print_formats);
$print.empty().add_options(cur_frm.print_preview.print_formats);

if(cur_frm.$print_view_select && cur_frm.$print_view_select.val())
$print.val(cur_frm.$print_view_select.val());


+ 27
- 10
frappe/templates/pages/print.py Voir le fichier

@@ -3,12 +3,13 @@

from __future__ import unicode_literals

import frappe, os, copy, json
import frappe, os, copy, json, re
from frappe import _

from frappe.modules import get_doc_path
from jinja2 import TemplateNotFound
from frappe.utils import cint
from frappe.utils.pdf import get_pdf

no_cache = 1
no_sitemap = 1
@@ -29,13 +30,6 @@ def get_context(context):

doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name)

for ptype in ("read", "print"):
if not frappe.has_permission(doc.doctype, ptype, doc):
return {
"body": """<h1>Error</h1>
<p>No {ptype} permission</p>""".format(ptype=ptype)
}

meta = frappe.get_meta(doc.doctype)

return {
@@ -59,6 +53,8 @@ def get_html(doc, name=None, print_format=None, meta=None,
if isinstance(doc, basestring):
doc = frappe.get_doc(json.loads(doc))

validate_print_permission(doc)

if hasattr(doc, "before_print"):
doc.before_print()

@@ -88,6 +84,18 @@ def get_html(doc, name=None, print_format=None, meta=None,

return html

@frappe.whitelist()
def download_pdf(doctype, name, format=None):
html = frappe.get_print_format(doctype, name, format)
frappe.local.response.filename = "{name}.pdf".format(name=name.replace(" ", "-").replace("/", "-"))
frappe.local.response.filecontent = get_pdf(html)
frappe.local.response.type = "download"

def validate_print_permission(doc):
for ptype in ("read", "print"):
if not frappe.has_permission(doc.doctype, ptype, doc):
raise frappe.PermissionError(_("No {0} permission").format(ptype))

def get_letter_head(doc, no_letterhead):
if no_letterhead:
return ""
@@ -168,12 +176,21 @@ def is_visible(df):

def get_print_style(style=None):
if not style:
style = frappe.db.get_default("print_style") or "Standard"
style = frappe.db.get_single_value("Print Settings", "print_style") or "Standard"

css = frappe.get_template("templates/styles/standard.css").render()

try:
css += frappe.get_template("templates/styles/" + style.lower() + ".css").render()
additional_css = frappe.get_template("templates/styles/" + style.lower() + ".css").render()

# move @import to top
for at_import in list(set(re.findall("(@import url\([^\)]+\)[;]?)", additional_css))):
additional_css = additional_css.replace(at_import, "")

# prepend css with at_import
css = at_import + css

css += additional_css
except TemplateNotFound:
pass



+ 1
- 1
frappe/templates/print_formats/standard.html Voir le fichier

@@ -8,7 +8,7 @@
{% for section in page %}
<div class="row">
{% for column in section %}
<div class="col-sm-{{ (12 / section|len)|int }}">
<div class="col-xs-{{ (12 / section|len)|int }}">
{% for df in column %}
{{ render_field(df, doc) }}
{% endfor %}


+ 4
- 4
frappe/templates/print_formats/standard_macros.html Voir le fichier

@@ -49,13 +49,13 @@

{%- macro render_field_with_label(df, doc) -%}
<div class="row">
<div class="col-sm-5 text-right">
<div class="col-xs-5 text-right">
{% if df.fieldtype not in ("Image","HTML") and
doc.get(df.fieldname) != None %}
<label>{{ df.label }}</label>
{% endif %}
</div>
<div class="col-sm-7 {%- if df.fieldtype
<div class="col-xs-7 {%- if df.fieldtype
in ('Int', 'Check', 'Float', 'Currency') %} text-right{% endif %}">
{% if doc.get(df.fieldname) != None -%}
{{ print_value(df, doc) }}{% endif %}
@@ -75,9 +75,9 @@

{%- macro print_value(df, doc, parent_doc=None) -%}
{% if df.fieldtype=="Check" %}
<i class="{{ 'icon-check' if doc[df.fieldname] else 'icon-check-empty' }}"></i>
<i class="{{ 'icon-check' if doc[df.fieldname] else 'icon-check-empty' }}"></i>
{% elif df.fieldtype=="Image" %}
<img src="{{ doc[doc.meta.get_field(df.fieldname).options] }}" class="img-responsive">
<img src="{{ doc[doc.meta.get_field(df.fieldname).options] }}" class="img-responsive">
{% else %}
{{ doc.get_formatted(df.fieldname, parent_doc or doc) }}
{% endif %}


+ 3
- 1
frappe/templates/styles/classic.css Voir le fichier

@@ -1,3 +1,5 @@
@import url(http://fonts.googleapis.com/css?family=Noto+Serif:400,700);

/*
common style for whole page
This should include:
@@ -12,5 +14,5 @@
.print-format h2,
.print-format h3,
.print-format h4 {
font-family: serif;
font-family: 'Noto Serif', serif;
}

+ 10
- 1
frappe/templates/styles/standard.css Voir le fichier

@@ -1,3 +1,5 @@
@import url(http://fonts.googleapis.com/css?family=Noto+Sans:400,700);

@media screen {
.print-format-gutter {
background-color: #ddd;
@@ -26,8 +28,16 @@
}
}

@media print {
.print-format p {
margin-left: 1px;
margin-right: 1px;
}
}

.print-format {
font-size: 9pt;
-webkit-print-color-adjust:exact;
}

.page-break {
@@ -49,4 +59,3 @@
table.no-border, table.no-border td {
border: 0px;
}


+ 10
- 2
frappe/utils/__init__.py Voir le fichier

@@ -348,9 +348,17 @@ def fmt_money(amount, precision=None, currency=None):
"""
Convert to string with commas for thousands, millions etc
"""
number_format = frappe.db.get_default("number_format") or "#,###.##"
decimal_str, comma_str, precision = get_number_format_info(number_format)
number_format = None
if currency:
number_format = frappe.db.get_value("Currency", currency, "number_format")

if not number_format:
number_format = frappe.db.get_default("number_format") or "#,###.##"

decimal_str, comma_str, number_format_precision = get_number_format_info(number_format)

if not precision:
precision = number_format_precision

amount = '%.*f' % (precision, flt(amount))
if amount.find('.') == -1:


+ 1
- 1
frappe/utils/email_lib/email_body.py Voir le fichier

@@ -17,7 +17,7 @@ def get_email(recipients, sender='', msg='', subject='[No Subject]',
msg = markdown(msg)
emailobj.set_html(msg, text_content, footer=footer, print_html=print_html, formatted=formatted)

for attach in attachments:
for attach in (attachments or []):
emailobj.add_attachment(**attach)

return emailobj


+ 1
- 1
frappe/utils/formatters.py Voir le fichier

@@ -14,7 +14,7 @@ def format_value(value, df, doc=None):
currency=get_field_currency(df, doc) if doc else None)

elif df.fieldtype == "Float":
return fmt_money(value)
return fmt_money(value, precision=get_field_precision(df, doc))

elif df.fieldtype == "Percent":
return "{}%".format(flt(value, 2))


+ 16
- 1
frappe/utils/pdf.py Voir le fichier

@@ -2,13 +2,28 @@
# MIT License. See license.txt

import pdfkit, os, frappe
from frappe.utils import scrub_urls

def get_pdf(html, options=None):
if not options:
options = {}

options.update({
"print-media-type": None,
"background": None,
"images": None,
'margin-top': '15mm',
'margin-right': '15mm',
'margin-bottom': '15mm',
'margin-left': '15mm',
'encoding': "UTF-8",
'no-outline': None
})

if not options.get("page-size"):
options['page-size'] = frappe.df.get_single_value("Print Settings", "pdf_page_size") or "A4"
options['page-size'] = frappe.db.get_single_value("Print Settings", "pdf_page_size") or "A4"

html = scrub_urls(html)
fname = os.path.join("/tmp", frappe.generate_hash() + ".pdf")
pdfkit.from_string(html, fname, options=options or {})



+ 1
- 1
frappe/utils/response.py Voir le fichier

@@ -49,7 +49,7 @@ def as_csv():

def as_raw():
response = Response()
response.headers[b"Content-Type"] = mimetypes.guess_type(frappe.response['filename'])[0] or b"application/unknown"
response.headers[b"Content-Type"] = frappe.response.get("content_type") or mimetypes.guess_type(frappe.response['filename'])[0] or b"application/unknown"
response.headers[b"Content-Disposition"] = ("filename=%s" % frappe.response['filename'].replace(' ', '_')).encode("utf-8")
response.data = frappe.response['filecontent']
return response


+ 2
- 1
frappe/website/render.py Voir le fichier

@@ -3,6 +3,7 @@

from __future__ import unicode_literals
import frappe
from frappe.utils import cstr
import mimetypes, json
from werkzeug.wrappers import Response

@@ -49,7 +50,7 @@ def render(path, http_status_code=None):

def render_403(e):
path = "message"
frappe.local.message = "Did you log out?"
frappe.local.message = "<p><strong>{error}</strong></p><p>Did you log out?</p>".format(error=cstr(e))
frappe.local.message_title = "Not Permitted"
return render_page(path), e.http_status_code



Chargement…
Annuler
Enregistrer