# 各类票据的真实文本层形态

下面每一段，都是把**按真实票面版式构造的夹具**用 `Read` 读一遍后实测拿到的输出。版式、换行、
空白折叠、竖排拆行、NUL 字节都来自实际运行结果，不是示意图；但票据本身是合成的，人名、公司名、
税号均为虚构值（`invoice-extract` 里 OFD 一节引用的样本才是第三方真实票据）。
写抽取规则时对照这里的形态，不要凭想象拼正则。

⚠️ **有一条不能当成普遍事实**：下面反复出现的「买卖方靠出现顺序区分」，在这些夹具上成立，
但顺序取决于**生成这份 PDF 的开票系统的坐标布局**，不是国标保证的。真碰上顺序相反的开票系统，
每张票的买卖方都会静默对调，而勾稽校验、金额校验一条都不会报错。
`invoice-extract` 的通用规则第 3 条给了自校验办法（拿用户的报销主体抬头回验顺序），务必照做。

---

## 1. 数电票（电子发票，2023 年后主流）

```
电子发票（增值税专用发票）
（全国统一电子发票服务平台）
发票号码：24312000000000020002
开票日期：2024年04月08日
购
买
方
信
息
销
售
方
信
息
名 称：示例数字科技（北京）有限公司
统一社会信用代码/纳税人识别号：
91110108MA01X2Y3QK
名 称：示范酒店管理有限公司
统一社会信用代码/纳税人识别号：
91310104MA1FL9K3T6
项目名称 规格型号 单位 数量 单价 金额 税率/征收率 税额
*住宿服务*住宿费 标准间 晚 4 462.26 1849.04 6% 110.94
合 计 ¥1849.04 ¥110.94
价税合计（大写）   壹仟玖佰伍拾玖元玖角捌分 （小写）¥1959.98
备
注
出差住宿 4 晚
开票人：陈静
```

要点：

- 抬头行区分票种：`电子发票（增值税专用发票）` = 数电专票，`电子发票（普通发票）` = 数电普票。
- **只有 20 位发票号码，没有发票代码。** 别去找 `发票代码：`。
- 竖排字段名被逐字拆成单行（`购/买/方/信/息`、`销/售/方/信/息`、`备/注`）。这是版式文档竖排文本的必然结果，不是错误——按「单个汉字独占一行」的模式整段忽略即可。
- **买卖方靠出现顺序区分。** 两方各有一次 `名 称：` + `统一社会信用代码/纳税人识别号：`，**先出现的是购买方，后出现的是销售方**。字段名完全一样，只靠前缀分不出来。**这个顺序必须用报销主体抬头回验一次**（见 `invoice-extract` 通用规则第 3 条）——顺序相反的开票系统会让两方静默对调，没有任何校验能发现。
- 税号往往换行在字段名的下一行，不在同一行。
- `名 称` 中间有一个空格（全角排版所致）。匹配时把空白折叠掉再比对，别写死 `名 称：`。

---

## 2. 旧版增值税电子普通发票（2023 年前）

```
浙江增值税电子普通发票
（此发票为电子发票，与增值税普通发票具有同等法律效力）
发票代码：033002100211
发票号码：41250933
开票日期：2023年03月29日
校 验 码：55019 27384 60172 39948
购
买
方
名 称：测试云图信息技术（上海）有限公司
纳税人识别号：91310115MA1K88N7XQ
地 址、电 话：北京市海淀区示例路 1 号 010-88880001
开户行及账号：示例银行北京分行 110060000012345678
密码区
0<7/*>3-25+8/91<<->406*2/57
>>1*4-08/6<32+95*17-0/4>>86
5/2*<91+3-70*4<6/8>21*05-39
4-8>05*/13<27+6*90/4-<851>2
货物或应税劳务、服务名称 规格型号 单位 数量 单价 金额 税率 税额
*会议服务*会议服务费 场 1 12264.15 12264.15 6% 735.85
合 计 ¥12264.15 ¥735.85
价税合计（大写）   壹万叁仟元整 （小写）¥13000.00
销
售
方
名 称：虚构会议服务有限公司
纳税人识别号：91330106MA2AB5D8JC
地 址、电 话：北京市朝阳区样例大街 88 号 010-66660002
开户行及账号：示例银行朝阳支行 110060000087654321
备注
2023 春季技术交流会
收款人：李娜 复核：孙涛 开票人：郑昊 销售方：（章）
```

与数电票的差别：

- 12 位 `发票代码：` + 8 位 `发票号码：`，两者一起才是主键。
- 字段名是 `纳税人识别号`，不是 `统一社会信用代码/纳税人识别号`。
- 有 `校 验 码：`（4 组 5 位数字）。
- **购买方在上、销售方在下**，中间隔着密码区与明细表——不是数电票那种交替排列。
- `密码区` 下面 4 行是无语义乱码，**抽取时整段丢弃**。
- 明细表头是 `货物或应税劳务、服务名称`。
- 抬头行常带省份前缀（`浙江增值税电子普通发票`）。

---

## 3. 铁路电子客票报销凭证

两种形态，都会遇到：

**（a）新版：走数电票版式**

```
电子发票（铁路电子客票）
（全国统一电子发票服务平台）
发票号码：24112000000000800080
开票日期：2024年09月02日
…（购买方 / 销售方与数电票同构）…
*运输服务*旅客运输服务 二等座 张 1 507.34 507.34 9% 45.66
合 计 ¥507.34 ¥45.66
价税合计（大写）   伍佰伍拾叁元整 （小写）¥553.00
电子客票号 E123456789；乘车人 李明；身份证 1101**********0018
车 次：G103 席别：二等座
行 程：北京南 ⇒ 上海虹桥
发车时间：2024-09-02 08:00
```

