发票合并打印助手 (Invoice Merger)
电子发票按月份自动分组并合并到A4纸上,便于打印报销。
专为中国高铁电子发票设计,支持自动识别乘车日期、票价金额,按月份分组合并,生成统计报表。
功能特点
- ✅ 自动扫描文件夹中的PDF发票(支持递归扫描子文件夹)
- ✅ 智能识别乘车日期(基于最早日期原则)
- ✅ 智能识别票价金额并自动汇总
- ✅ 按票价详细分组统计(显示每种票价的数量和小计)
- ✅ 基于发票号码自动去重
- ✅ 按月份自动分组
- ✅ 同一月份内按票价从小到大排序(方便财务审计)
- ✅ 生成统计文件(票数量、总金额)
- ✅ 保持原始宽高比,不扭曲图片
- ✅ 可配置每行列数(默认3列)
- ✅ 自动计算行数,充分利用A4纸空间
- ✅ 实时进度显示
效果预览
合并后的PDF示例(3列布局,保持原始发票宽高比):
安装
方式一:从 PyPI 安装(推荐)
pip install invoice-merger
方式二:从源码安装
git clone https://github.com/luzhongqiu/invoice-merger.git
cd invoice-merger
pip install -e .
使用方法
基本用法(推荐)
使用 Python 模块方式运行(适用于虚拟环境):
python -m invoice_merger <输入文件夹路径>
例如:
python -m invoice_merger ./invoices
输出文件将保存在输入文件夹的 output 子目录中。
命令行方式
如果全局安装(使用 pip install 或 pipx install),也可以直接使用命令:
invoice-merger ./invoices
高级选项
python -m invoice_merger <输入文件夹> -o <输出文件夹> -c <列数>
参数说明:
-o, --output: 指定输出文件夹路径(可选,默认为输入文件夹下的output目录)-c, --columns: 每行放置的列数(可选,默认为3列)-v, --version: 显示版本信息-h, --help: 显示帮助信息
示例
-
使用默认设置(3列):
python -m invoice_merger ./my_invoices
-
自定义输出目录:
python -m invoice_merger ./my_invoices -o ./merged_pdfs
-
自定义列数(如每行2列):
python -m invoice_merger ./my_invoices -c 2
-
同时指定输出目录和列数:
python -m invoice_merger ./my_invoices -o ./output -c 4
-
查看帮助信息:
python -m invoice_merger --help
输出格式
PDF文件
- 输出文件按月份命名,格式为
YYYY-MM.pdf - 例如:
2025-10.pdf,2025-11.pdf - 同一月份内的发票按票价从小到大排列(方便财务审计)
统计文件(info.txt)
程序会自动生成 info.txt 统计文件,包含每个月的汇总信息和详细的按票价分组统计:
============================================================
发票合并统计信息
============================================================
2025-10.pdf 票数量: 35张 总金额: 1285.00元
票价: 16.00元 × 1张 = 16.00元
票价: 17.00元 × 1张 = 17.00元
票价: 26.00元 × 2张 = 52.00元
票价: 30.00元 × 3张 = 90.00元
票价: 38.00元 × 18张 = 684.00元
票价: 42.00元 × 2张 = 84.00元
票价: 46.00元 × 5张 = 230.00元
...
2025-11.pdf 票数量: 38张 总金额: 1533.00元
票价: 30.00元 × 2张 = 60.00元
票价: 35.00元 × 5张 = 175.00元
票价: 38.00元 × 25张 = 950.00元
...
------------------------------------------------------------
合计 票数量: 119张 总金额: 4602.00元
============================================================
说明:
- 每个月显示总票数和总金额
- 详细列出每种票价的数量和小计金额
- 按票价从小到大排序
- 便于报销时核对不同票价的发票数量
- 最后一行显示所有月份的总计
支持的日期格式
程序会自动识别PDF中的以下日期格式:
2025年10月15日或2025-10-152025.10.1510/15/2025
如果无法从PDF内容中提取日期,将使用文件的修改时间作为日期。
工作原理
- 扫描阶段:递归扫描指定文件夹及所有子文件夹中的PDF文件(自动排除output目录)
- 识别阶段:从PDF文本内容中提取日期、发票号码和金额信息
- 去重阶段:根据发票号码自动去除重复文件
- 分组阶段:按月份分组,组内按票价从小到大排序(方便财务审计)
- 合并阶段:
- 按指定列数排列
- 计算缩放比例,保持原始宽高比
- 自动计算行数,充分利用A4纸空间
- 当前页放不下时自动创建新页面
- 同时统计每个月的票数量、总金额、按票价分组统计
- 统计阶段:生成info.txt文件,包含:
- 每个月的票数和总金额汇总
- 详细的按票价分组统计(票价 × 张数 = 小计)
- 所有月份的总计
日期识别逻辑
程序专门针对高铁电子发票设计,使用智能算法识别乘车日期:
识别原理
- 提取所有日期:从PDF中查找所有
YYYY年MM月DD日格式的日期 - 智能选择:选择最早的日期作为乘车日期
- 原理依据:开票时间总是晚于或等于乘车时间
示例
PDF内容包含两个日期:
- 2025年10月31日 ← 乘车日期
- 2026年01月07日 ← 开票日期
程序选择:2025-10-31(最早日期)
重要假设
⚠️ 本程序假设开票日期晚于乘车日期
这个假设适用于绝大多数场景,因为:
- ✅ 高铁票通常在乘车后统一开票
- ✅ 即使乘车当天开票,日期也不会早于乘车日期
- ❌ 不适用:如果PDF中包含其他更早的日期(如打印日期、系统生成日期等),可能导致识别错误
去重机制
程序通过文件名自动去重:
- 从文件名中提取发票ID(格式:
{id}-电子发票.pdf) - 如果发现相同ID,自动跳过
- 避免重复下载的文件被合并多次
示例:
26319130671000153108-电子发票.pdf ✓ 保留
26319130671000153108-电子发票 (1).pdf ✗ 跳过(重复)
金额识别逻辑
程序自动从PDF中提取票价信息:
识别方法
- 查找
票价:¥xx.xx格式的金额 - 验证金额合理性(0-10000元范围)
- 按月份自动汇总
金额统计(详细分组)
程序生成 info.txt 统计文件,包含以下信息:
- 月度汇总:每个月的PDF文件显示总票数和总金额
- 按票价分组:详细列出每种票价的数量和小计金额(按票价从小到大排序)
- 计算公式:票价 × 张数 = 小计金额
- 总计统计:最后显示所有月份的合计
示例:
2025-10.pdf 票数量: 35张 总金额: 1285.00元
票价: 38.00元 × 18张 = 684.00元 ← 一目了然哪种票价最多
票价: 46.00元 × 5张 = 230.00元
...
这样的详细统计让报销时可以:
- ✅ 快速核对不同票价的发票数量
- ✅ 确认每种票价的小计是否正确
- ✅ 方便财务审核时分类统计
⚠️ 重要安全保护机制
为保证报销准确性,程序内置了金额识别保护机制:
✅ 如果无法识别票价金额,程序会立即报错并停止运行
这是有意设计的安全措施,因为:
- 金额=0是严重错误:报销金额不准确会造成财务问题
- 及时发现问题:在合并前就发现问题,避免打印后才发现错误
- 保证报销准确:只有所有金额都正确识别,才允许生成报销文件
当遇到金额识别错误时:
⚠️ 金额识别错误 - 程序已停止
错误: 无法从 xxx.pdf 提取票价金额!
建议:
1. 检查PDF文件是否损坏
2. 检查是否为扫描版PDF(需要OCR处理)
3. 手动打开PDF确认是否包含票价信息
这意味着:
- ✅ 所有生成的
info.txt中的金额都是准确的 - ✅ 可以放心用于报销,无需担心金额错误
- ✅ 程序不会"悄悄"地把金额设为0
目录结构示例
程序支持递归扫描,可以处理包含子文件夹的复杂目录结构:
my_invoices/
├── 2025年10月/
│ ├── 发票1.pdf
│ └── 发票2.pdf
├── 2025年11月/
│ ├── 发票3.pdf
│ └── 发票4.pdf
├── 其他发票/
│ └── 发票5.pdf
└── output/ ← 输出目录(自动排除扫描)
├── 2025-10.pdf (合并后的PDF)
├── 2025-11.pdf (合并后的PDF)
└── info.txt (统计信息)
说明:
- 程序会自动递归扫描所有子文件夹
- 无需手动整理文件夹结构
- output 目录会被自动排除,避免重复处理
- info.txt 自动生成,包含所有月份的统计汇总
注意事项
- 确保输入的PDF文件包含文本内容(非纯图片扫描)
- 如果是扫描版PDF,建议先进行OCR处理
- 程序会在处理过程中输出详细的日志信息
- 如果某个PDF无法提取日期,会使用文件修改时间并给出警告
技术栈
- Python 3.8+
- PyMuPDF (fitz) - PDF处理库
- tqdm - 进度条显示
FAQ
为什么推荐使用 python -m invoice_merger 方式?
- 虚拟环境友好:在虚拟环境中无需激活即可使用
- 跨平台一致:Windows/Mac/Linux 都是相同的命令
- 明确 Python 版本:可以指定使用哪个 Python(如
python3.11 -m invoice_merger) - 开发更方便:使用
pip install -e .安装后立即可用
如何在任何地方使用?
# 方式1: 使用 python -m(推荐)
python -m invoice_merger /path/to/invoices
# 方式2: 全局安装后使用命令
pip install invoice-merger
invoice-merger /path/to/invoices
# 方式3: pipx 隔离安装
pipx install invoice-merger
invoice-merger /path/to/invoices
开发时如何测试?
# 1. 克隆仓库
git clone https://github.com/luzhongqiu/invoice-merger.git
cd invoice-merger
# 2. 创建虚拟环境
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. 安装开发版
pip install -e .
# 4. 测试运行
python -m invoice_merger ./test_data
贡献
欢迎提交 Issue 和 Pull Request!
项目地址:https://github.com/luzhongqiu/invoice-merger
许可证
MIT License
Metadata
Release files for invoice-merger 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| invoice_merger-1.2.0.tar.gz | 1.1 MB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| invoice_merger-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / invoice_merger-1.2.0.tar.gz
| Download URL | invoice_merger-1.2.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b51622b20248fadf77c0d56614c703fb4bafbcc2e66b9108911c437eeb608ce6
|
|
BLAKE2b-256 checksum How to use checksums |
21696dcbf7fb974ca16aeeaad136267c29a8edf30f886ee06eaaf7be1a75bbd8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Release files / invoice_merger-1.2.0-py3-none-any.whl
| Download URL | invoice_merger-1.2.0-py3-none-any.whl |
|---|---|
| Size | 13.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0b17dc5292a71760fa1f0aee57cb916d8bdc853cade0fc49b5d50e63266cfc05
|
|
BLAKE2b-256 checksum How to use checksums |
e4aa974a907077b2c654ac5086a8028ed916f1b598e2fe7cd541b688b3e5f9a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Security for invoice-merger 1.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.