跳到主要内容

基于Airtable数据库的ZUGFeRD电子发票:使用PDF4me在Make中构建 JSON 并通过电子邮件发送混合 PDF

· 阅读需 24 分钟
SEO and Content Writer

PDF4me Create a ZUGFeRD invoice是一个Make模块,它将结构化发票数据转换为混合PDF/A-3电子发票。该模块指向一个Airtable数据库,读取一行数据,将其转换为EN 16931 XML,返回一个既可读又可机器解析的文件,并将其放回记录中。

搜索电子发票软件,你会看到供应商对比:平台、套件、单页定价。搜索Airtable是否可以生成发票,你会看到一大堆帖子解释说它不能。介于这两种结果之间的是这样一个团队:他们已经在使用Airtable,却被告知发票必须机器可读,并且他们不想为了满足格式要求而将业务迁移到其他发票平台。本文正是为他们量身打造的。

概况
1. Watch records
Airtable. 从ZUGFeRD Requests中提取Status为“新建”的行。
2. Create a ZUGFeRD invoice
PDF4me将发票 JSON 转换为PDF/A-3文件中的EN 16931 XML。
3. Upload an attachment
Airtable将混合 PDF 文件放回同一记录中。
4. Update a record
Airtable会将结果写回,以便该行显示发生了什么。
5. Send an email
Gmail会将电子发票发送给该行的买家。
简短版

一行数据被添加到Airtable表格中,包含卖家、买家、一个商品项和总计。Make Make该数据,根据列构建一个简洁的 JSON 对象, PDF4me将其转换为嵌入EN 16931 XML 的PDF/A-3文件。该文件作为附件返回到记录中,并通过电子邮件发送给买家。五个模块,无需编写 XML 代码。

基于原因的问答​

Why run this from Airtable rather than an invoicing product?因为发票数据通常是已在其他地方跟踪的工作的副产品。如果项目、客户和约定的价格都存储在数据库中,那么该数据库就是最接近真实数据源的东西。为了满足文件格式的要求而将其迁移到产品中,意味着同一个客户的信息将永远存在于两个地方。

Why describe the invoice as JSON instead of writing the XML?因为跨行业发票语法冗长、嵌套层级深,而且对代码位置的要求非常严格。JSON 对象用更少的空间就能表达相同的信息,其键也更符合业务术语, PDF4me会根据 JSON 对象生成符合规范的 XML。最容易出错的部分往往是你没有维护的部分。

Why does the hybrid file matter?因为发票有两个受众。买方的会计系统读取嵌入的 XML 数据,无需人工录入即可完成记账;应付账款人员打开同一个文件,看到的是一页内容。一份文件就能满足两者的需求,这就是原因。FeRD该格式背后的德国论坛就是这样构建的,而不是仅仅指定 XML。

Why attach the result back to the record?因为找不到电子发票只是把问题延后了而已。将 PDF 文件放入该行的Document字段中,就能将该文件与生成该文件的数据关联起来,这正是您在买家对 18 个月后的金额提出异议时所需要的。


您将获得​

输入:一条Airtable记录,包含发票编号、日期、卖家信息栏、买家信息栏、一个项目明细和总计, Status设置为“新建”。输出:一个ZUGFeRD PDF/A-3附件,并同时向买家发送一封包含该文件的电子邮件。

Airtable base PDF4me Live Automations with the ZUGFeRD Requests table open, showing rows RE-2026-0007 to RE-2026-0010 with InvoiceDate 2026-10-12, DueDate 2026-11-11, Currency EUR, SellerName Muster GmbH at Hauptstrasse 1, 10115 Berlin DE with SellerVATID DE123456789, BuyerName Test Kunde AG at Bahnhofstrasse 5, 80331 Muenchen DE, ItemDescription Consulting, Quantity 10.00, UnitPrice 100.50, VATRate 19.00, NetTotal 1005, VATAmount 191, GrossTotal 1196 and Status Generated

每张发票对应一行。这里的每一列都会成为场景构建的 JSON 中的一个键。