**（b）旧版：独立版式，没有 `发票号码：` 标签**

```
铁路电子客票报销凭证
220310010012345678901 开票日期：2023-05-11
上海虹桥 G7331 杭州东
2023-05-11 14:23开 05车 12A 一等座
票价：¥90.00 金额 82.57 税率 9% 税额 7.43
1101**********0018 李明
购买方名称：示例数字科技（北京）有限公司
统一社会信用代码：91110108MA01X2Y3QK
报销凭证 仅供报销使用 报销凭证，非退票凭证；限乘当日当次车
销售方：示例铁路运输集团有限公司 统一社会信用代码：91110000MA02RL7X3B
价税合计（大写）玖拾元整 （小写）¥90.00
```

旧版的坑：抬头行下面那串 21 位数字是**电子客票号**，不是发票号码，但它是这张凭证唯一的稳定标识——`invoiceNumber` 就填它，并在 `invoiceType` 里写明是铁路电子客票，`confidence` 上限给 0.9。开票日期在同一行的 `开票日期：` 后面。

---

## 4. 航空运输电子客票行程单

```
航空运输电子客票行程单
ITINERARY / RECEIPT OF e-TICKET FOR AIR TRANSPORT
印刷序号：03987654321
旅客姓名 李明 有效身份证件号码 1101**********0018
电子客票号码 999-2345678901 验证码 8Q2K4M
承运人 示例航空 ZZ 航班号 ZZ5237
自 FROM 北京/首都T2 至 TO 广州/白云T1
日期 DATE 2024-10-08 时间 TIME 09:45
座位等级 经济舱 Y 客票级别 Y
票价 ¥1247.71 民航发展基金 ¥50.00
燃油附加费/税额 ¥112.29 其他税费 ¥0.00
合计金额 ¥1410.00 填开日期 2024-09-25
销售单位代号 08-1234567 GP 单号 GP03987654321
购买方名称：示例数字科技（北京）有限公司
购买方统一社会信用代码/纳税人识别号：91110108MA01X2Y3QK
销售方：示例航空股份有限公司 纳税人识别号：91110000MA02QK8N5D
价税合计（大写）壹仟肆佰壹拾元整 （小写）¥1410.00
```

要点：

- **没有发票号码**。用 `电子客票号码`（`999-2345678901`）当 `invoiceNumber`，`印刷序号` 记进 `checkCode`。
- `invoiceDate` 取 `填开日期`（这里是 `2024-09-25`），**不是** `日期 DATE`（那是航班日期）。这两个日期跨月时会把票记到错误的月份。
- `合计金额` 就是价税合计；`票价 + 民航发展基金 + 燃油附加费/税额 + 其他税费` 应当等于它，可以拿来做勾稽校验。
- 这类行程单常常没有单独的税额行，`taxAmount` 留空而不是填 0。

---

## 5. 作废票

作废标记出现在文本层，两处：

```
备
注
开票信息有误，已作废
开票人：陈静
作废
```

命中 `作废` 或 `已作废` → `isVoid: true`。

**文本里没有不代表没作废**——真实世界的作废戳也可能是纯图形。所以只要 `isVoid` 判定依赖的是「文本里没找到」，就不要把它当强结论；台账里作废票单独一栏，不计入合计。

---

## 6. 免税 / 不征税

税率列不是百分比：

```
*非学历教育服务*职业技能培训费 人次 12 480.00 5760.00 免税 0.00
合 计 ¥5760.00 ¥0.00
```

`taxRate` 原样记 `免税`（也可能是 `不征税`、`***`、`0%`），不要强行转成数字 0。`amountExcludingTax == totalAmount` 且 `taxAmount == 0` 是正常的。

---

## 7. 外币结算

票面仍是人民币，原币信息在备注里：

```
*技术服务*境外技术服务费 项 1 13396.23 13396.23 6% 803.77
合 计 ¥13396.23 ¥803.77
价税合计（大写）   壹万肆仟贰佰元整 （小写）¥14200.00
备
注
原币金额 USD 2,000.00；结算汇率 7.1000；折合人民币 14200.00
开票人：何雪 结算币种：USD
```

`totalAmount` 记 `14200.00`，`currency` 记 `CNY`（票面币种）。原币信息照抄进备注字段，**不要**自己做汇率换算，也不要把 `currency` 改成 `USD`。

---

## 8. 负样本：对账单

```
供应商往来对账单
账期：2024 年 9 月 1 日 — 2024 年 9 月 30 日
供应方：样例办公用品销售有限公司
需方：示例数字科技（北京）有限公司
日期 关联发票号码 业务摘要 金额（元）
2024-09-03 24112000000000040004 办公用品采购 2338.05
2024-09-11 24322000000000050005 运输服务 6000.00
本期合计 29658.05
说明：本对账单仅用于双方核对往来款项，不是发票，不得作为报销与抵扣凭证。
```

**这份文件里有 20 位数字串，而且不止一个。** 只按数字模式匹配一定误判成发票（还会一次误判出好几张）。判据必须是**带标签的**发票号码（`发票号码：` 紧跟数字）加上 `价税合计` 同时出现——两个条件这份文件一个都不满足。

合同、报价单、邮件正文导出的 PDF 同理。
