Skip to content
AI & Agents
Skill

/frappe-syntax-jinja

Use when writing Jinja templates for ERPNext/Frappe Print Formats, Email Templates, and Portal Pages. Covers template syntax, context variables, filters, macros, and v16 Chrome PDF rendering. Prevents common mistakes with doc context and child table iteration. Keywords: Jinja,

From plugin
frappe-claude-skill-package
17861 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-syntax-jinja --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-syntax-jinja

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when writing Jinja templates for ERPNext/Frappe Print Formats, Email Templates, and Portal Pages. Covers template syntax, context variables, filters, macros, and v16 Chrome PDF rendering. Prevents common mistakes with doc context and child table iteration. Keywords: Jinja,

SKILL.md

frappe-syntax-jinja.SKILL.md
name: frappe-syntax-jinja
description: >
  Use when writing Jinja templates for ERPNext/Frappe Print Formats, Email
  Templates, and Portal Pages. Covers template syntax, context variables,
  filters, macros, and v16 Chrome PDF rendering. Prevents common mistakes
  with doc context and child table iteration. Keywords: Jinja, print format,
  email template, portal page, template syntax, PDF, v14-v16,
  template syntax, Jinja example, print format code, how to show child table in print.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Frappe Jinja Templates Syntax

> Deterministic Jinja reference for Print Formats, Email Templates, Notification Templates, and Portal Pages in Frappe v14/v15/v16.

---

When to Use This Skill

USE when:

  • Creating or modifying Print Formats (Jinja-based)
  • Writing Email Templates with dynamic fields
  • Building Portal Pages (`www/*.html`) with Python controllers
  • Writing Notification Templates (system/email/SMS)
  • Registering custom Jinja methods or filters via `hooks.py`

DO NOT USE for:

  • Report Print Formats — they use JavaScript templating (`{%= %}`), NOT Jinja
  • Client Scripts — see `frappe-syntax-clientscripts`
  • Server Scripts — see `frappe-syntax-serverscripts`

---

Decision Tree: Which Template Type?

Need a printable document?
├─ YES → Is it for a Query/Script Report?
│        ├─ YES → Use JS Template ({%= %}), NOT Jinja
│        └─ NO  → Use Jinja Print Format
└─ NO  → Is it for email?
         ├─ YES → Is it triggered by workflow/notification?
         │        ├─ YES → Notification Template (Jinja)
         │        └─ NO  → Email Template (Jinja)
         └─ NO  → Is it a web page?
                  ├─ YES → Portal Page (www/*.html + .py controller)
                  └─ NO  → frappe.render_template() for ad-hoc rendering

---

Quick Reference: Jinja Syntax

| Syntax | Purpose | Example | |--------|---------|---------| | `{{ }}` | Output expression | `{{ doc.name }}` | | `{% %}` | Control statement | `{% if doc.status == "Paid" %}` | | `{# #}` | Comment | `{# This is a comment #}` | | `{{ _("text") }}` | Translation | `{{ _("Invoice") }}` | | `{{ val \| filter }}` | Filter | `{{ name \| default("N/A") }}` |

CRITICAL: Jinja vs JS Template Syntax

| Aspect | Jinja (Print Formats) | JS Template (Report Print Formats) | |--------|----------------------|-------------------------------------| | Output | `{{ expression }}` | `{%= expression %}` | | Code block | `{% statement %}` | `{% js_code %}` | | Language | Python | JavaScript | | Context | `doc`, `frappe` | `data`, `filters` |

**NEVER use Jinja syntax in Report Print Formats. NEVER use `{%= %}` in standard Print Formats.**

---

Context Objects by Template Type

Print Formats

| Object | Description | |--------|-------------| | `doc` | The document being printed (full Document object) | | `frappe` | Frappe module (whitelisted methods only) | | `frappe.utils` | Utility functions | | `_()` | Translation function | | `doc.items`, `doc.taxes` | Child table accessors (by fieldname) |

Email Templates

| Object | Description | |--------|-------------| | `doc` | The linked document (when triggered from a DocType) | | `frappe` | Frappe module (limited) | | `_()` | Translation function |

Notification Templates

| Object | Description | |--------|-------------| | `doc` | The document that triggered the notification | | `frappe` | Frappe module | | `_()` | Translation function |

Portal Pages (www/*.html)

| Object | Description | |--------|-------------| | `frappe` | Frappe module | | `frappe.session.user` | Current authenticated user | | `frappe.form_dict` | Query parameters from URL | | `frappe.lang` | Current language code | | Custom context | Set via `get_context(context)` in `.py` controller |

> **Full details**: `references/context-objects.md`

---

Essential Methods (Whitelisted in Jinja)

Formatting: ALWAYS Use for Display

{# ALWAYS use get_formatted() for fields in Print Formats #}
{{ doc.get_formatted("posting_date") }}
{{ doc.get_formatted("grand_total") }}

{# Child table rows — ALWAYS pass parent doc for currency context #}
{% for row in doc.items %}
    {{ row.get_formatted("rate", doc) }}
    {{ row.get_formatted("amount", doc) }}
{% endfor %}

{# General formatting with explicit fieldtype #}
{{ frappe.format(value, {'fieldtype': 'Currency'}) }}
{{ frappe.format_date(doc.posting_date) }}

Document Retrieval

{# Full document — use only when multiple fields needed #}
{% set customer = frappe.get_doc("Customer", doc.customer) %}

{# Single field — ALWAYS prefer over get_doc for one field #}
{% set abbr = frappe.db.get_value("Company", doc.company, "abbr") %}

{# List of records (no permission check) #}
{% set tasks = frappe.get_all("Task",
    filters={"status": "Open"},
    fields=["title", "due_date"],
    order_by="due_date asc",
    page_length=10) %}

{# List with permission check (portal pages) #}
{% set orders = frappe.get_list("Sales Order",
    filters={"customer": doc.customer},
    fields=["name", "grand_total"]) %}

Translation: REQUIRED for All User-Facing Strings

<h1>{{ _("Invoice") }}</h1>
<p>{{ _("Total: {0}").format(doc.get_formatted("grand_total")) }}</p>

System & Session

{{ frappe.get_url() }}
{{ frappe.get_fullname() }}
{{ frappe.get_fullname(doc.owner) }}
{{ frappe.db.get_single_value("System Settings", "time_zone") }}
{% if frappe.session.user != "Guest" %}...{% endif %}

> **Full method reference**: `references/methods-reference.md`

---

Control Structures

Conditionals

{% if doc.status == "Paid" %}
    <span class="paid">{{ _("Paid") }}</span>
{% elif doc.status == "Overdue" %}
    <span class="overdue">{{ _("Overdue") }}</span>
{% else %}
    <span>{{ doc.status }}</span>
{% endif %}

Loops with Child Tables

{%
Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package