Gmail showing the received message with subject Your e-invoice RE-2026-0009, the body reading Hello Test Kunde AG, Please find your ZUGFeRD e-invoice RE-2026-0009 attached, Total 1195.95 EUR, and one PDF attachment whose thumbnail shows the rendered German invoice

买家看到的是一张普通的发票。他们的软件看到的是发票中的 XML 数据。


你需要什么​

  • A Make account。Open Make. 五个关于标准应用程序的模块。
  • PDF4me API key。Get your API key一个PDF4me连接即可满足此场景的需求。
  • An Airtable base捕获的基础是PDF4me Live Automations,其中包含一个名为ZUGFeRD Requests的表,该表包含发票抬头、包含增值税 ID 的卖家块、买家块、行项目字段、总计、电子邮件列、 Status字段和名为Document ”的附件字段。
  • A Gmail account用于交付,与Make连接。
  • Your seller details including a VAT ID它们来自列,因此一个数据库可以为多个法人实体开具发票。
先看成品。
这是捕获的场景为RE-2026-0009行生成的确切文件。在阅读器中打开它以查看页面,在ZUGFeRD验证器中打开它以查看嵌入的 XML。
下载 sample-output-zugferd-re-2026-0009.pdf

概览​

  1. Watch records (Airtable) 从ZUGFeRD Requests中获取新行。
  2. Create a ZUGFeRD invoice ( PDF4me)将 JSON 转换为混合 PDF。
  3. Upload an attachment ( Airtable)会将其附加到同一行的Document字段中。
  4. Update a record ( Airtable)会将结果写回。
  5. Send an email ( Gmail)将电子发票发送给买方。

完整场景概述​

The Make scenario canvas showing five connected modules in order: Airtable Watch records with a clock icon, PDF4me Create a ZUGFeRD invoice, Airtable Upload an attachment, Airtable Update a record, and Gmail Send an email, each marked with a green tick and one operation

与瑞士QR方案相同的五模块结构,仅中间的PDF4me模块有所不同。


步骤 1:如何从Airtable行触发场景?​

目前流程:尚未启动,这是触发点。

标记为“就绪”的行表示请求开具发票。

  1. 添加Airtable > Watch records作为触发器。
  2. 配置:
    • 根据: PDF4me Live Automations
    • 桌子: ZUGFeRD Requests
    • Trigger field: Created
    • Label field: Title
    • 限制: 10
    • 公式: {Status}='New'

Watch records配置​

Make Airtable Watch records module with Base set to PDF4me Live Automations, Table ZUGFeRD Requests, Trigger field Created, Label field Title, Limit 10, View left empty and Formula braces Status equals New

Status是关键所在。只有当有人将行设置为“新建”时,才会发布任何内容。


步骤 2:如何将一行转换为ZUGFeRD发票?​

目前为止的流程: Airtable触发器。

这是生成电子发票的模块。设置项决定发票格式;JSON 数据决定发票内容。

  1. 添加PDF4me > Create a ZUGFeRD invoice。
  2. 配置输出:
    • File Name: ZUGFeRD_ + InvoiceNumber + .pdf
    • Input Format: JSON
    • Output Mode: XML With PDF
    • ZUGFeRD Version: 2*
    • Conformance Level: EN16931
    • Render Invoice on PDF: Yes
  3. 将发票对象粘贴到Invoice JSON Data中,并映射其中的列:
{
"InvoiceId": "{{1.InvoiceNumber}}",
"TypeCode": "380",
"InvoiceType": "RECHNUNG",
"IssueDate": "{{formatDate(1.InvoiceDate; "YYYY-MM-DD")}}T00:00:00",
"DeliveryDate": "{{formatDate(1.InvoiceDate; "YYYY-MM-DD")}}T00:00:00",
"IsTest": false,
"Seller": {
"Name": "{{1.SellerName}}",
"VatId": "{{1.SellerVATID}}",
"Address": {
"Street": "{{1.SellerStreet}}",
"PostalCode": "{{1.SellerPostalCode}}",
"City": "{{1.SellerCity}}",
"Country": "{{1.SellerCountry}}"
}
},
"Buyer": {
"Name": "{{1.BuyerName}}",
"Address": {
"Street": "{{1.BuyerStreet}}",
"PostalCode": "{{1.BuyerPostalCode}}",
"City": "{{1.BuyerCity}}",
"Country": "{{1.BuyerCountry}}"
}
},
"LineItems": [
{
"ProductName": "{{1.ItemDescription}}",
"Quantity": {{1.Quantity}},
"UnitCode": "C62",
"UnitPrice": {{1.UnitPrice}},
"LineTotal": {{parseNumber(1.NetTotal; ".")}}
}
],
"Totals": {
"Currency": "{{1.Currency}}",
"LineTotal": {{parseNumber(1.NetTotal; ".")}},
"TaxBasisTotal": {{parseNumber(1.NetTotal; ".")}},
"TaxPercent": {{1.VATRate}},
"TaxTotal": {{parseNumber(1.VATAmount; ".")}},
"GrandTotal": {{parseNumber(1.GrossTotal; ".")}},
"ChargeTotal": 0,
"AllowanceTotal": 0
},
"Payment": {
"Currency": "{{1.Currency}}",
"PaymentReference": "{{1.InvoiceNumber}}",
"DueDate": "{{formatDate(1.DueDate; "YYYY-MM-DD")}}T00:00:00"
}
}

