mcpField
P
printing-templates

printing-templates

github.com/lubusin文档处理69105 次安装12 周前更新
代码仓库

关于 printing-templates

使用 Jinja 创建 Frappe 打印格式及邮件或文档模板的技能,涵盖数据访问、格式化、PDF 生成和信头配置。

主要功能

  1. 01Jinja 打印格式
  2. 02动态邮件模板
  3. 03通过代码生成 PDF
  4. 04信头配置
  5. 05Jinja 过滤器与宏

适用场景

  1. 01制作带品牌样式的文档打印件
  2. 02从 Frappe 文档生成 PDF
  3. 03创建填充数据的邮件模板

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lubusin/frappe-skills --skill printing-templates

安装后在 Claude Code 中输入/printing-templates确认已加载。

附带文件references/jinja.mdreferences/printing.md

详细介绍

Frappe 打印与模板

在 Frappe 中使用 Jinja 创建打印格式、电子邮件模板和文档模板。

适用场景

  • 为文档创建自定义打印格式
  • 构建包含动态内容的电子邮件模板
  • 从文档生成 PDF
  • 在网页中使用 Jinja 模板
  • 配置信头以展示品牌形象
  • 使用打印格式构建器

所需输入

  • 打印格式的目标 DocType
  • 布局要求(字段、表格、页眉)
  • 格式类型是标准格式(受版本控制)还是自定义格式(存储在数据库中)
  • 信头/品牌形象要求
  • PDF 生成需求

操作步骤

0) 选择格式类型
类型创建方式受版本控制用户可自定义
标准格式开发者模式,保存为 JSON是否
打印格式构建器拖放式 UI否(数据库)是
自定义 HTML(Jinja)在快捷栏中输入 "new print format"可选视情况而定
1) 创建 Jinja 打印格式

通过快捷栏 → "New Print Format" 创建:

  1. 设置唯一名称
  2. 链接到目标 DocType
  3. 将 "Standard" 设为 "No"(开发模式导出时设为 "Yes")
  4. 勾选 "Custom Format"
  5. 将 Print Format Type 设为 "Jinja"
  6. 编写 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) 配置信头
  1. 前往信头列表 → 新建
  2. 上传公司徽标和页眉图片
  3. 将其设为公司的默认信头
  4. 信头会自动显示在打印格式中
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

升级处理

  • 应用级钩子和结构问题 → app-development
  • DocType 架构问题 → doctype-development

参考资料

防护措施

  • 使用实际数据测试:始终使用真实文档预览;边界情况可能导致模板出错
  • 妥善处理缺失字段:使用 {{ doc.field or '' }} 或 {% if doc.field %}
  • 使用 get_url() 获取图片:切勿硬编码 URL;使用 {{ frappe.utils.get_url() }}/files/...
  • 转义用户内容:对用户生成的内容使用 {{ value | e }},以防止 XSS
  • 保持样式内联:PDF 生成器不支持外部 CSS;使用内联 style 属性

常见错误

错误失败原因修正方法
Jinja 语法错误模板报错,输出为空输出使用 {{ }},逻辑使用 {% %};检查结束标签
缺少过滤器显示原始数据使用 frappe.format() 或 frappe.format_date() 进行格式化
URL 硬编码图片/链接在不同站点上失效使用 {{ frappe.utils.get_url() }} 获取绝对 URL
子表访问方式错误内容为空或报错使用 {% for item in doc.items %},而不是 doc.child_table_name
打印格式中的 CSS 过于复杂PDF 中样式丢失使用内联样式、简单布局,并使用 <table> 构建结构
未处理 None 值输出中出现 'None' 字符串使用 {{ value or '' }} 或 {% if value %}

评论

支持 Markdown