diff --git a/frappe/__init__.py b/frappe/__init__.py
index 4a0232e624..b92104ca1d 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -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
diff --git a/frappe/client.py b/frappe/client.py
index 924b3a0b22..50e6f6cfca 100644
--- a/frappe/client.py
+++ b/frappe/client.py
@@ -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):
diff --git a/frappe/config/setup.py b/frappe/config/setup.py
index 066750f4a5..1acf10a6ff 100644
--- a/frappe/config/setup.py
+++ b/frappe/config/setup.py
@@ -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",
diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 3224acf6ed..a9cd3793b8 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -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)
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index 1253bb23e2..8c3069c246 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -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)
diff --git a/frappe/model/meta.py b/frappe/model/meta.py
index 08c73c79e5..f020e94510 100644
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -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
diff --git a/frappe/print/doctype/print_format/print_format.json b/frappe/print/doctype/print_format/print_format.json
index 6c8d71b464..998eceb6ac 100644
--- a/frappe/print/doctype/print_format/print_format.json
+++ b/frappe/print/doctype/print_format/print_format.json
@@ -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",
diff --git a/frappe/print/doctype/print_format/test_print_format.py b/frappe/print/doctype/print_format/test_print_format.py
index a6b9981b0f..eec0d075a4 100644
--- a/frappe/print/doctype/print_format/test_print_format.py
+++ b/frappe/print/doctype/print_format/test_print_format.py
@@ -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("" in print_html)
self.assertTrue(re.findall('
[\s]*Administrator[\s]*
', print_html))
return print_html
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 70408f384b..23265f6d65 100644
--- a/frappe/print/page/print_format_builder/print_format_builder.js
+++ b/frappe/print/page/print_format_builder/print_format_builder.js
@@ -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('');
+ 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"));
+ }
+ });
}
});
diff --git a/frappe/print/page/print_format_builder/print_format_builder_start.html b/frappe/print/page/print_format_builder/print_format_builder_start.html
new file mode 100644
index 0000000000..dc01bba596
--- /dev/null
+++ b/frappe/print/page/print_format_builder/print_format_builder_start.html
@@ -0,0 +1,18 @@
+
+
{%= __("Select an existing format to edit or start a new format.") %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frappe/public/css/page.css b/frappe/public/css/page.css
index dae62357cd..b99fbd430b 100644
--- a/frappe/public/css/page.css
+++ b/frappe/public/css/page.css
@@ -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 */
diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js
index 996c76cc2b..0d88941276 100644
--- a/frappe/public/js/frappe/form/control.js
+++ b/frappe/public/js/frappe/form/control.js
@@ -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: " "
+ + __("Create a new {0}", [__(me.df.options)]) + "",
+ action: me.new_doc
+ });
+ };
+ // advanced search
r.results.push({
- value: " "
- + __("Create a new {0}", [__(me.df.options)]) + "",
- action: me.new_doc
+ value: " "
+ + __("Advanced Search") + "",
+ action: me.open_advanced_search
});
- };
- // advanced search
- r.results.push({
- value: " "
- + __("Advanced Search") + "",
- 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
diff --git a/frappe/public/js/frappe/form/print.js b/frappe/public/js/frappe/form/print.js
index 5da67cc35c..95e03cd960 100644
--- a/frappe/public/js/frappe/form/print.js
+++ b/frappe/public/js/frappe/form/print.js
@@ -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;
diff --git a/frappe/public/js/frappe/form/print_layout.html b/frappe/public/js/frappe/form/print_layout.html
index 704df7fd38..4da96c0f1c 100644
--- a/frappe/public/js/frappe/form/print_layout.html
+++ b/frappe/public/js/frappe/form/print_layout.html
@@ -13,6 +13,8 @@
{%= __("Print") %}
+
+ {%= __("Edit") %}
{%= __("Full Page") %}
diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js
index e35d026f61..12efc7cabf 100644
--- a/frappe/public/js/frappe/model/model.js
+++ b/frappe/public/js/frappe/model/model.js
@@ -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)
diff --git a/frappe/public/less/page.less b/frappe/public/less/page.less
index 3335f94c24..48881ef20a 100644
--- a/frappe/public/less/page.less
+++ b/frappe/public/less/page.less
@@ -42,7 +42,7 @@
margin-right: 7px;
}
-.layout-main {
+.page-content {
margin-top: 70px;
}
diff --git a/frappe/templates/pages/print.py b/frappe/templates/pages/print.py
index 1dc45b114c..8c21c63bc3 100644
--- a/frappe/templates/pages/print.py
+++ b/frappe/templates/pages/print.py
@@ -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([])