该TypeCode有三个部分值得注意。TypeCode 380是商业发票的代码,它是固定的,并非源自任何基础UnitCode。UnitCode C62是联合国/欧洲经济委员会 (UN/ECE) 编码,表示“某物的一个”,也就是您用来表示服务时长或单位的代码。末尾的Payment块包含PaymentReference,这是买方付款时提供的文本,在这里就是发票号码。

Create a ZUGFeRD invoice配置​

Make PDF4me Create a ZUGFeRD invoice module showing File Name ZUGFeRD_ plus the InvoiceNumber token plus .pdf, an empty Document slot for a base PDF, Input Format JSON, an Invoice JSON Data block containing InvoiceId, TypeCode 380, InvoiceType RECHNUNG, IssueDate and DeliveryDate from formatDate expressions, Seller and Buyer objects with addresses, a LineItems array with UnitCode C62, a Totals object, then Output Mode XML With PDF, ZUGFeRD Version 2 star, Conformance Level EN16931 and Render Invoice on PDF set to Yes

该字段在编辑器中可以滚动,因此一次只能看到对象的一部分。上面的代码块就是它的全部内容。

每个设置决定什么​

场地这里的价值它控制着什么
Input FormatJSON请提供结构化对象,而不是现成的 XML 或 CSV 行。
Output ModeXML With PDF返回一个混合文件。XML XML Only返回发票 XML 文件。
ZUGFeRD Version2*2.x 代产品符合EN 16931标准,而旧版 1.0 产品则不符合。
Conformance LevelEN16931发票声称符合的规范,以及因此 JSON 必须提供的键。
Render Invoice on PDFYes根据相同的数据生成易于阅读的发票页面。由于没有提供基础 PDF 文件,这赋予了文件人性化的一面。
Document空的这里原本应该放置带有您信头的基础 PDF 文件。如果留空, PDF4me会渲染一个空白页面。

Tip.金额和数量密钥必须以纯数字形式传入,而不是带引号的字符串,这就是为什么它们要经过以下流程:parseNumber文本字段用引号括起来。"1005"如果是普通的,则会被拒绝。1005接受此操作,并且返回的错误信息将与模式相关,而不是与引号相关。请注意第二个参数:parseNumber(1.NetTotal; ".")告诉系统Make小数分隔符设为点号。省略点号,并设置一个会写入的区域设置。1005,00解析结果会出错而不是失败,这是最糟糕的结果。


步骤 3:如何将电子发票重新添加到记录中?​

目前流程:触发器加上PDF4me模块。

  1. 添加Airtable > Upload an attachment。
  2. 配置:
    • Base和Table:同步骤 1
    • Record ID:触发器中的id
    • Attachment field: Document
    • File: PDF4me的输出 - Create a ZUGFeRD invoice
    • File name: ZUGFeRD_ + InvoiceNumber + .pdf
    • Data:模块 2 的Document输出。
    • Content type: PDF file

