diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index e1097a647c..39a82ad7a2 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -364,6 +364,29 @@ class BaseDocument(object):
return format_value(self.get(fieldname), self.meta.get_field(fieldname),
doc=doc or self, currency=currency)
+ def get_print_template(self, fieldname, parent_doc=None):
+ """Returns print template for given fieldname if specified in controller
+ or parent controller.
+
+ Templates must be specified as:
+
+ class MyDocType(Document):
+ def __setup__(self):
+ self.print_templates = {
+ "[fieldname]": "templates/includes/template_name.html",
+ "[table fieldname].[fieldname]": "templates/includes/template_name.html"
+ }
+
+ :param fieldname: Field for which template is queried.
+ :param parent_doc: Parent Document, if child doc."""
+ src = self
+ if parent_doc:
+ src = parent_doc
+ fieldname = self.parentfield + "." + fieldname
+ if hasattr(src, "print_templates"):
+ return src.print_templates.get(fieldname)
+
+
def _filter(data, filters, limit=None):
"""pass filters as:
{"key": "val", "key": ["!=", "val"],
diff --git a/frappe/model/meta.py b/frappe/model/meta.py
index f020e94510..5f5a13b550 100644
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -213,7 +213,7 @@ class Meta(Document):
def is_print_hide(self, fieldname):
df = self.get_field(fieldname)
- return df.get("__print_hide") or df.print_hide
+ return df and (df.get("__print_hide") or df.print_hide)
doctype_table_fields = [
frappe._dict({"fieldname": "fields", "options": "DocField"}),
diff --git a/frappe/print/page/print_format_builder/print_format_builder.css b/frappe/print/page/print_format_builder/print_format_builder.css
index 204acaac2b..98e3b69131 100644
--- a/frappe/print/page/print_format_builder/print_format_builder.css
+++ b/frappe/print/page/print_format_builder/print_format_builder.css
@@ -3,13 +3,16 @@
margin: 0px;
margin-bottom: 15px;
}
-.print-format-builder-add-section {
+.print-format-builder-add-section, .print-format-builder-header {
border: 1px dashed #d1d8dd;
- text-align: center;
padding: 15px;
cursor: pointer;
}
+.print-format-builder-header {
+ margin-bottom: 15px;
+}
+
.print-format-builder-column {
padding: 15px;
margin: 0px -15px 0 -16px;
diff --git a/frappe/print/page/print_format_builder/print_format_builder.js b/frappe/print/page/print_format_builder/print_format_builder.js
index 23265f6d65..7fdca1afe5 100644
--- a/frappe/print/page/print_format_builder/print_format_builder.js
+++ b/frappe/print/page/print_format_builder/print_format_builder.js
@@ -166,12 +166,19 @@ frappe.PrintFormatBuilder = Class.extend({
.appendTo(this.page.main);
this.setup_sortable();
this.setup_add_section();
+ this.setup_edit_heading();
},
prepare_data: function() {
this.data = JSON.parse(this.print_format.format_data || "[]");
if(!this.data.length) {
// new layout
this.data = this.meta.fields;
+ } else {
+ // extract print_heading_template if found
+ if(this.data[0].fieldname==="print_heading_template") {
+ this.print_heading_template = this.data[0].options;
+ this.data = this.data.splice(1);
+ }
}
this.layout_data = [];
var section = null, column = null, me = this;
@@ -242,7 +249,7 @@ frappe.PrintFormatBuilder = Class.extend({
!_f.print_hide && f.label) {
// column names set as fieldname|width
- f.visible_columns.push({fieldname: _f.fieldname, width: _f.width});
+ f.visible_columns.push({fieldname: _f.fieldname, print_width: (_f.width || "")});
}
});
}
@@ -412,6 +419,14 @@ frappe.PrintFormatBuilder = Class.extend({
me.setup_sortable_for_column($section.find(".print-format-builder-column").get(0));
});
},
+ setup_edit_heading: function() {
+ var me = this;
+ this.page.main.find(".edit-heading").on("click", function() {
+ var $heading = $(this).parents(".print-format-builder-header:first")
+ .find(".print-format-builder-print-heading");
+ var d = me.get_edit_html_dialog(__("Edit Heading"), __("Heading"), $heading);
+ })
+ },
setup_column_selector: function() {
var me = this;
this.page.main.on("click", ".select-columns", function() {
@@ -424,7 +439,7 @@ frappe.PrintFormatBuilder = Class.extend({
$.each(columns, function(i, v) {
var parts = v.split("|");
- widths[parts[0]] = parts[1];
+ widths[parts[0]] = parts[1] || "";
});
var d = new frappe.ui.Dialog({
@@ -473,7 +488,7 @@ frappe.PrintFormatBuilder = Class.extend({
});
},
get_visible_columns_string: function(f) {
- return $.map(f.visible_columns, function(v) { return v.fieldname + "|" + v.width }).join(",")
+ return $.map(f.visible_columns, function(v) { return v.fieldname + "|" + (v.print_width || "") }).join(",")
},
get_no_content: function() {
return '
'
@@ -481,38 +496,48 @@ frappe.PrintFormatBuilder = Class.extend({
setup_edit_custom_html: function() {
var me = this;
this.page.main.on("click", ".edit-html", function() {
- var parent = $(this).parents(".print-format-builder-field:first"),
- $content = parent.find(".html-content");
-
- var d = new frappe.ui.Dialog({
- title: __("Edit Custom HTML"),
+ me.get_edit_html_dialog(__("Edit Custom HTML"), __("Custom HTML"),
+ $(this).parents(".print-format-builder-field:first").find(".html-content"));
+ });
+ },
+ get_edit_html_dialog: function(title, label, $content) {
+ var d = new frappe.ui.Dialog({
+ title: title,
fields: [
{
fieldname: "content",
fieldtype: "Text Editor",
- label: "Custom HTML"
+ label: label
}
]
});
- var content = $content.html();
- if(content.indexOf("data-no-content")!==-1) content = "";
+ // set existing content in input
+ content = $content.html();
+ if(content.indexOf("data-no-content")!==-1) content = "";
+ d.set_input("content", content);
- d.set_input("content", content);
-
- d.set_primary_action(__("Update"), function() {
- $content.html(d.get_value("content"));
- d.hide();
- });
-
- d.show();
-
- return false;
+ d.set_primary_action(__("Update"), function() {
+ $content.html(d.get_value("content"));
+ d.hide();
});
+
+ d.show();
+
+ return d;
},
save_print_format: function() {
var data = [],
me = this;
+
+ // add print heading as the first field
+ // this will be removed and set as a doc property
+ // before rendering
+ data.push({"fieldname": "print_heading_template",
+ fieldtype:"HTML",
+ options: this.page.main.find(".print-format-builder-print-heading").html()});
+
+ // add pages
this.page.main.find(".print-format-builder-section").each(function() {
data.push({"fieldtype": "Section Break"});
$(this).find(".print-format-builder-column").each(function() {
@@ -530,7 +555,7 @@ frappe.PrintFormatBuilder = Class.extend({
$.each(columns, function(i, c) {
var parts = c.split("|");
df.visible_columns.push({fieldname:parts[0],
- width:parts[1]});
+ print_width:parts[1]});
});
}
if(fieldtype==="Custom HTML") {
diff --git a/frappe/print/page/print_format_builder/print_format_builder_column_selector.html b/frappe/print/page/print_format_builder/print_format_builder_column_selector.html
index 6cdb523dc2..f6b645aac9 100644
--- a/frappe/print/page/print_format_builder/print_format_builder_column_selector.html
+++ b/frappe/print/page/print_format_builder/print_format_builder_column_selector.html
@@ -21,7 +21,7 @@
diff --git a/frappe/print/page/print_format_builder/print_format_builder_layout.html b/frappe/print/page/print_format_builder/print_format_builder_layout.html
index e1d1c6e2f5..713bf1643a 100644
--- a/frappe/print/page/print_format_builder/print_format_builder_layout.html
+++ b/frappe/print/page/print_format_builder/print_format_builder_layout.html
@@ -2,13 +2,23 @@
{%= __("Drag elements from the sidebar to add. Drag them back to trash.") %}
+
{% for(var i=0; i < data.length; i++) { %}
{%= frappe.render_template("print_format_builder_section",
{section: data[i], me:me}) %}
{% } %}
-
+
diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js
index f0ef2d2e4d..9f0e63a175 100644
--- a/frappe/public/js/frappe/router.js
+++ b/frappe/public/js/frappe/router.js
@@ -9,7 +9,7 @@ frappe.route_titles = {};
frappe.route_history = [];
frappe.view_factory = {};
frappe.view_factories = [];
-frappe.route_options = {};
+frappe.route_options = null;
frappe.route = function() {
if(frappe.re_route[window.location.hash]) {
diff --git a/frappe/public/js/frappe/ui/editor.js b/frappe/public/js/frappe/ui/editor.js
index 12d6b12126..433884f603 100644
--- a/frappe/public/js/frappe/ui/editor.js
+++ b/frappe/public/js/frappe/ui/editor.js
@@ -375,11 +375,8 @@ bsEditorToolbar = Class.extend({
// edit html
this.toolbar.find(".btn-html").on("click", function() {
- if(!window.bs_html_editor)
- window.bs_html_editor = new bsHTMLEditor();
-
- window.bs_html_editor.show(me.editor);
- })
+ new bsHTMLEditor().show(me.editor);
+ });
},
update: function () {
@@ -457,10 +454,9 @@ bsHTMLEditor = Class.extend({
var me = this;
this.modal = bs_get_modal("
Edit HTML", '
\
-
');
- this.modal.addClass("frappe-ignore-click");
- this.modal.find(".btn-primary").on("click", function() {
+ ');
+ this.modal.addClass("frappe-ignore-click");
+ this.modal.find(".btn-primary").removeClass("hide").html(__("Update")).on("click", function() {
me._html = me.modal.find("textarea").val();
$.each(me.editor.dataurls, function(key, val) {
@@ -476,7 +472,7 @@ bsHTMLEditor = Class.extend({
show: function(editor) {
var me = this;
this.editor = editor;
- this.modal.modal("show")
+ this.modal.modal("show");
var html = me.editor.html();
// pack dataurls so that html display is faster
this.editor.dataurls = {}
diff --git a/frappe/templates/pages/print.py b/frappe/templates/pages/print.py
index 8c21c63bc3..c85e449d85 100644
--- a/frappe/templates/pages/print.py
+++ b/frappe/templates/pages/print.py
@@ -142,8 +142,22 @@ def get_print_format(doctype, print_format):
frappe.TemplateNotFoundError)
def make_layout(doc, meta, format_data=None):
+ """Builds a hierarchical layout object from the fields list to be rendered
+ by `standard.html`
+
+ :param doc: Document to be rendered.
+ :param meta: Document meta object (doctype).
+ :param format_data: Fields sequence and properties defined by Print Format Builder."""
layout, page = [], []
layout.append(page)
+
+ if format_data:
+ # extract print_heading_template from the first field
+ # and remove the field
+ if format_data[0].get("fieldname") == "print_heading_template":
+ doc.print_heading_template = format_data[0].get("options")
+ format_data = format_data[1:]
+
for df in format_data or meta.fields:
if format_data:
# embellish df with original properties
@@ -234,11 +248,19 @@ def get_print_style(style=None):
return css
-def get_visible_columns(data, table_meta):
+def get_visible_columns(data, table_meta, df):
+ """Returns list of visible columns based on print_hide and if all columns have value."""
columns = []
- for tdf in table_meta.fields:
- if is_visible(tdf) and column_has_value(data, tdf.fieldname):
- columns.append(tdf)
+ if df.get("visible_columns"):
+ # columns specified by column builder
+ for col_df in df.get("visible_columns"):
+ newdf = table_meta.get_field(col_df.get("fieldname")).as_dict().copy()
+ newdf.update(col_df)
+ columns.append(newdf)
+ else:
+ for col_df in table_meta.fields:
+ if is_visible(col_df) and column_has_value(data, col_df.get("fieldname")):
+ columns.append(col_df)
return columns
diff --git a/frappe/templates/print_formats/standard_macros.html b/frappe/templates/print_formats/standard_macros.html
index a0d46ffd0d..d4493e2dff 100644
--- a/frappe/templates/print_formats/standard_macros.html
+++ b/frappe/templates/print_formats/standard_macros.html
@@ -13,13 +13,13 @@
{%- macro render_table(df, doc) -%}
{%- set table_meta = frappe.get_meta(df.options) -%}
{%- set data = doc.get(df.fieldname)[df.start:df.end] -%}
- {%- if doc.table_print_templates and
- doc.table_print_templates.get(df.fieldname) -%}
- {% include doc.table_print_templates[df.fieldname] %}
+ {%- if doc.print_templates and
+ doc.print_templates.get(df.fieldname) -%}
+ {% include doc.print_templates[df.fieldname] %}
{%- else -%}
{%- if data -%}
{%- set visible_columns = get_visible_columns(doc.get(df.fieldname),
- table_meta) -%}
+ table_meta, df) -%}
@@ -36,7 +36,8 @@
| {{ d.idx }} |
{% for tdf in visible_columns %}
- {{ print_value(tdf, d, doc) }} |
+
+ {{ print_value(tdf, d, doc) }} |
{% endfor %}
{% endfor %}
@@ -76,10 +77,14 @@
{%- endmacro -%}
{%- macro print_value(df, doc, parent_doc=None) -%}
- {% if df.fieldtype=="Check" %}
+ {% if doc.get_print_template(df.fieldname, parent_doc) %}
+ {% include doc.get_print_template(df.fieldname, parent_doc) %}
+ {% elif df.fieldtype=="Check" %}
{% elif df.fieldtype=="Image" %}
+ {% elif df.fieldtype=="HTML" %}
+ {{ frappe.render_template(df.options, {"doc":doc}) }}
{% else %}
{{ doc.get_formatted(df.fieldname, parent_doc or doc) }}
{% endif %}
@@ -101,19 +106,26 @@
{% if letter_head and not no_letterhead %}
{{ letter_head }}
{% endif %}
-
-
{{ doc.select_print_heading or (doc.print_heading if doc.print_heading != None
- else _(doc.doctype)) }}
- {{ doc.sub_heading if doc.sub_heading != None
- else ("#" + doc[doc.meta.title_field or "name"]) }}
-
+ {% if doc.print_heading_template %}
+ {{ frappe.render_template(doc.print_heading_template, {"doc":doc}) }}
+ {% else %}
+
+
{{ doc.select_print_heading or (doc.print_heading if doc.print_heading != None
+ else _(doc.doctype)) }}
+ {{ doc.sub_heading if doc.sub_heading != None
+ else doc.name }}
+
+
+ {% endif %}
{%- if doc.meta.is_submittable and doc.docstatus==0-%}
-
{{ _("DRAFT") }}
+ {{ _("DRAFT") }}
+
{%- endif -%}
{%- if doc.meta.is_submittable and doc.docstatus==2-%}
-
{{ _("CANCELLED") }}
+ {{ _("CANCELLED") }}
+
{%- endif -%}
{% if max_pages > 1 %}
{{ _("Page #{0} of {1}").format(page_num, max_pages) }}
diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py
index 78579c8574..254a6f576c 100644
--- a/frappe/utils/jinja.py
+++ b/frappe/utils/jinja.py
@@ -67,7 +67,8 @@ def get_allowed_functions_for_jenv():
"date_format": frappe.db.get_default("date_format") or "yyyy-mm-dd",
"get_fullname": frappe.utils.get_fullname,
"get_gravatar": frappe.utils.get_gravatar,
- "full_name": hasattr(frappe.local, "session") and frappe.local.session.data.full_name or "Guest"
+ "full_name": hasattr(frappe.local, "session") and frappe.local.session.data.full_name or "Guest",
+ "render_template": frappe.render_template
},
"autodoc": {
"get_version": get_version,