Community写作与编辑github.com

FIZ-invoicing 开票技能

使用 FIZ 公共 API 为葡萄牙开具发票(含 IVA、免税、CAE、NIF、AT 集成)——适用于 Claude Code 等 AI 代理。

FIZ-invoicing 开票技能 是什么?

FIZ-invoicing 技能为 AI 代理(如 Claude Code、Cursor、Codex)提供通过 FIZ 公共 API (`https://api.fiz.co`) 开具有关葡萄牙税务发票的完整工作流。其核心用途是生成合法合规的葡萄牙税务文件,需要跨境处理 IVA 税率、免税理由代码、CAE 经济活动和 NIF 税号等复杂税务规则。技能的核心安全机制是:在执行任何写操作前预览所有请求(包括创建客户、商品和发票草稿),并在最终开票(`POST /invoices/:id/issue`)前获取用户的明确单独确认——因为开票是不可逆的,且需要与葡萄牙税务部门(AT)同步。技能支持通过环境变量(`FIZ_API_KEY`)安全配置 API 密钥,并提供 `fiz.sh` 辅助脚本自动处理 HTTP 状态码检查,避免将错误误认为成功。关键领域知识包括:默认使用 `NORMAL` 税率,除非明确适用降低税率类别;对于免税商品必须设置合法的 `vatExemptionReason` M 代码;以及发票类型(如 `INVOICE`、`INVOICE_RECEIPT`)对付款和客户信息要求的不同规则。该技能配备了一个 `domain.md` 文件,详细解释葡萄牙税务领域,以及一个 `reference.md` 文件,提供完整 API 字段参考。

兼容平台✓Claude Code✓Codex CLI~Cursor
npx skills add FIZ-co/fiz-invoicing-skill

Installed? Explore more 写作与编辑 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

FIZ 开票

使用 FIZ 公共 API(https://api.fiz.co)开具发票。 本技能将指导您完成完整的工作流:查找或创建客户、查找或创建商品、构建草稿发票、开具发票(这是具有法律约束力的步骤),并可选择下载 PDF。

设置

每个请求都需要在 x-api-key 头部中包含客户端的 API 密钥。可在 https://app.fiz.co/settings/integrations 获取密钥。

将其存储在环境变量中,以免硬编码到命令中:

export FIZ_API_KEY="fiz_api_..."     # 来自 app.fiz.co 的用户密钥
export FIZ_API_URL="https://api.fiz.co"   # 可选;这是默认值

以下所有示例都使用 $FIZ_API_KEY 和 $FIZ_API_URL。如果 FIZ_API_URL 未设置,则使用 https://api.fiz.co。如果 FIZ_API_KEY 未设置,请停止并告知用户在其环境变量中设置它(在他们的 shell 中执行 export FIZ_API_KEY=…,或使用密钥管理器)——不要要求他们将密钥粘贴到聊天中,并且永远不要打印或回显密钥值。API 密钥是一种凭据;请将其排除在对话记录之外。

scripts/fiz.sh 中提供了一个可重用的 curl 辅助脚本。命令从会话的工作目录(您的项目根目录)运行,而不是技能目录,因此通过 ${CLAUDE_SKILL_DIR}(Claude Code 将其设置为技能自身的目录)的绝对路径来加载该辅助脚本:

source "${CLAUDE_SKILL_DIR}/scripts/fiz.sh"   # 从任何工作目录解析
fiz GET /invoices

在未提供 CLAUDE_SKILL_DIR 的运行时中,请替换为该运行时的等效技能目录路径(从项目根目录无法解析裸的相对路径 scripts/fiz.sh)。

辅助脚本会打印 HTTP 状态,并在 4xx/5xx 情况下返回非零值,因此错误响应永远不会被误认为成功——请参阅下面的“错误处理”。本文也提供了内联的纯 curl 示例;如果使用原始 curl,请始终检查 HTTP 状态,而不仅仅是命令是否退出 0。

思维模型

  • 发票引用一个客户(按 ID)和一个或多个商品(按 ID)。因此,客户和商品必须在构建发票之前存在。
  • POST /invoices 创建草稿。草稿还不是法律文件——它不会发送给税务机关,并且可以自由编辑或删除。
  • POST /invoices/:id/issue 开具发票——分配官方编号和 ATCUD,与葡萄牙税务机关(AT)同步,并使其成为最终文件。开票是不可逆的;在开票前请与用户确认。 已开具的发票无法删除——唯一撤销的方式是开具贷项通知单。
  • “先创建后开具”的唯一例外:如果在创建时为 INVOICE_RECEIPT 或 SIMPLIFIED_INVOICE 类型传递了 payment 对象,则文档会记录付款——但您仍需开具它以完成最终化。

写入前的确认

以下端点会更改用户账户的状态。需要两个级别的谨慎处理:

  • 计划,然后预览所有写入操作,在第一个操作之前。 一旦您知道请求需要什么,请列出您计划为此任务执行的每个写入操作——每个端点及其 JSON 负载(要创建的客户、每个商品、草稿发票)——并在一个预览中向用户显示该批次。一旦他们批准该计划,再执行这些写入操作。如果在运行过程中计划发生变化(例如商品查找失败,您现在需要创建一个),请在发送前预览新的写入操作。切勿编造客户数据、税务字段或金额——只能使用用户提供的信息。
  • 在 POST /invoices/:id/issue 之前——这是不可逆的,并且会报告给税务机关。始终获取明确的、单独的确认(超出上述计划批准),显示草稿的客户、明细项、增值税和总计。不要主动开具发票。

调用策略(故意为之): 本技能保持模型自动调用启用状态——description 是发现面,真正的安全网是上述的“预览所有写入 + 单独确认开票”机制,而不是限制发现。Codex 清单(agents/openai.yaml)设置为匹配(allow_implicit_invocation: true)。希望仅限手动使用的操作者可以在文件头设置 disable-model-invocation: true(仅当以 /fiz-invoicing 加载时生效)。

这是葡萄牙税务发票——为什么正确性至关重要

FIZ 开具具有法律约束力的税务文件,这些文件会报告给葡萄牙税务机关(Autoridade Tributária,简称“AT”)。错误的增值税税率或遗漏的免税理由不仅仅是外观上的错误——它会产生一份不正确的税务文件,一旦开具,只能通过贷项通知单进行更正。在开具之前,请确保税务字段正确。

最容易出错的两件事,您必须仔细推理:

1. 增值税税率(每个商品上的 vatRate)——切勿猜测。 税率档位(NORMAL | INTERMEDIATE | REDUCED | EXEMPT)及其对应的百分比取决于地区(大陆 / 亚速尔群岛 / 马德拉群岛)。默认为 NORMAL——REDUCED/INTERMEDIATE 仅适用于特定法律定义的类别(某些食品、餐厅、文化活动……)。如果用户未提及此类类别,请使用 NORMAL 并说明原因,或询问用户;切勿静默地应用降低税率。各地区税率百分比表见 domain.md。

2. 增值税免税需要理由代码(vatExemptionReason)。 如果 vatRate 为 EXEMPT,葡萄牙法律要求提供 motivo de isenção——一个说明法律依据的 M 代码。始终设置一个;没有有效理由的免税商品是无效文档,并在您开具时可能被拒绝。该代码必须反映实际理由:小企业制度 → M10,活动性质上免税(健康/教育)→ M07,欧盟 B2B / 反向征收 → M40/M16/M19。如果用户是免税的但不知道代码,请询问原因并映射——不要随意选择。完整代码列表及法律参考见 domain.md。

其他值得了解的领域规则(详情见 domain.md):

  • CAE 是一个 5 位数的葡萄牙经济活动代码,必须是企业已注册的代码。不要编造——询问用户,或从之前的发票中重用 CAE。必须是当前(Rev. 4)的代码。
  • NIF(taxpayerNumber)是一个 9 位数的葡萄牙税号,含有校验位;B2B 必填,最终消费者可选。对于欧盟 B2B,客户的增值税号对反向征收处理很重要。
  • 预扣税(retenção na fonte:IRS/IRC/IS)在商品上配置;自雇服务通常税率为 25%。由付款方预扣,减少实际支付的金额。
  • 单据类型影响规则:SIMPLIFIED_INVOICE 用于小额零售销售,可以省略完整的客户信息;对于超过 €1,000 的企业 INVOICE 需要客户 NIF。

工作流

请遵循以下步骤。如果用户已提供客户 ID 和商品 ID,请跳过步骤 1-2。

步骤 1 — 解析客户

先搜索以避免重复,然后在需要时创建。

示例使用 fiz 辅助脚本(参见设置)——它会为您检查 HTTP 状态。等效的原始 curl 见附录。

按搜索词(名称、税号、电子邮件)查找:

fiz GET "/customers?search=Joao"

创建客户(仅 name 为必填):

fiz POST /customers '{
  "name": "João Silva",
  "email": "[email protected]",
  "taxpayerNumber": "303741791",
  "country": "PT"
}'

保留返回的 id——即发票的 customerId。

步骤 2 — 解析商品(产品/服务)

查找现有商品:

fiz GET "/items?search=consulting"

仅当现有商品的税务字段与本次销售匹配时才重用。 名称匹配的搜索结果不足——对于税务文件,商品的 vatRate、vatTerritory、vatExemptionReason、taxRate 和预扣字段必须与您打算为此客户和销售使用的处理方式一致。如果其中任何一个不同(例如,同一服务但客户免税、位于其他地区或需预扣税),请创建一个具有正确字段的新商品,而不是重用不匹配的商品。不确定时,检查找到的商品的字段并与用户确认。

创建商品。必填字段:name、type(PRODUCT | SERVICE)、unitPrice、vatRate(NORMAL | INTERMEDIATE | REDUCED | EXEMPT):

fiz POST /items '{
  "name": "Consulting hour",
  "type": "SERVICE",
  "unitPrice": 80,
  "vatRate": "NORMAL"
}'

保留每个返回的 id——这些是发票的商品 ID。

unitPrice 是单价;每行总额由 FIZ 根据您在发票中提供的 quantity 计算。慎重选择 vatRate——请参见上面的领域部分;默认使用 NORMAL,除非该类目明显符合降低税率。如果 vatRate 为 EXEMPT,请始终设置一个与销售免税原因匹配的 vatExemptionReason M 代码(例如,小企业制度用 M10)。该字段在商品创建时技术上可选,但没有有效理由的免税商品是不正确的,并且可能在您稍后开具发票时被拒绝。完整代码列表见 domain.md。

步骤 3 — 创建草稿发票

必填字段:dueDate(ISO 8601)、cae(葡萄牙经济活动代码)、type、customerId 和 items(至少一个 { id, quantity })。

fiz POST /invoices '{
  "dueDate": "2026-07-18T00:00:00.000Z",
  "cae": "62010",
  "type": "INVOICE",
  "customerId": "6863b1513117c5892ff55296",
  "items": [
    { "id": "68483e978073231c3947077c", "quantity": 10 }
  ]
}'

响应包含新发票的 id(用于开具)和 status(DRAFT)。发票的 date 设置为当前时间,currency 由 API 设置为 EUR——您无需发送它们。

可选——全局折扣(summary):

"summary": { "globalDiscountType": "PERCENT", "globalDiscountPercent": 10 }

如需固定金额折扣,请使用 "AMOUNT" 及 globalDiscountAmount。

可选——创建时付款(payment):仅当 type 为 INVOICE_RECEIPT 或 SIMPLIFIED_INVOICE 时允许;否则 API 返回 400。

