Skill125 estrellas del repoactualizado 2mo ago
frappe-syntax-jinja
>
Instalar en Claude Code
Copiargit clone --depth 1 https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package /tmp/frappe-syntax-jinja && cp -r /tmp/frappe-syntax-jinja/skills/source/syntax/frappe-syntax-jinja ~/.claude/skills/frappe-syntax-jinjaDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
SKILL.md
# 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
```jinja
{# 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
```jinja
{# 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
```jinja
<h1>{{ _("Invoice") }}</h1>
<p>{{ _("Total: {0}").format(doc.get_formatted("grand_total")) }}</p>
```
### System & Session
```jinja
{{ 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
```jinja
{% 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
```jinja
{% 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 %}
```
### Loop Variables
| Variable | Description |
|----------|-------------|
| `loop.index` | 1-indexed position |
| `loop.index0` | 0-indexed position |
| `loop.first` | `True` on first iteration |
| `loop.last` | `True` on last iteration |
| `loop.length` | Total number of items |
### Variables
```jinja
{% set total = 0 %}
{% set name = doc.customer_name | default("Unknown") %}
```
---
## Filters
| Filter