--- name: erpnext-syntax-jinja version: 1.0.0 description: Deterministic Jinja template syntax for ERPNext/Frappe Print Formats, Email Templates, and Portal Pages author: OpenAEC Foundation tags: [erpnext, frappe, jinja, templates, print-formats, email-templates, portal-pages] languages: [en] frappe_versions: [v14, v15, v16] --- # ERPNext Jinja Templates Syntax Skill > Correct Jinja syntax for Print Formats, Email Templates, and Portal Pages in ERPNext/Frappe v14/v15/v16. --- ## When to Use This Skill USE this skill when: - Creating or modifying Print Formats - Developing Email Templates - Building Portal Pages (www/*.html) - Adding custom Jinja filters/methods via hooks DO NOT USE for: - Report Print Formats (they use JavaScript templating, not Jinja) - Client Scripts (use erpnext-syntax-clientscripts) - Server Scripts (use erpnext-syntax-serverscripts) --- ## Context Objects per Template Type ### Print Formats | Object | Description | |--------|-------------| | `doc` | The document being printed | | `frappe` | Frappe module with utility methods | | `_()` | Translation function | ### Email Templates | Object | Description | |--------|-------------| | `doc` | The linked document | | `frappe` | Frappe module (limited) | ### Portal Pages | Object | Description | |--------|-------------| | `frappe.session.user` | Current user | | `frappe.form_dict` | Query parameters | | `frappe.lang` | Current language | | Custom context | Via Python controller | > **See**: `references/context-objects.md` for complete details. --- ## Essential Methods ### Formatting (ALWAYS use) ```jinja {# RECOMMENDED for fields in print formats #} {{ doc.get_formatted("posting_date") }} {{ doc.get_formatted("grand_total") }} {# For child table rows - pass parent doc #} {% for row in doc.items %} {{ row.get_formatted("rate", doc) }} {{ row.get_formatted("amount", doc) }} {% endfor %} {# General formatting #} {{ frappe.format(value, {'fieldtype': 'Currency'}) }} {{ frappe.format_date(doc.posting_date) }} ``` ### Document Retrieval ```jinja {# Full document #} {% set customer = frappe.get_doc("Customer", doc.customer) %} {# Specific field value (more efficient) #} {% set abbr = frappe.db.get_value("Company", doc.company, "abbr") %} {# List of records #} {% set tasks = frappe.get_all('Task', filters={'status': 'Open'}, fields=['title', 'due_date']) %} ``` ### Translation (REQUIRED for user-facing strings) ```jinja
{{ _("Total: {0}").format(doc.grand_total) }}
``` > **See**: `references/methods-reference.md` for all methods. --- ## Control Structures ### Conditionals ```jinja {% if doc.status == "Paid" %} {{ _("Paid") }} {% elif doc.status == "Overdue" %} {{ _("Overdue") }} {% else %} {{ doc.status }} {% endif %} ``` ### Loops ```jinja {% for item in doc.items %}{{ doc.name }}
{{ _("Date") }}: {{ doc.get_formatted("posting_date") }}
| {{ _("Item") }} | {{ _("Qty") }} | {{ _("Amount") }} |
|---|---|---|
| {{ row.item_name }} | {{ row.qty }} | {{ row.get_formatted("amount", doc) }} |
{{ _("Grand Total") }}: {{ doc.get_formatted("grand_total") }}
``` --- ## Email Template ```jinja{{ _("Dear") }} {{ doc.customer_name }},
{{ _("Invoice") }} {{ doc.name }} {{ _("for") }} {{ doc.get_formatted("grand_total") }} {{ _("is due.") }}
{{ _("Due Date") }}: {{ frappe.format_date(doc.due_date) }}
{% if doc.items %}{{ _("Best regards") }},
{{ frappe.db.get_value("Company", doc.company, "company_name") }}
{{ _("Welcome") }}, {{ frappe.get_fullname() }}
{% endif %} {% for project in projects %}{{ project.description | truncate(150) }}
{{ _("No projects found.") }}
{% endfor %} {% endblock %} ``` ### www/projects/index.py ```python import frappe def get_context(context): context.title = "Projects" context.projects = frappe.get_all( "Project", filters={"is_public": 1}, fields=["name", "title", "description"], order_by="creation desc" ) return context ``` --- ## Custom Filters/Methods via jenv Hook ### hooks.py ```python jenv = { "methods": ["myapp.jinja.methods"], "filters": ["myapp.jinja.filters"] } ``` ### myapp/jinja/methods.py ```python import frappe def get_company_logo(company): """Get company logo URL""" return frappe.db.get_value("Company", company, "company_logo") or "" ``` ### Usage ```jinja