Upload an attachment配置​

Make Airtable Upload an attachment module with Base PDF4me Live Automations, Table ZUGFeRD Requests, Record ID mapped from the trigger id, Attachment field Document, File set to the PDF4me Create a ZUGFeRD invoice output, File name ZUGFeRD_ plus the InvoiceNumber token plus .pdf, Data set to the module 2 Document output and Content type PDF file

此处的Content type为PDF file。瑞士 QR 码的对应版本则在同一位置使用 PNG 图片。


步骤 4:如何将结果记录在行中?​

目前流程:所有操作均通过附件上传完成。

  1. 添加Airtable > Update a record。
  2. 配置:
    • Base和Table:同步骤 1
    • Record ID:触发器中的id
    • Record:仅填写您希望写入的内容,通常为Status和OutputLink

Update a record配置​

Make Airtable Update a record module with Base PDF4me Live Automations, Table ZUGFeRD Requests, Record ID mapped from the trigger, and the Record section listing the table fields Title, InvoiceNumber, InvoiceDate, DueDate, Currency with its Map toggle enabled, SellerName, SellerStreet, SellerPostalCode, SellerCity and SellerCountry as empty inputs

表格中的每一列都会显示在这里。截图显示这些列为空,面板也处于滚动状态,所以请特意设置Status。

Tip.如果某个字段已映射但为空,则会清除该单元格。在发票表中,这意味着增值税号为空,或者在运行结果显示成功的情况下丢失了到期日,因此请不要修改任何您并非有意写入的内容。


第五步:如何向买家发送电子发票?​

目前流程:所有操作均已完成记录更新。

数据库中尚未开具电子发票。

  1. 添加Gmail > Send an email。
  2. 配置:
    • To:触发器中的Email列
    • Subject: Your e-invoice 及InvoiceNumber
    • Body type: Raw HTML
    • Content:一段简短的HTML正文,包含买家姓名、发票号码和总金额。
  3. 添加附件:
    • File: PDF4me - Create a ZUGFeRD invoice输出
    • File name: ZUGFeRD_ + InvoiceNumber + .pdf
    • Data:模块 2 的Document输出。

Send an email配置​

Make Gmail Send an email module with the recipient mapped from the Email column, Subject Your e-invoice plus the InvoiceNumber token, Body type Raw HTML, an HTML content block greeting the buyer and stating the gross total and currency, and one attachment taking the PDF4me module output with the file name ZUGFeRD_ plus InvoiceNumber plus .pdf

附件来自模块 2,因此Airtable永远不会读取该文件。


运行场景并验证​

  1. Save方案并运行一次。
  2. 向ZUGFeRD Requests中添加一行,其中包含卖家、买家、一个项目、两个日期和Status New。
  3. 五个模块都应该各自报告一项操作。
  4. 打开该行: Document字段应包含一个以发票编号命名的 PDF 文件。
  5. 打开附件。您应该可以看到一个可读的发票页面,并且ZUGFeRD验证器应该在同一个文件中找到EN 16931 XML 代码。
  6. 查看买家收件箱,找到邮件及其附件。

