feat: Field Templates
- standard field templates for rendering custom templates for fields for e.g., taxes in invoice - ability to create user defined reusable templates that show up in print format builder
This commit is contained in:
parent
c31bf0aadc
commit
76b5ee8b9e
12 changed files with 237 additions and 4 deletions
|
|
@ -11,6 +11,14 @@ from frappe.utils.weasyprint import get_html, download_pdf
|
|||
from frappe.model.document import Document
|
||||
|
||||
class PrintFormat(Document):
|
||||
def onload(self):
|
||||
templates = frappe.db.get_all(
|
||||
"Print Format Field Template",
|
||||
fields=["template", "field", "name"],
|
||||
filters={"document_type": self.doc_type},
|
||||
)
|
||||
self.set_onload("print_templates", templates)
|
||||
|
||||
def get_html(self, docname, letterhead=None):
|
||||
return get_html(self.doc_type, docname, self.name, letterhead)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2021, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Print Format Field Template', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2021-10-05 14:23:56.508499",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"document_type",
|
||||
"field",
|
||||
"template_file",
|
||||
"column_break_3",
|
||||
"module",
|
||||
"standard",
|
||||
"section_break_5",
|
||||
"template"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"depends_on": "eval:!doc.multiple",
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Document Type",
|
||||
"mandatory_depends_on": "eval:!doc.multiple",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "field",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Default Template For Field"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.standard",
|
||||
"fieldname": "template",
|
||||
"fieldtype": "Code",
|
||||
"label": "Template",
|
||||
"mandatory_depends_on": "eval:!doc.standard",
|
||||
"options": "HTML"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "standard",
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"label": "Module",
|
||||
"options": "Module Def"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "standard",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Standard"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.standard",
|
||||
"fieldname": "template_file",
|
||||
"fieldtype": "Data",
|
||||
"label": "Template File",
|
||||
"mandatory_depends_on": "eval:doc.standard"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-10-19 17:47:59.577949",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Printing",
|
||||
"name": "Print Format Field Template",
|
||||
"naming_rule": "Set by user",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
||||
|
||||
class PrintFormatFieldTemplate(Document):
|
||||
def validate(self):
|
||||
if self.standard and not (frappe.conf.developer_mode or frappe.flags.in_patch):
|
||||
frappe.throw(_("Enable developer mode to create a standard Print Template"))
|
||||
|
||||
def before_insert(self):
|
||||
self.validate_duplicate()
|
||||
|
||||
def on_update(self):
|
||||
self.validate_duplicate()
|
||||
self.export_doc()
|
||||
|
||||
def validate_duplicate(self):
|
||||
if not self.standard:
|
||||
return
|
||||
if not self.field:
|
||||
return
|
||||
|
||||
filters = {"document_type": self.document_type, "field": self.field}
|
||||
if not self.is_new():
|
||||
filters.update({"name": ("!=", self.name)})
|
||||
result = frappe.db.get_all("Print Format Field Template", filters=filters, limit=1)
|
||||
if result:
|
||||
frappe.throw(
|
||||
_("A template already exists for field {0} of {1}").format(
|
||||
frappe.bold(self.field), frappe.bold(self.document_type)
|
||||
),
|
||||
frappe.DuplicateEntryError,
|
||||
title=_("Duplicate Entry"),
|
||||
)
|
||||
|
||||
def export_doc(self):
|
||||
from frappe.modules.utils import export_module_json
|
||||
|
||||
export_module_json(self, self.standard, self.module)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestPrintFormatFieldTemplate(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -7,6 +7,12 @@
|
|||
v-if="df.fieldtype == 'HTML' && df.html"
|
||||
v-html="df.html"
|
||||
></div>
|
||||
<div
|
||||
class="custom-html"
|
||||
v-if="df.fieldtype == 'Field Template'"
|
||||
>
|
||||
{{ df.label }}
|
||||
</div>
|
||||
<input
|
||||
v-else-if="editing && df.fieldtype != 'HTML'"
|
||||
ref="label-input"
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ export default {
|
|||
"fieldtype",
|
||||
"options",
|
||||
"table_columns",
|
||||
"html"
|
||||
"html",
|
||||
"field_template"
|
||||
]);
|
||||
if (cloned.custom) {
|
||||
// generate unique fieldnames for custom blocks
|
||||
|
|
@ -196,8 +197,35 @@ export default {
|
|||
fieldname: "name",
|
||||
fieldtype: "Data"
|
||||
},
|
||||
...this.print_templates,
|
||||
...fields
|
||||
];
|
||||
},
|
||||
print_templates() {
|
||||
let templates = this.print_format.__onload.print_templates || {};
|
||||
let out = [];
|
||||
for (let template of templates) {
|
||||
let df;
|
||||
if (template.field) {
|
||||
df = frappe.meta.get_docfield(
|
||||
this.meta.name,
|
||||
template.field
|
||||
);
|
||||
} else {
|
||||
df = {
|
||||
label: template.name,
|
||||
fieldname: frappe.scrub(template.name)
|
||||
};
|
||||
}
|
||||
out.push({
|
||||
label: `${__(df.label)} (${__("Field Template")})`,
|
||||
fieldname: df.fieldname + "_template",
|
||||
fieldtype: "Field Template",
|
||||
field_template: template.name
|
||||
});
|
||||
}
|
||||
return out;
|
||||
},
|
||||
page_number_positions() {
|
||||
return [
|
||||
{ label: __("Hide"), value: "Hide" },
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ export function getStore(print_format_name) {
|
|||
"fieldname",
|
||||
"fieldtype",
|
||||
"options",
|
||||
"width"
|
||||
"width",
|
||||
"field_template",
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
|
@ -99,7 +100,8 @@ export function getStore(print_format_name) {
|
|||
"fieldtype",
|
||||
"options",
|
||||
"table_columns",
|
||||
"html"
|
||||
"html",
|
||||
"field_template",
|
||||
]);
|
||||
});
|
||||
return column;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,17 @@ export function create_default_layout(meta) {
|
|||
options: df.options
|
||||
};
|
||||
|
||||
let field_template = get_field_template(
|
||||
print_format,
|
||||
df.fieldname
|
||||
);
|
||||
if (field_template) {
|
||||
field.label = `${__(df.label)} (${__("Field Template")})`;
|
||||
field.fieldtype = "Field Template";
|
||||
field.field_template = field_template.name;
|
||||
field.fieldname = df.fieldname = "_template";
|
||||
}
|
||||
|
||||
if (df.fieldtype === "Table") {
|
||||
field.table_columns = get_table_columns(df);
|
||||
}
|
||||
|
|
@ -109,6 +120,15 @@ export function get_table_columns(df) {
|
|||
return table_columns;
|
||||
}
|
||||
|
||||
function get_field_template(print_format, fieldname) {
|
||||
let templates = print_format.__onload.print_templates || {};
|
||||
for (let template of templates) {
|
||||
if (template.field === fieldname) {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export function pluck(object, keys) {
|
||||
let out = {};
|
||||
for (let key of keys) {
|
||||
|
|
|
|||
4
frappe/templates/print_format/macros/FieldTemplate.html
Normal file
4
frappe/templates/print_format/macros/FieldTemplate.html
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<div class="field-template" {{ field_attributes(df) }}>
|
||||
{% set template = frappe.db.get_value('Print Format Field Template', df.field_template, ['template', 'template_file', 'standard'], as_dict=1) %}
|
||||
{{ frappe.render_template(template.template_file if template.standard else template.template, {'doc': doc}) }}
|
||||
</div>
|
||||
|
|
@ -198,12 +198,17 @@ class PrintFormatGenerator:
|
|||
return layout
|
||||
|
||||
def set_field_renderers(self, layout):
|
||||
renderers = {"HTML Editor": "HTML", "Markdown Editor": "Markdown"}
|
||||
renderers = {
|
||||
"HTML Editor": "HTML",
|
||||
"Markdown Editor": "Markdown",
|
||||
"Field Template": "FieldTemplate",
|
||||
}
|
||||
for section in layout["sections"]:
|
||||
for column in section["columns"]:
|
||||
for df in column["fields"]:
|
||||
fieldtype = df["fieldtype"]
|
||||
df["renderer"] = renderers.get(fieldtype) or fieldtype
|
||||
df["section"] = section
|
||||
return layout
|
||||
|
||||
def process_margin_texts(self, layout):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue