From 76b5ee8b9e1f82907d0b478da13e1149f32150d0 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 19 Oct 2021 19:12:15 +0530 Subject: [PATCH] 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 --- .../doctype/print_format/print_format.py | 8 ++ .../print_format_field_template/__init__.py | 0 .../print_format_field_template.js | 8 ++ .../print_format_field_template.json | 101 ++++++++++++++++++ .../print_format_field_template.py | 43 ++++++++ .../test_print_format_field_template.py | 8 ++ .../public/js/print_format_builder/Field.vue | 6 ++ .../PrintFormatControls.vue | 30 +++++- .../public/js/print_format_builder/store.js | 6 +- .../public/js/print_format_builder/utils.js | 20 ++++ .../print_format/macros/FieldTemplate.html | 4 + frappe/utils/weasyprint.py | 7 +- 12 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 frappe/printing/doctype/print_format_field_template/__init__.py create mode 100644 frappe/printing/doctype/print_format_field_template/print_format_field_template.js create mode 100644 frappe/printing/doctype/print_format_field_template/print_format_field_template.json create mode 100644 frappe/printing/doctype/print_format_field_template/print_format_field_template.py create mode 100644 frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py create mode 100644 frappe/templates/print_format/macros/FieldTemplate.html diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 867e36997d..f19c0af9bf 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -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) diff --git a/frappe/printing/doctype/print_format_field_template/__init__.py b/frappe/printing/doctype/print_format_field_template/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/printing/doctype/print_format_field_template/print_format_field_template.js b/frappe/printing/doctype/print_format_field_template/print_format_field_template.js new file mode 100644 index 0000000000..7fbb0d7359 --- /dev/null +++ b/frappe/printing/doctype/print_format_field_template/print_format_field_template.js @@ -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) { + + // } +}); diff --git a/frappe/printing/doctype/print_format_field_template/print_format_field_template.json b/frappe/printing/doctype/print_format_field_template/print_format_field_template.json new file mode 100644 index 0000000000..3b79aae7e8 --- /dev/null +++ b/frappe/printing/doctype/print_format_field_template/print_format_field_template.json @@ -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 +} \ No newline at end of file diff --git a/frappe/printing/doctype/print_format_field_template/print_format_field_template.py b/frappe/printing/doctype/print_format_field_template/print_format_field_template.py new file mode 100644 index 0000000000..b66afdb6b1 --- /dev/null +++ b/frappe/printing/doctype/print_format_field_template/print_format_field_template.py @@ -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) diff --git a/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py b/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py new file mode 100644 index 0000000000..f0b1329763 --- /dev/null +++ b/frappe/printing/doctype/print_format_field_template/test_print_format_field_template.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt + +# import frappe +import unittest + +class TestPrintFormatFieldTemplate(unittest.TestCase): + pass diff --git a/frappe/public/js/print_format_builder/Field.vue b/frappe/public/js/print_format_builder/Field.vue index af1e574593..2884ea48c5 100644 --- a/frappe/public/js/print_format_builder/Field.vue +++ b/frappe/public/js/print_format_builder/Field.vue @@ -7,6 +7,12 @@ v-if="df.fieldtype == 'HTML' && df.html" v-html="df.html" > +
+ {{ df.label }} +
+ {% 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}) }} + diff --git a/frappe/utils/weasyprint.py b/frappe/utils/weasyprint.py index b213081d78..89b45c629b 100644 --- a/frappe/utils/weasyprint.py +++ b/frappe/utils/weasyprint.py @@ -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):