frappe-printing-templates について
Frappe Printing & Templatesは、Frappeで文書の表示形式を作成する開発者向けのスキルです。Jinjaによる印刷・メールテンプレート、PDF生成、レターヘッド設定、出力確認を扱います。
主な機能
- 01Jinja印刷フォーマットの作成
- 02動的なメールテンプレートの作成
- 03文書からのPDF生成
- 04レターヘッドとブランド要素の設定
- 05描画とエスケープのリスク確認
活用シーン
- 01DocType文書の印刷レイアウトをカスタマイズ
- 02動的フィールドを含む注文メールを作成
- 03プログラムから文書PDFを生成
- 04テンプレートのフィールドやPDF出力を調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lubusin/ frappe-skills --skill frappe-printing-templatesインストール後、Claude Code で/frappe-printing-templatesを入力して読み込まれたか確認します。
同梱ファイルreferences/jinja.mdreferences/printing.md
詳細
Frappe の印刷とテンプレート
Frappe で Jinja を使って印刷フォーマット、メールテンプレート、ドキュメントテンプレートを作成します。
使用する場面
- ドキュメント用のカスタム印刷フォーマットを作成する
- 動的コンテンツを含むメールテンプレートを作成する
- ドキュメントから PDF を生成する
- Web ページで Jinja テンプレートを使用する
- ブランディング用のレターヘッドを設定する
- Print Format Builder を使用する
必要な入力
- 印刷フォーマットの対象 DocType
- レイアウト要件(フィールド、テーブル、ヘッダー)
- フォーマットが標準(バージョン管理される)かカスタム(DB に保存される)か
- レターヘッド/ブランディング要件
- PDF 生成の要件
手順
0) フォーマットの種類を選択する
| 種類 | 作成方法 | バージョン管理 | ユーザーによるカスタマイズ |
|---|---|---|---|
| 標準 | Developer Mode で JSON として保存 | はい | いいえ |
| Print Format Builder | ドラッグ&ドロップ UI | いいえ(DB) | はい |
| カスタム HTML(Jinja) | awesomebar に「new print format」と入力 | 任意 | 場合による |
1) Jinja 印刷フォーマットを作成する
awesomebar →「New Print Format」から作成します。
- 一意の名前を設定します
- 対象 DocType にリンクします
- 「Standard」=「No」に設定します(開発モードでエクスポートする場合は「Yes」)
- 「Custom Format」にチェックを入れます
- Print Format Type =「Jinja」に設定します
- Jinja HTML を記述します
<div class="print-format">
<h1>{{ doc.name }}</h1>
<p><strong>{{ _("Customer") }}:</strong> {{ doc.customer }}</p>
<p><strong>{{ _("Date") }}:</strong> {{ frappe.format_date(doc.transaction_date) }}</p>
<table class="table table-bordered">
<thead>
<tr>
<th>{{ _("Item") }}</th>
<th>{{ _("Qty") }}</th>
<th class="text-right">{{ _("Rate") }}</th>
<th class="text-right">{{ _("Amount") }}</th>
</tr>
</thead>
<tbody>
{% for item in doc.items %}
<tr>
<td>{{ item.item_name }}</td>
<td>{{ item.qty }}</td>
<td class="text-right">{{ frappe.format(item.rate, {'fieldtype': 'Currency'}) }}</td>
<td class="text-right">{{ frappe.format(item.amount, {'fieldtype': 'Currency'}) }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right"><strong>{{ _("Total") }}</strong></td>
<td class="text-right"><strong>{{ frappe.format(doc.grand_total, {'fieldtype': 'Currency'}) }}</strong></td>
</tr>
</tfoot>
</table>
{% if doc.terms %}
<div class="terms">
<h4>{{ _("Terms & Conditions") }}</h4>
<p>{{ doc.terms }}</p>
</div>
{% endif %}
</div>
<style>
.print-format { font-family: Arial, sans-serif; }
.print-format h1 { color: #333; }
.print-format table { width: 100%; margin-top: 20px; }
</style>
2) Frappe Jinja API を使用する
テンプレート内でのデータ取得:
{# Fetch a document #}
{% set customer = frappe.get_doc('Customer', doc.customer) %}
{{ customer.customer_name }}
{# List query (ignores permissions) #}
{% set open_orders = frappe.get_all('Sales Order',
filters={'customer': doc.customer, 'status': 'To Deliver and Bill'},
fields=['name', 'grand_total'],
order_by='creation desc',
page_length=5) %}
{# Permission-aware list query #}
{% set my_tasks = frappe.get_list('Task',
filters={'owner': frappe.session.user}) %}
{# Single value lookup #}
{% set company_abbr = frappe.db.get_value('Company', doc.company, 'abbr') %}
{# Settings value #}
{% set timezone = frappe.db.get_single_value('System Settings', 'time_zone') %}
書式設定:
{{ frappe.format(50000, {'fieldtype': 'Currency'}) }}
{{ frappe.format_date('2025-01-15') }}
{{ frappe.format_date(doc.posting_date) }}
セッションとコンテキスト:
{{ frappe.session.user }}
{{ frappe.get_fullname() }}
{{ frappe.lang }}
{{ _("Translatable string") }}
URL:
<a href="{{ frappe.get_url() }}/app/sales-order/{{ doc.name }}">View Order</a>
3) メールテンプレートを作成する
Dear {{ doc.customer_name }},
Your order {{ doc.name }} has been confirmed.
Items:
{% for item in doc.items %}
- {{ item.item_name }} x {{ item.qty }}
{% endfor %}
Total: {{ frappe.format(doc.grand_total, {'fieldtype': 'Currency'}) }}
Thank you,
{{ frappe.get_fullname() }}
4) プログラムから PDF を生成する
import frappe
# Generate PDF
pdf_content = frappe.get_print(
doctype="Sales Invoice",
name="SINV-001",
print_format="Custom Invoice",
as_pdf=True
)
# Attach PDF to document
frappe.attach_print(
doctype="Sales Invoice",
name="SINV-001",
print_format="Custom Invoice",
file_name="invoice.pdf"
)
# Send with email
frappe.sendmail(
recipients=["[email protected]"],
subject="Your Invoice",
message="Please find attached your invoice.",
attachments=[{
"fname": "invoice.pdf",
"fcontent": pdf_content
}]
)
5) レターヘッドを設定する
- Letter Head リストに移動し、「New」を選択します
- 会社のロゴとヘッダー画像をアップロードします
- 会社のデフォルトとして設定します
- レターヘッドは印刷フォーマットに自動的に表示されます
6) Jinja フィルターを使用する
{{ doc.customer_name|upper }} {# UPPERCASE #}
{{ doc.notes|truncate(100) }} {# Truncate text #}
{{ doc.description|striptags }} {# Remove HTML #}
{{ doc.html_content|safe }} {# Render raw HTML (trusted only!) #}
{{ items|length }} {# Count items #}
{{ items|first }} {# First item #}
{{ names|join(', ') }} {# Join list #}
{{ amount|round(2) }} {# Round number #}
{{ value|default('N/A') }} {# Default if undefined #}
{{ data|tojson }} {# Convert to JSON #}
7) テンプレートの継承とマクロ
{# macros/fields.html #}
{% macro field_row(label, value) %}
<tr>
<td class="label"><strong>{{ _(label) }}</strong></td>
<td>{{ value }}</td>
</tr>
{% endmacro %}
{# In print format #}
{% from "macros/fields.html" import field_row %}
<table>
{{ field_row("Customer", doc.customer_name) }}
{{ field_row("Date", frappe.format_date(doc.posting_date)) }}
{{ field_row("Total", frappe.format(doc.grand_total, {'fieldtype': 'Currency'})) }}
</table>
検証
- 印刷ビューで印刷フォーマットが正しく表示される
- すべてのフィールドが適切な書式で表示される
- PDF がエラーなく生成される
- メールテンプレートに正しいデータが表示される
- 印刷したドキュメントにレターヘッドが表示される
- テンプレート内で翻訳が機能する(
_()) - エスケープされていないコンテンツによる XSS のリスクがない
失敗時の動作/デバッグ
- テンプレート構文エラー:Jinja の区切り文字(
{{ }}、{% %})を確認し、閉じられていないブロックを探します - フィールドが表示されない:フィールド名が DocType のスキーマと一致していることを確認し、子テーブルのアクセス方法を確認します
- PDF 生成に失敗する:wkhtmltopdf のインストールを確認し、印刷フォーマットの Jinja が有効であることを確認します
- PDF のスタイルに問題がある:インラインスタイルを使用し、複雑な CSS は避け、まず印刷ビューでテストします
- テンプレートの権限エラー:
frappe.get_all(権限チェックなし)とfrappe.get_listを使い分けます
エスカレーション
- アプリレベルのフックや構造について →
frappe-app-development - DocType スキーマに関する質問について →
frappe-doctype-development
参考資料
- references/jinja.md — Jinja テンプレートと Frappe Jinja API
- references/printing.md — 印刷フォーマットと PDF 生成
ガードレール
- 実データでテストする:実際のドキュメントで必ずプレビューします。エッジケースによってテンプレートが壊れることがあります
- フィールドが欠落していても適切に処理する:
{{ doc.field or '' }}または{% if doc.field %}を使用します - 画像には
get_url()を使用する:URL をハードコードせず、{{ frappe.utils.get_url() }}/files/...を使用します - ユーザーコンテンツをエスケープする:XSS を防ぐため、ユーザー生成コンテンツには
{{ value | e }}を使用します - スタイルはインラインにする:PDF ジェネレーターは外部 CSS に対応していません。インラインの
style属性を使用します
よくある間違い
| 間違い | 失敗する理由 | 修正方法 |
|---|---|---|
| Jinja の構文が間違っている | テンプレートエラーが発生し、出力が空になります | 出力には {{ }}、ロジックには {% %} を使用し、閉じタグを確認します |
| フィルターが不足している | 生データが表示されます | 書式設定には frappe.format() または frappe.format_date() を使用します |
| URL をハードコードしている | サイト間で画像やリンクが機能しなくなります | 絶対 URL には {{ frappe.utils.get_url() }} を使用します |
| 子テーブルへのアクセス方法が間違っている | 空の結果になるか、エラーが発生します | doc.child_table_name ではなく {% for item in doc.items %} を使用します |
| 印刷形式で複雑な CSS を使用している | PDF でスタイルが失われます | インラインスタイルとシンプルなレイアウトを使用し、構造には <table> を使用します |
| None 値を処理していない | 出力に 'None' という文字列が表示されます | {{ value or '' }} または {% if value %} を使用します |
コメント
Markdown 対応