xiaohongshu
xiaohongshu
erpnext-syntax-jinja
Deterministic Jinja template syntax for ERPNext/Frappe Print Formats, Email Templates, and Portal Pages
Full skill instructions
Correct Jinja syntax for Print Formats, Email Templates, and Portal Pages in ERPNext/Frappe v14/v15/v16.
USE this skill when:
DO NOT USE for:
| Object | Description |
|---|---|
doc | The document being printed |
frappe | Frappe module with utility methods |
_() | Translation function |
| Object | Description |
|---|---|
doc | The linked document |
frappe | Frappe module (limited) |
| 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.mdfor complete details.
{# 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) }}
{# 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']) %}
<h1>{{ _("Invoice") }}</h1>
<p>{{ _("Total: {0}").format(doc.grand_total) }}</p>
See:
references/methods-reference.mdfor all methods.
{% if doc.status == "Paid" %}
<span class="label-success">{{ _("Paid") }}</span>
{% elif doc.status == "Overdue" %}
<span class="label-danger">{{ _("Overdue") }}</span>
{% else %}
<span>{{ doc.status }}</span>
{% endif %}
{% for item in doc.items %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ item.item_name }}</td>
<td>{{ item.get_formatted("amount", doc) }}</td>
</tr>
{% else %}
<tr><td colspan="3">{{ _("No items") }}</td></tr>
{% endfor %}
| Variable | Description |
|---|---|
loop.index | 1-indexed position |
loop.first | True on first |
loop.last | True on last |
loop.length | Total items |
{% set total = 0 %}
{% set customer_name = doc.customer_name | default('Unknown') %}
| Filter | Example |
|---|---|
default | {{ value | default('N/A') }} |
length | {{ items | length }} |
join | {{ names | join(', ') }} |
truncate | {{ text | truncate(100) }} |
safe | {{ html | safe }} (trusted content only!) |
See:
references/filters-reference.mdfor all filters.
<style>
.header { background: #f5f5f5; padding: 15px; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { border: 1px solid #ddd; padding: 8px; }
.text-right { text-align: right; }
</style>
<div class="header">
<h1>{{ doc.select_print_heading or _("Invoice") }}</h1>
<p>{{ doc.name }}</p>
<p>{{ _("Date") }}: {{ doc.get_formatted("posting_date") }}</p>
</div>
<table class="table">
<thead>
<tr>
<th>{{ _("Item") }}</th>
<th class="text-right">{{ _("Qty") }}</th>
<th class="text-right">{{ _("Amount") }}</th>
</tr>
</thead>
<tbody>
{% for row in doc.items %}
<tr>
<td>{{ row.item_name }}</td>
<td class="text-right">{{ row.qty }}</td>
<td class="text-right">{{ row.get_formatted("amount", doc) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><strong>{{ _("Grand Total") }}:</strong> {{ doc.get_formatted("grand_total") }}</p>
<p>{{ _("Dear") }} {{ doc.customer_name }},</p>
<p>{{ _("Invoice") }} <strong>{{ doc.name }}</strong> {{ _("for") }}
{{ doc.get_formatted("grand_total") }} {{ _("is due.") }}</p>
<p>{{ _("Due Date") }}: {{ frappe.format_date(doc.due_date) }}</p>
{% if doc.items %}
<ul>
{% for item in doc.items %}
<li>{{ item.item_name }} - {{ item.qty }} x {{ item.get_formatted("rate", doc) }}</li>
{% endfor %}
</ul>
{% endif %}
<p>{{ _("Best regards") }},<br>
{{ frappe.db.get_value("Company", doc.company, "company_name") }}</p>
{% extends "templates/web.html" %}
{% block title %}{{ _("Projects") }}{% endblock %}
{% block page_content %}
<h1>{{ _("Projects") }}</h1>
{% if frappe.session.user != 'Guest' %}
<p>{{ _("Welcome") }}, {{ frappe.get_fullname() }}</p>
{% endif %}
{% for project in projects %}
<div class="project">
<h3>{{ project.title }}</h3>
<p>{{ project.description | truncate(150) }}</p>
</div>
{% else %}
<p>{{ _("No projects found.") }}</p>
{% endfor %}
{% endblock %}
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
jenv = {
"methods": ["myapp.jinja.methods"],
"filters": ["myapp.jinja.filters"]
}
import frappe
def get_company_logo(company):
"""Get company logo URL"""
return frappe.db.get_value("Company", company, "company_logo") or ""
<img src="{{ get_company_logo(doc.company) }}">
_() for all user-facing stringsget_formatted() for currency/date fields{{ value | default('') }}row.get_formatted("field", doc)| safe for user input (XSS risk)WARNING: Report Print Formats for Query/Script Reports use JavaScript templating.
| Aspect | Jinja (Print Formats) | JS (Report Print Formats) |
|---|---|---|
| Output | {{ }} | {%= %} |
| Code | {% %} | {% %} |
| Language | Python | JavaScript |
<!-- JS Template for Reports -->
{% for(var i=0; i<data.length; i++) { %}
<tr><td>{%= data[i].name %}</td></tr>
{% } %}
| Feature | v14 | v15 |
|---|---|---|
| Basic Jinja API | ✅ | ✅ |
| get_formatted() | ✅ | ✅ |
| jenv hook | ✅ | ✅ |
| Portal pages | ✅ | ✅ |
| frappe.utils.format_date with format | ✅ | ✅+ |
Version 16 introduced Chrome-based PDF rendering replacing wkhtmltopdf.
| Aspect | v14/v15 (wkhtmltopdf) | v16 (Chrome) |
|---|---|---|
| CSS Support | Limited CSS3 | Full modern CSS |
| Flexbox/Grid | Partial | Full support |
| Page breaks | page-break-* | break-* preferred |
| Fonts | System fonts | Web fonts supported |
| Performance | Faster | Slightly slower |
/* v14/v15 */
.page-break { page-break-before: always; }
/* v16 - both work, but break-* is preferred */
.page-break { break-before: page; }
# In site_config.json
{
"pdf_engine": "chrome", # or "wkhtmltopdf" for legacy
"chrome_path": "/usr/bin/chromium"
}
Most print formats work unchanged. Update if using:
| File | Contents |
|---|---|
references/context-objects.md | Available objects per template type |
references/methods-reference.md | All frappe.* methods |
references/filters-reference.md | Standard and custom filters |
references/examples.md | Complete working examples |
references/anti-patterns.md | Mistakes to avoid |
erpnext-syntax-hooks - For jenv configuration in hooks.pyerpnext-impl-jinja - For implementation patternserpnext-errors-jinja - For error handlingxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience