Help us shape the future of Go! Take the 2026 Go Developer Survey

ofd-invoice

command
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

ofd-invoice

ofd-invoice 是 OFD 电子发票信息抽取与汇总工具,从增值税电子发票、铁路电子客票 OFD 文件中 读取结构化附件(通常为 Doc_0/Attachs/original_invoice.xml)或自定义标引,输出 JSON、CSV/XLSX 对账报表,并能把多张 发票合并为一个便于打印的 PDF。适合财务入账、对账和自动化处理。

使用本工具处理文档前,请阅读项目根目录的 免责声明。抽取结果来自 文件内容本身,不代表文件真实、完整、可信或具有法律效力。

功能

  • 优先读取 OFD 包内嵌的发票结构化附件(税控开具的电子普票/专票通常自带)。
  • 规范路径 Doc_0/Attachs/original_invoice.xml 不存在时,自动扫描所有名称含 attach 的 XML 条目,取第一个能解析出有效发票数据的文件;命名空间差异不影响字段匹配。
  • 没有有效附件时,沿 Document.xml 的 CustomTags 清单读取标引数据,按字段中的 ObjectRef 和 PageRef 定位页面文字,支持号码、日期、购销方、金额及明细。多段引用 按引用顺序拼接,缺少页面或文字对象时明确报错。attachment 字段记录实际使用的 标引数据文件路径,例如 Doc_0/Tags/CustomTag.xml。
  • 抽取字段包括:发票标题、类型、机器编号、代码、号码、开票日期、校验码、不含税金额、 税额、价税合计(小写与大写)、税控码、收款人/复核人/开票人、购销方资料(名称、税号、 地址电话、开户账号)以及逐行价税明细(名称、规格、单位、数量、单价、金额、税率、税额)。
  • 金额、数量、税率按十进制精确表示(基于任意精度有理数),税率兼容 13%、13 和 0.13 三种写法并归一化为小数。
  • 除自定义标引明确引用的字段外,页面文字层用于补齐票面标题与价税合计大写金额, 并按标题推断类型(普通/专用/通行费/铁路电子客票); 页面读取失败不中断附件抽取,只记录警告。
  • 结构化数据有效性按发票标志字段判定:必须带发票代码、铁路电子客票号或价税合计(取或,定额 发票可能没代码、代码齐全的票也可能缺金额)。只有购方名称之类的零散字段不算——Attachs 里可能放着版式说明、签章属性之类的 XML,兜一个几乎全空的 Invoice 会让调用方看不出 出错,汇总还会安静地算出 0 元。不满足时报"不是发票"(ErrNotInvoice)而不是空壳; 没有 attach XML 和自定义标引数据时返回 ErrNoAttachment,两者在库里是不同的错误。不处理 PDF 等 非 OFD 输入。
  • 自校验:比对同一张票内部的金额关系(不含税金额 + 税额是否等于价税合计、逐行明细 汇总是否等于票面金额),不自洽的发票在结果与报表的异常列中标出。
  • 多票汇总:金额按最小货币单位(分)四舍五入后求和;红字(红冲)发票的负金额照常 参与合计;合计按销售方(按纳税人识别号)、发票类型和开票月份分组。
  • 重复识别:发票代码与号码相同视为同一张。默认只标记不丢弃,重复项仍参与合计; --dedup 时重复项移入 skipped 且不计入合计。
  • 对账报表:CSV(UTF-8 BOM + CRLF,Excel 直接打开)与 XLSX。明细、分组汇总、 异常与跳过三个工作表,统一版式:标题 + 副标题(带张数与三项合计)+ 第 4 行表头 + 冻结窗格 + 隔行底色 + 自动筛选;金额是带两位小数的数值单元格,负数标红,合计行 用粗顶线与明细分开。
  • 合并打印:把多张发票合并为一个 PDF,保留矢量文字与按票划分的大纲,可在首页插入 一张汇总对账表。

构建

在项目根目录执行:

go build -o ofd-invoice ./cmd/ofd-invoice

快速开始

命令格式:

ofd-invoice [选项] input.ofd
# 输出紧凑 JSON 到标准输出(单张发票保持单个 Invoice 对象)
ofd-invoice invoice.ofd

# 输出缩进后的 JSON
ofd-invoice --pretty invoice.ofd

# 写入文件
ofd-invoice -o invoice.json invoice.ofd
ofd-invoice --pretty -o invoice.json invoice.ofd

# 多张发票汇总(输出含 items 与 summary 的批量 JSON)
ofd-invoice --pretty a.ofd b.ofd c.ofd

# 批目录,按开票日期排序;跳过抽不出附件的文件继续处理
ofd-invoice --input-dir ./发票 --sort date --skip-broken

# 导出对账报表(格式按扩展名推断,也可用 --format 指定)
ofd-invoice -o 九月对账.csv --input-dir ./九月
ofd-invoice -o 九月对账.xlsx --input-dir ./九月
ofd-invoice --format xlsx -o 对账.xlsx --input-dir ./九月 --sort date

# 合并成一个 PDF,首页放汇总对账表;按发票号码排序
ofd-invoice --merge-pdf 九月.pdf --cover --sort code --input-dir ./九月

未指定 -o / --output 时输出到标准输出;输出路径为 - 同样表示标准输出。 工具不允许输出文件覆盖任何一张输入的 OFD 文件,也不允许与 --merge-pdf 指向同一个文件。

输出形态:单个输入、只要 JSON、且没有改变处理口径(未用 --skip-broken / --dedup)时, 输出仍是单个 Invoice 对象,已有脚本无需改动;多张发票、批目录或 CSV/XLSX 报表时输出批量 形态 {items, skipped, summary},字段与 github.com/zc310/ofd/pkg/invoice 的模型一致, 字段缺失时为空或不出现。金额在 JSON 里始终是十进制字符串(如 "113.00"),不要当数字 解析后再转回文本,否则会丢小数位。

命令选项

选项 默认值 说明
-o, --output PATH 标准输出 输出路径,使用 - 输出到标准输出
--format FORMAT 按扩展名 输出格式 json、csv 或 xlsx
--pretty 关闭 缩进 JSON 输出(只适用于 JSON)
--input-dir DIR 空 批量处理目录下全部 .ofd / .zip 文件,按名排序
--skip-broken 关闭 跳过抽取失败的输入继续处理(默认整批报错)
--dedup 关闭 按发票代码与号码去重,重复票不计入合计
--sort KEY input 页序与报表排序:input、date、code、name
--merge-pdf PATH 空 把全部发票合并为一个 PDF 并写入该路径
--cover 关闭 合并 PDF 时在首页插入汇总对账页(需 --merge-pdf)
--cover-title TITLE 默认标题 汇总对账页标题
-h, --help 关闭 显示命令帮助
--version 关闭 输出工具版本

布尔选项不带值

--cover、--pretty、--dedup、--skip-broken、--cell-border 都是布尔开关,写 --cover 或 --cover=true,不能写 --cover true:后面的 true 会被当成输入文件 路径(报"输入文件 true 不存在")。

退出码

  • 0:抽取成功,结果已输出;--version 输出版本号也算成功。
  • 1:抽取或输出失败(例如未找到发票结构化附件、附件不是发票数据、解析失败、写出文件失败、合并 PDF 失败)。
  • 2:命令参数或输入输出配置无效(缺少输入、格式不支持、--cover 未配合 --merge-pdf、 输出覆盖输入等)。

合并 PDF 的行为

  • 页序由 --sort 决定。--cover 的汇总页排在最前,并且独占一整张纸——它不会跟着 --nup 挤进九宫格的某一格。指定了 --paper 时汇总页与票页用同一张纸,但汇总页恒为 纵向:--orientation auto 可能把票页选成横向(例如 --nup 4 时 210×140 的票在 2×2 横向更省),汇总页仍取该纸张的纵向,版心与列宽按实际尺寸重算。
  • 合并走「逐张解析成渲染文档,自己按版面输出 PDF」,保留矢量文字和按票划分的大纲, 不生成中间文件;每张票在大纲里有一条,拼版时指向它所在的那张纸与格子。
  • 被去重跳过和抽取失败的发票不会进入 PDF,但会出现在 -o 输出的汇总结果里。
  • 单张发票默认 64MB、全部输入合计 512MB、最多 1000 张,超限直接报错。
纸张与拼版
# 非 A4 的票(卷式发票 210×140、火车票等)居中补白到 A4 打印
ofd-invoice --merge-pdf 补白.pdf --paper A4 --input-dir ./九月

# 一张 A4 纵向放两张票(1:1 原始尺寸),并画出裁切线
ofd-invoice --merge-pdf 两格.pdf --nup 2 --cell-border --input-dir ./九月

# 四格 2×2 + 5mm 间距
ofd-invoice --merge-pdf 四格.pdf --nup 4 --nup-gap 5 --input-dir ./九月

# 票源是竖高版式时改用横向
ofd-invoice --merge-pdf 横向.pdf --nup 2 --orientation landscape --input-dir ./九月
  • 不给 --paper 且 --nup 1(默认):每页按源页自身尺寸 1:1 输出,不缩放不补白。

  • --fit 只有 contain:等比缩放并居中补白。不提供「填满裁切」——裁切会切掉票面 边缘的印章与价税合计,对发票等于毁票。

  • 票面永不被放大。源页比格子小时按 1:1 印、多出来的部分留白:票面的物理尺寸本身 就是信息(定额发票的票面大小、火车票票幅),放大后打印件与票面对不上。也不提供填满 裁切——裁切会切掉票面边缘的印章与价税合计。

  • 拼版份数只给约定俗成的档位,不给的份数直接报错,不做任意排布。网格固定为纵向: 两格、三格是单列纵向拼接,四格起才分列(4 是 2×2、6 是 2×3、8 是 2×4、9 是 3×3)。

  • 方向默认 auto:比较纵向与横向的占用率(源页面等比缩放后占格子面积的比例), 取高者。规则由票源长宽比与格子长宽比是否匹配决定,不由格子行列数决定。以 210×140 的 卷式票为例:

    --nup 纵向占用率 横向占用率 auto 选
    2(格子 210×148.5 / 297×105) 94% 53% 纵向,票 1:1
    4(格子 105×148.5 / 148.5×105) 47% 94% 横向,2×2

    同一个票源,2 格与 4 格的最优方向相反——按"列数是否多于行数"判方向会把这两种都选错。 需要固定方向时用 --orientation portrait 或 landscape 覆盖。

  • 拼版页不写文字标注(页码、发票号)。矢量绘制接口目前没有文字绘制入口,只有字形 轮廓;--cell-border 的裁切线不受影响。

  • --cell-border 用灰色虚线(0xBF、0.2mm、3mm 实 4mm 空,空白比实线段长,密集短线在 票据打印分辨率下会糊成灰实线),画在实际放了票的格子 周围;实线容易被当成票面的一部分,虚线一眼能认出是后加的辅助线。一页一票 (未指定 --paper 且 --nup 1)时纸张就是页面本身,没有可裁的东西,因此不画框。

按票取页
# 每张票只打第 1 页
ofd-invoice --merge-pdf 首页.pdf --pages first --input-dir ./九月

# 每张票只打票面页(页面上有发票代码、发票号码、价税合计或开票日期字样的那页)
ofd-invoice --merge-pdf 票面.pdf --pages face --input-dir ./九月

# 按票内页码取
ofd-invoice --merge-pdf 清单.pdf --pages 1,3-5 --input-dir ./九月

电子普票常见「第 1 页发票清单 + 第 2 页票面」,这种票用 --pages first 会拿到清单页, 应该用 --pages face。face 靠文字层识别,认不出时退回第 1 页而不是不输出——发票票面 信息在附件 XML 里,金额汇总不受取页影响。

批量口径

  • 默认严格:任一输入抽不出发票结构化数据,整批报错并返回 1。--skip-broken 打开后 失败项记入结果并继续,避免整批金额在无人察觉时少算。
  • 金额:按精确十进制求和后四舍五入到分,输出 100.00 这类定长形态;红字发票的负金额 参与合计,--dedup 与排序都不改变金额口径。
  • 分组:销售方按纳税人识别号分组(税号相同视为同一销售方),发票类型与开票月份按票面 值分组,缺失值归入「未知」而不是被丢弃。

CI 示例

ofd-invoice --pretty -o invoice.json invoice.ofd
status=$?
test "$status" -eq 0

Documentation

Overview

Command ofd-invoice 从 OFD 电子发票中抽取结构化发票信息,汇总多张发票的金额, 并支持把多张发票合并为一个便于打印的 PDF。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL