[print-format] completed print format builder

This commit is contained in:
Rushabh Mehta 2015-01-30 17:07:45 +05:30
parent a6cbecefba
commit cf3074a20b
17 changed files with 267 additions and 94 deletions

View file

@ -856,7 +856,7 @@ def format_value(value, df, doc=None, currency=None):
import frappe.utils.formatters
return frappe.utils.formatters.format_value(value, df, doc, currency=currency)
def get_print_format(doctype, name, print_format=None, style=None, as_pdf=False):
def get_print(doctype, name, print_format=None, style=None, as_pdf=False):
"""Get Print Format for given document.
:param doctype: DocType of document.
@ -886,12 +886,12 @@ def attach_print(doctype, name, file_name):
if int(print_settings.send_print_as_pdf or 0):
return {
"fname": file_name + ".pdf",
"fcontent": get_print_format(doctype, name, as_pdf=True)
"fcontent": get_print(doctype, name, as_pdf=True)
}
else:
return {
"fname": file_name + ".html",
"fcontent": scrub_urls(get_print_format(doctype, name)).encode("utf-8")
"fcontent": scrub_urls(get_print(doctype, name)).encode("utf-8")
}
logging_setup_complete = False

View file

@ -42,7 +42,7 @@ def set_value(doctype, name, fieldname, value):
frappe.throw(_("Cannot edit standard fields"))
doc = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True)
if doc and doc.parent:
if doc and doc.parent and doc.parenttype:
doc = frappe.get_doc(doc.parenttype, doc.parent)
child = doc.getone({"doctype": doctype, "name": name})
child.set(fieldname, value)
@ -59,30 +59,26 @@ def set_value(doctype, name, fieldname, value):
return doc.as_dict()
@frappe.whitelist()
def insert(doclist):
if isinstance(doclist, basestring):
doclist = json.loads(doclist)
def insert(doc=None):
if isinstance(doc, basestring):
doc = json.loads(doc)
if isinstance(doclist, dict):
doclist = [doclist]
if doclist[0].get("parent") and doclist[0].get("parenttype"):
if doc.get("parent") and doc.get("parenttype"):
# inserting a child record
d = doclist[0]
doc = frappe.get_doc(d["parenttype"], d["parent"])
doc.append(d)
doc.save()
return [d]
parent = frappe.get_doc(doc.parenttype, doc.parent)
parent.append(doc)
parent.save()
return parent.as_dict()
else:
doc = frappe.get_doc(doclist).insert()
doc = frappe.get_doc(doc).insert()
return doc.as_dict()
@frappe.whitelist()
def save(doclist):
if isinstance(doclist, basestring):
doclist = json.loads(doclist)
def save(doc):
if isinstance(doc, basestring):
doc = json.loads(doc)
doc = frappe.get_doc(doclist).save()
doc = frappe.get_doc(doc).save()
return doc.as_dict()
@frappe.whitelist()
@ -91,14 +87,14 @@ def rename_doc(doctype, old_name, new_name, merge=False):
return new_name
@frappe.whitelist()
def submit(doclist):
if isinstance(doclist, basestring):
doclist = json.loads(doclist)
def submit(doc):
if isinstance(doc, basestring):
doc = json.loads(doc)
doclistobj = frappe.get_doc(doclist)
doclistobj.submit()
doc = frappe.get_doc(doc)
doc.submit()
return doclistobj.as_dict()
return doc.as_dict()
@frappe.whitelist()
def cancel(doctype, name):

View file

@ -143,6 +143,12 @@ def get_data():
"label": _("Printing"),
"icon": "icon-print",
"items": [
{
"type": "page",
"label": "Print Format Builder",
"name": "print-format-builder",
"description": _("Drag and Drop tool to build and customize Print Formats.")
},
{
"type": "doctype",
"name": "Print Settings",

View file

@ -187,7 +187,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
def attach_print(mail, parent_doc, print_html, print_format):
name = parent_doc.name if parent_doc else "attachment"
if (not print_html) and parent_doc and print_format:
print_html = frappe.get_print_format(parent_doc.doctype, parent_doc.name, print_format)
print_html = frappe.get_print(parent_doc.doctype, parent_doc.name, print_format)
print_settings = frappe.db.get_singles_dict("Print Settings")
send_print_as_pdf = cint(print_settings.send_print_as_pdf)

View file

@ -172,7 +172,7 @@ class BaseDocument(object):
if self.get(key):
doc[key] = self.get(key)
return doc
return frappe._dict(doc)
def as_json(self):
return json.dumps(self.as_dict(), indent=1, sort_keys=True)

View file

@ -88,10 +88,10 @@ class Meta(Document):
return { "fields": "DocField", "permissions": "DocPerm"}.get(fieldname)
def get_field(self, fieldname):
if not fieldname in self._fields:
fields = self.get("fields", {"fieldname":fieldname})
self._fields[fieldname] = fields[0] if fields else frappe._dict()
return self._fields[fieldname]
if not self._fields:
for f in self.get("fields"):
self._fields[f.fieldname] = f
return self._fields.get(fieldname)
def get_label(self, fieldname):
return self.get_field(fieldname).label

View file

@ -115,6 +115,14 @@
"permlevel": 0,
"precision": "",
"read_only": 0
},
{
"fieldname": "print_format_builder",
"fieldtype": "Check",
"hidden": 1,
"label": "Print Format Builder",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
@ -126,7 +134,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2015-01-28 00:42:25.948759",
"modified": "2015-01-30 02:15:31.686084",
"modified_by": "Administrator",
"module": "Print",
"name": "Print Format",

View file

@ -9,7 +9,7 @@ test_records = frappe.get_test_records('Print Format')
class TestPrintFormat(unittest.TestCase):
def test_print_user(self, style=None):
print_html = frappe.get_print_format("User", "Administrator", style=style)
print_html = frappe.get_print("User", "Administrator", style=style)
self.assertTrue("<label>First Name</label>" in print_html)
self.assertTrue(re.findall('<div class="col-xs-7[\s]*">[\s]*Administrator[\s]*</div>', print_html))
return print_html

View file

@ -3,10 +3,9 @@ frappe.pages['print-format-builder'].on_page_load = function(wrapper) {
}
frappe.pages['print-format-builder'].on_page_show = function(wrapper) {
// load a new page
var frm = frappe.route_options.print_format
if(!frm || !frappe.print_format_builder.doc ||
(frm.doc.name != frappe.print_format_builder.doc.name)) {
if(frappe.route_options) {
frappe.print_format_builder.print_format = frappe.route_options;
frappe.route_options = null;
frappe.print_format_builder.refresh();
}
}
@ -15,14 +14,13 @@ frappe.PrintFormatBuilder = Class.extend({
init: function(parent) {
this.parent = parent;
this.make();
this.refresh();
},
refresh: function() {
this.frm = frappe.route_options.print_format;
if(!(this.frm && this.frm.doc)) {
this.ask_user_to_start_from_print_format();
if(!this.print_format) {
this.show_start();
} else {
this.page.set_title(this.frm.doc.name);
this.page.set_title(this.print_format.name);
this.setup_print_format();
}
},
@ -31,22 +29,113 @@ frappe.PrintFormatBuilder = Class.extend({
parent: this.parent,
title: __("Print Format Builder")
});
// future-bindings for buttons on sections / fields
// bind only once
this.setup_section_settings();
this.setup_column_selector();
this.setup_edit_custom_html();
},
ask_user_to_start_from_print_format: function() {
this.page.main.html('<div class="padding"><a class="btn btn-default btn-sm" href="#List/Print Format">'
+__("Please create or edit a new Print Format")+'</a></div>');
show_start: function() {
this.page.main.html(frappe.render_template("print_format_builder_start", {}));
this.page.sidebar.html("");
this.page.clear_primary_action();
this.page.clear_actions();
this.page.set_title(__("Print Format Builder"));
this.start_edit_print_format();
this.start_new_print_format();
},
start_edit_print_format: function() {
// print format control
var me = this;
this.print_format_input = frappe.ui.form.make_control({
parent: this.page.main.find(".print-format-selector"),
df: {
fieldtype: "Link",
options: "Print Format",
filters: {
print_format_builder: 1
},
label: __("Select Print Format to Edit"),
only_select: true
},
render_input: true
});
// create a new print format.
this.page.main.find(".btn-edit-print-format").on("click", function() {
var name = me.print_format_input.get_value();
if(!name) return;
frappe.model.with_doc("Print Format", name, function(doc) {
me.print_format = frappe.get_doc("Print Format", name);
me.refresh();
});
});
},
start_new_print_format: function() {
var me = this;
this.doctype_input = frappe.ui.form.make_control({
parent: this.page.main.find(".doctype-selector"),
df: {
fieldtype: "Link",
options: "DocType",
filters: {
"istable": 0,
"issingle": 0
},
label: __("Select a DocType to make a new format")
},
render_input: true
});
this.name_input = frappe.ui.form.make_control({
parent: this.page.main.find(".name-selector"),
df: {
fieldtype: "Data",
label: __("Name of the new Print Format"),
},
render_input: true
});
this.page.main.find(".btn-new-print-format").on("click", function() {
var doctype = me.doctype_input.get_value(),
name = me.name_input.get_value();
if(!(doctype && name)) {
msgprint(__("Both DocType and Name required"));
return;
}
frappe.call({
method: "frappe.client.insert",
args: {
doc: {
doctype: "Print Format",
name: name,
standard: "No",
doc_type: doctype,
print_format_builder: 1
}
},
callback: function(r) {
me.print_format = r.message;
me.refresh();
}
});
});
},
setup_print_format: function() {
var me = this;
frappe.model.with_doctype(this.frm.doc.doc_type, function(doctype) {
me.meta = frappe.get_meta(me.frm.doc.doc_type);
frappe.model.with_doctype(this.print_format.doc_type, function(doctype) {
me.meta = frappe.get_meta(me.print_format.doc_type);
me.setup_sidebar();
me.render_layout();
me.page.set_primary_action(__("Save"), function() {
me.save_print_format();
});
me.page.set_secondary_action(__("Close"), function() {
me.print_format = null;
me.refresh();
});
});
},
setup_sidebar: function() {
@ -76,13 +165,10 @@ frappe.PrintFormatBuilder = Class.extend({
data: this.layout_data, me: this}))
.appendTo(this.page.main);
this.setup_sortable();
this.setup_section_settings();
this.setup_column_selector();
this.setup_edit_custom_html();
this.setup_add_section();
},
prepare_data: function() {
this.data = JSON.parse(this.frm.doc.format_data || "[]");
this.data = JSON.parse(this.print_format.format_data || "[]");
if(!this.data.length) {
// new layout
this.data = this.meta.fields;
@ -108,7 +194,7 @@ frappe.PrintFormatBuilder = Class.extend({
f.label = "Custom HTML";
f.fieldtype = "Custom HTML"
} else {
f = $.extend(frappe.meta.get_docfield(me.frm.doc.doc_type,
f = $.extend(frappe.meta.get_docfield(me.print_format.doc_type,
f.fieldname) || {}, f);
}
}
@ -201,7 +287,7 @@ frappe.PrintFormatBuilder = Class.extend({
if(fieldname==="_custom_html") {
var field = me.get_custom_html_field();
} else {
var field = frappe.meta.get_docfield(me.frm.doc.doc_type, fieldname);
var field = frappe.meta.get_docfield(me.print_format.doc_type, fieldname);
}
$item.replaceWith(frappe.render_template("print_format_builder_field",
@ -456,7 +542,20 @@ frappe.PrintFormatBuilder = Class.extend({
});
});
});
this.frm.doc.format_data = JSON.stringify(data);
setTimeout(function() { me.frm.save(); }, 100);
// save format_data
frappe.call({
method: "frappe.client.set_value",
args: {
doctype: "Print Format",
name: this.print_format.name,
fieldname: "format_data",
value: JSON.stringify(data),
},
callback: function(r) {
me.print_format = r.message;
msgprint(__("Saved"));
}
});
}
});

View file

@ -0,0 +1,18 @@
<div class="padding">
<p class="text-muted">{%= __("Select an existing format to edit or start a new format.") %}</p>
</div>
<div class="padding" style="max-width: 300px;">
<div class="print-format-selector"></div>
<p>
<button class="btn btn-sm btn-default btn-edit-print-format">
{%= __("Edit") %}</button>
</p>
</div>
<div class="padding" style="max-width: 300px;">
<div class="doctype-selector"></div>
<div class="name-selector"></div>
<p>
<button class="btn btn-sm btn-default btn-new-print-format">
{%= __("Create a New Format") %}</button>
</p>
</div>

View file

@ -32,7 +32,7 @@
.page-icon-group a {
margin-right: 7px;
}
.layout-main {
.page-content {
margin-top: 70px;
}
/* show menu aligned to the right border of the dropdown */

View file

@ -20,6 +20,10 @@ frappe.ui.form.Control = Class.extend({
frappe.boot.developer_mode===1 && this.$wrapper) {
this.$wrapper.attr("title", __(this.df.fieldname));
}
if(this.render_input) {
this.refresh();
}
},
make: function() {
this.make_wrapper();
@ -944,21 +948,23 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
no_spinner: true,
args: args,
callback: function(r) {
if(frappe.model.can_create(doctype)
&& me.df.fieldtype !== "Dynamic Link") {
// new item
if(!me.df.only_select) {
if(frappe.model.can_create(doctype)
&& me.df.fieldtype !== "Dynamic Link") {
// new item
r.results.push({
value: "<i class='icon-plus'></i> <em class='link-option'>"
+ __("Create a new {0}", [__(me.df.options)]) + "</em>",
action: me.new_doc
});
};
// advanced search
r.results.push({
value: "<i class='icon-plus'></i> <em class='link-option'>"
+ __("Create a new {0}", [__(me.df.options)]) + "</em>",
action: me.new_doc
value: "<i class='icon-search'></i> <em class='link-option'>"
+ __("Advanced Search") + "</em>",
action: me.open_advanced_search
});
};
// advanced search
r.results.push({
value: "<i class='icon-search'></i> <em class='link-option'>"
+ __("Advanced Search") + "</em>",
action: me.open_advanced_search
});
}
me.$input.cache[doctype][request.term] = r.results;
response(r.results);
@ -1032,6 +1038,11 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
}
}
}
if(this.df.filters) {
set_nulls(this.df.filters);
if(!args.filters) args.filters = {};
$.extend(args.filters, this.df.filters);
}
},
validate: function(value, callback) {
// validate the value just entered

View file

@ -8,6 +8,9 @@ frappe.ui.form.PrintPreview = Class.extend({
},
make: function() {
this.wrapper = this.frm.page.add_view("print", frappe.render_template("print_layout", {}));
// only system manager can edit
this.wrapper.find(".btn-print-edit").toggle(frappe.user.has_role("System Manager"));
},
bind_events: function() {
var me = this;
@ -62,6 +65,20 @@ frappe.ui.form.PrintPreview = Class.extend({
}
}
});
this.wrapper.find(".btn-print-edit").on("click", function() {
var print_format = me.get_print_format();
if(print_format && print_format.name) {
if(print_format.print_format_builder) {
frappe.route_options = print_format;
frappe.set_route("print-format-builder");
} else {
frappe.set_route("Form", "Print Format", print_format.name);
}
} else {
msgprint(__("Standard Format Not Editable"));
}
});
},
preview: function() {
var me = this;

View file

@ -13,6 +13,8 @@
<div class="btn-group">
<a class="btn-print-print btn-sm btn btn-default">
<strong>{%= __("Print") %}</strong></a>
<a class="btn-print-edit btn-sm btn btn-default">
{%= __("Edit") %}</a>
<a class="btn-print-preview btn-sm btn btn-default">
{%= __("Full Page") %}</a>
<a class="btn-download-pdf btn-sm btn btn-default">

View file

@ -243,7 +243,6 @@ $.extend(frappe.model, {
if(doc && doc[fieldname] !== value) {
doc[fieldname] = value;
frappe.model.trigger(fieldname, value, doc);
setTimeout(function() { console.log(doc[fieldname]) }, 100);
return true;
} else {
// execute link triggers (want to reselect to execute triggers)

View file

@ -42,7 +42,7 @@
margin-right: 7px;
}
.layout-main {
.page-content {
margin-top: 70px;
}

View file

@ -68,16 +68,27 @@ def get_html(doc, name=None, print_format=None, meta=None,
meta = frappe.get_meta(doc.doctype)
jenv = frappe.get_jenv()
format_data = {}
# determine template
if print_format in ("Standard", standard_format):
template = jenv.get_template("templates/print_formats/standard.html")
template = "standard"
else:
template = jenv.from_string(get_print_format(doc.doctype,
print_format))
print_format = frappe.get_doc("Print Format", print_format)
if print_format.format_data:
format_data = json.loads(print_format.format_data)
template = "standard"
else:
template = jenv.from_string(get_print_format(doc.doctype,
print_format))
if template == "standard":
template = jenv.get_template("templates/print_formats/standard.html")
args = {
"doc": doc,
"meta": frappe.get_meta(doc.doctype),
"layout": make_layout(doc, meta),
"layout": make_layout(doc, meta, format_data),
"no_letterhead": no_letterhead,
"trigger_print": cint(trigger_print),
"letter_head": get_letter_head(doc, no_letterhead)
@ -89,7 +100,7 @@ def get_html(doc, name=None, print_format=None, meta=None,
@frappe.whitelist()
def download_pdf(doctype, name, format=None):
html = frappe.get_print_format(doctype, name, format)
html = frappe.get_print(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"
@ -111,35 +122,41 @@ def get_letter_head(doc, no_letterhead):
else:
return frappe.db.get_value("Letter Head", {"is_default": 1}, "content") or ""
def get_print_format(doctype, format_name):
if format_name==standard_format:
return format_name
opts = frappe.db.get_value("Print Format", format_name, "disabled", as_dict=True)
if not opts:
frappe.throw(_("Print Format {0} does not exist").format(format_name), frappe.DoesNotExistError)
elif opts.disabled:
frappe.throw(_("Print Format {0} is disabled").format(format_name), frappe.DoesNotExistError)
def get_print_format(doctype, print_format):
if print_format.disabled:
frappe.throw(_("Print Format {0} is disabled").format(print_format.name),
frappe.DoesNotExistError)
# server, find template
path = os.path.join(get_doc_path(frappe.db.get_value("DocType", doctype, "module"),
"Print Format", format_name), frappe.scrub(format_name) + ".html")
"Print Format", print_format.name), frappe.scrub(print_format.name) + ".html")
if os.path.exists(path):
with open(path, "r") as pffile:
return pffile.read()
else:
html = frappe.db.get_value("Print Format", format_name, "html")
if html:
return html
if print_format.html:
return print_format.html
else:
frappe.throw(_("No template found at path: {0}").format(path),
frappe.TemplateNotFoundError)
def make_layout(doc, meta):
def make_layout(doc, meta, format_data=None):
layout, page = [], []
layout.append(page)
for df in meta.fields:
for df in format_data or meta.fields:
if format_data:
# embellish df with original properties
df = frappe._dict(df)
if df.fieldname:
original = meta.get_field(df.fieldname)
if original:
newdf = original.as_dict()
newdf.update(df)
df = newdf
df.print_hide = 0
if df.fieldtype=="Section Break" or page==[]:
page.append([])