在捕获的运行中, RE-2026-0009行生成了一张来自Berlin Muster GmbH (增值税号DE123456789的发票,开给Muenchen Test Kunde AG,内容为 10 个单位的Consulting,每个单位 100.50,电子邮件报告的总金额为 1195.95 欧元。

Do not trust the grid for decimals.Airtable表格显示VATAmount 191, GrossTotal 1196,而发票和电子邮件中显示的分别为190.95和1195.95。表格并未重新计算: TaxTotal和GrandTotal直接取自这两列。表格只是按照数字字段设置的精度显示这些数值。在尝试将某一列与发票进行核对之前,请提高该精度,因为这些数字看起来不一致,但实际上并非如此。


无需重建即可添加的常见变体​

发票包含多行内容
LineItems已经是一个数组。将这些行保存在第二个链接表中,使用Search records功能获取它们,并在PDF4me模块之前使用聚合器构建数组。
把它放在你的信笺抬头上。
模块 2 中的Document ”插槽接受一个基础 PDF 文件。如果输入一个带有品牌标识的页面,“ Render Invoice on PDF功能会将发票绘制到 PDF 文件中,而不是空白文件上。
也要把 XML 文件单独保存。
有些买家需要单独的结构化文件。请将Output Mode设置为XML Only,再次运行该模块,并将结果与 PDF 文件一起附加。
每个买家可切换格式
添加格式列和路由。德国公共部门的采购人员通常需要XRechnung而不是混合文件,这是路由决策,而不是重建。

与其他电子发票生成方式相比,这种方式有何优势?​

每一行描述了一种替代方案。最后一行就是当前的情况。

比较谁在运营它数据来源你所维护的
电子发票平台与此场景的比较通过门户网站进行金融交易上传或集成按文件签订合同及其关联
ERP模块与此场景ERP系统中的财务部分ERP自身的账簿许可证,以及将您的数据迁移到新系统
开发者库与此场景应用程序内部的开发人员通过代码实现您的数据库代码、架构更新和版本发布
这种情况任何可以添加Airtable行的人团队已经保留的基础五个模块和一个 JSON 对象

常见问题​

什么是应付账款自动化?​

在接收端,这意味着发票到达后,无需人工输入即可读取其字段,核对数字,然后将结果发布或提交审批。混合型电子发票确保了读取步骤的可靠性,因为数据是嵌入在发票中的,而不是从页面图片中提取的。

这种情况属于该安排的发送环节。生成买方系统可以解析的文件,才能让他们的应付账款自动运行。

Airtable可以创建发票吗?​

不符合规范的。Airtable 可以存储Airtable数据,并将其布局在界面或可打印视图中,但它无法生成符合EN 16931 XML 标准的PDF/A-3文件,而这正是电子发票法规的要求。

此场景采用的拆分方式是实际可行的: Airtable是记录, PDF4me生成工件,工件作为附件返回到记录中。

应付账款自动化是如何运作的?​

它分三个部分运行:接收、提取和过账。接收是将文档集中到一个地方;提取是将文档转换为字段;过账是将这些字段写入账本,通常前面会设置一个置信度或审批机制。

混合式电子发票简化了中间环节。当 XML 数据已存在于文件中时,提取过程就变成了解析而非识别,这也是为什么相关法规不断推动这一方向发展的原因。

什么是Airtable自动化?​

Airtable自带自动化构建器,用于在数据库内部创建触发器和操作,这足以完成大部分内部工作。但它无法调用文档 API 并返回附件字段所需的文件,而这正是本场景所需要的步骤。

正是出于这个原因, Make才放在旁边。触发器仍然监视着一个Airtable表格,但场景中间部分可以访问Airtable自身自动化功能无法访问的服务。


故障排除​

该模块拒绝了发票 JSON 数据。

在运行历史记录中打开模块的输入包,并将其读取为 JSON 格式。如果Airtable列为空,则对应的键也为空;此外,如果货币值保留了引号,则该值会以文本形式返回,而预期是数字。这两种情况都会生成看起来没问题的对象,但转换仍然失败。

验证器拒绝了一致性级别

Conformance Level决定了配置文件所需的字段。如果 JSON 文件缺少EN16931配置文件所需的键,即使文件可以正常打开,验证也会失败。请补充缺失的键,或者降低配置文件的级别。

PDF 文件打开时空白。

Render Invoice on PDF已设置为“否”,且未提供基础 PDF 文件,因此嵌入的 XML 文件中没有任何内容。该文件仍然是有效的电子发票,但其内容部分为空。

买家说文件中没有 XML 文件

检查Output Mode是否为XML With PDF,然后检查模块 3 和 5 是否都从模块 2 获取数据。附加其他任何内容都会发送一个看起来正确的页面,但其中包含他们的软件无法读取的任何内容。


后续步骤​

授权请求通常以文件格式问题的形式出现。这实际上取决于您的发票数据存储在哪里,以及是否有任何程序可以读取这些数据。