"payment": { "method": "mbWay", "date": "2026-07-18T00:00:00.000Z" }

method 可选值:cash、card、bankTransfer、mbWay、multibanco、spin、other。

步骤 4 — 开具发票

这是具有约束力的步骤。与用户确认后,执行:

fiz POST /invoices/{id}/issue

响应包含官方 number、status: ISSUED 和一个 syncWithAt 块。检查 syncWithAt.status——如果为 FAILED,则发票已在本地创建但未与税务机关同步;请向用户报告 atMessage 以及 isRetriable 是否为 true,而不是声称成功。

步骤 5 — 下载 PDF(可选)

fiz GET /invoices/{id}/pdf

返回 { id, name, url };url 是 PDF 的可下载链接。添加 ?format=A4 或 ?format=RECEIPT 以选择布局。

单据类型(type)

可选值:INVOICE · INVOICE_RECEIPT · SIMPLIFIED_INVOICE · RECEIPT · CREDIT_NOTE · DEBIT_NOTE。默认使用 INVOICE,除非用户要求收据或简化发票。只有 INVOICE_RECEIPT 和 SIMPLIFIED_INVOICE 在创建时接受 payment 对象。每种类型的含义及使用时机请参见 domain.md。

常用操作参考

操作方法及路径
列出/搜索客户GET /customers?search=…
创建客户POST /customers
列出/搜索商品GET /items?search=…
创建商品POST /items
创建草稿POST /invoices
开具发票POST /invoices/:id/issue
列出发票GET /invoices
获取单个发票GET /invoices/:id
下载 PDFGET /invoices/:id/pdf
删除(草稿)DELETE /invoices/:id

错误处理

始终检查 HTTP 状态,而不仅仅是命令是否运行成功。 出错时,API 返回一个看起来正常的 JSON 主体——{ "statusCode": 400, "message": "...", "timestamp": "..." }——并带有匹配的 HTTP 状态。使用原始 curl -s 时,该主体会打印出来,而 curl 仍然以 0 退出,因此很容易将错误误认为结果。fiz 辅助脚本可防止这一点:它会打印一行 HTTP <code> 并在 4xx/5xx 时返回非零值。如果您必须使用原始 curl,请添加 -w '\n%{http_code}'(或 -o body -w '%{http_code}')并检查代码。

常见状态:

  • 401 —— 缺少/无效的 API 密钥。告知用户在其环境中设置 FIZ_API_KEY(不要在聊天中)。
  • 400 —— 验证错误;message 会指明哪个字段。常见情况:空的 items、在不受支持的 type 上使用 payment、未知/额外的字段(严格白名单)、格式错误的 dueDate(必须是完整的 ISO 8601 格式且带时区,例如 2026-07-18T00:00:00.000Z)。
  • 5xx —— 后端/AT 问题;向用户展示,不要盲目重试相同的写入操作(创建可能部分成功——请先通过 GET 检查)。

始终将 API 的 message 回显给用户,而不是盲目重试。

完整、权威的请求/响应合同在 https://api.fiz.co/ 的实时 Swagger UI 上——如果本文中的某个字段看起来已过时,请查阅该文档。

当您需要更多详细信息时

本技能目录中有两个配套文件:

  • domain.md —— 葡萄牙税务领域:各地区增值税税率、完整的免税(M)代码列表及其适用场景、单据类型、CAE/NIF 规则、预扣税以及 AT 同步/开票不可逆性的工作原理。每当税务决策不明确时(尤其是选择 vatRate 或免税理由时),请阅读此文件。
  • reference.md —— 完整的逐字段 API 参考(每个客户/商品/发票字段、所有枚举值、完整的请求/响应结构)。

附录 — 原始 curl(不使用辅助脚本)

优先使用上面的 fiz 辅助脚本;它会检查 HTTP 状态。如果不可用,请使用原始 curl 并带上 -w '\n%{http_code}' 并检查代码。

相关技能