16 KiB
财务报销自动化
自动从 PDF 发票或图片中提取信息,生成发票汇总表与易耗品出库单,并可选在财务系统中自动填报报销单。
支持发票类型区分:系统自动识别高铁票、酒店住宿等差旅发票与普通发票。差旅发票不生成易耗品出库单,走差旅报销流程;普通发票生成出库单,走普通报销流程。
项目结构
├── pyproject.toml # 项目配置(依赖、工具链)
├── uv.lock # 依赖锁定文件
├── Makefile # 任务脚本(跨平台)
├── tasks.py # 任务脚本(Windows 兼容)
├── .pre-commit-config.yaml # pre-commit 钩子配置
├── .env.example # 环境变量示例(SSO 地址、LLM 配置等)
├── config.example.json # 用户配置示例
├── 易耗品、出库单.doc # 易耗品出库单 Word 模板
├── src/
│ ├── __init__.py # 包初始化 / 日志器
│ ├── config.py # 配置加载
│ ├── bot.py # 浏览器自动填报
│ ├── pipeline.py # CLI 流程编排
│ ├── main.py # CLI 入口
│ ├── doc/ # 文档处理模块
│ │ ├── extractor.py # 编排入口:串联 PDF 读取 → LLM 提取 → 分类
│ │ ├── pdf.py # PDF 图片渲染(PyMuPDF,供多模态 LLM 使用)
│ │ ├── llm_extractor.py # LLM 信息提取
│ │ ├── matcher.py # 数据匹配与校验
│ │ ├── invoice.py # 发票类型常量、分类逻辑、CSV 读写工具
│ │ ├── fill_consumable_doc.py # 将 CSV 填入易耗品出库单(Word COM)
│ │ ├── prompt.py # LLM 提示词模板
│ │ └── prompts/ # 提示词模板文件
│ └── web/
│ ├── app.py # Web 服务入口
│ ├── templates/
│ │ ├── index.html # PC 端主页
│ │ └── mobile_upload.html # 移动端扫码上传
│ ├── static/
│ │ ├── css/ # 样式文件
│ │ └── js/ # 前端脚本
│ └── uploads/ # 按会话隔离的上传与产物目录
├── scripts/ # CLI 数据目录
│ ├── data/ # 发票源文件、config.json 与 .invoice_cache 缓存
│ └── test_*.py # 测试脚本
├── tests/ # 测试目录
├── docs/ # 用户文档(API 说明、操作指南等)
├── images/ # 浏览器调试截图
└── *.pdf / *.jpg / *.png # 发票 PDF 或图片(CLI 模式,放在 scripts/data/)
数据流
flowchart TB
PDF[PDF 发票 / 图片] --> Extract[extractor 多模态提取]
Extract --> Cache[(.invoice_cache/*.json)]
Cache --> Classify{发票类型分类}
Classify -->|差旅发票| Travel[高铁票 / 酒店住宿]
Classify -->|普通发票| General[普通发票]
Classify -->|支付记录| Payment[支付截图]
Classify -->|申请单| Application[出差事前申请单]
Travel --> Matcher[matcher 金额匹配]
Payment --> Matcher
Matcher --> MatchResult[(match_result.json)]
Cache --> TravelLLM[LLM 差旅信息提取]
MatchResult --> TravelLLM
TravelLLM --> TravelInfo[(travel_info.json)]
Cache --> NormalLLM[LLM 普通发票信息提取]
MatchResult --> NormalLLM
NormalLLM --> NormalInfo[(normal_info.json)]
TravelInfo -->|差旅基本信息| Bot_T[bot/travel.py<br/>差旅填报流程]
TravelInfo -->|报销明细| Bot_T
TravelInfo -->|支付方式| Bot_T
TravelInfo -->|补助清单| Bot_T
TravelInfo -->|附件清单| Bot_T
Bot_T --> Submit_T[差旅报销提交]
NormalInfo -->|报销说明| Bot_G[bot/normal.py<br/>普通填报流程]
NormalInfo -->|发票总数/金额| Bot_G
NormalInfo -->|支付方式| Bot_G
NormalInfo -->|附件清单| Bot_G
Bot_G --> Submit_G[普通报销提交]
General --> CSV[(invoice_summary.csv)]
CSV --> Fill[fill_consumable_doc]
Fill --> Doc[易耗品、出库单.doc]
关键中间产物
| 文件 | 生成阶段 | 作用 |
|---|---|---|
.invoice_cache/*.json |
extractor 提取 | 单张发票/支付记录/申请单的结构化数据 |
match_result.json |
matcher 匹配 | 支付截图与发票的关联关系(按金额匹配) |
travel_info.json |
LLM 差旅信息提取 | 综合发票缓存 + 匹配结果,生成差旅报销所需的全部结构化数据 |
normal_info.json |
LLM 普通发票信息提取 | 综合普通发票 + 匹配结果,生成普通报销所需的全部结构化数据 |
invoice_summary.csv |
extractor 提取 | 普通发票汇总(用于生成易耗品出库单) |
bot 模块架构
bot/ 包负责浏览器自动化填报,仅接收已提取的信息并执行填报操作,不承担信息提取职责:
| 模块 | 职责 |
|---|---|
bot/base.py |
BaseBot 基类:浏览器生命周期、登录、导航、截图 |
bot/travel.py |
差旅填报流程:基本信息 → 差旅明细 → 支付方式 → 补助清单 → 附件上传 |
bot/normal.py |
普通填报流程:基本信息 → 总明细 → 支付方式 → 附件上传 |
bot/__init__.py |
入口函数:run_bot() / run_bot_web(),负责类型判断和流程路由 |
环境要求
- Python 3.12+
- uv 包管理器(安装指南)
- Windows(易耗品出库单填写依赖 Microsoft Word + COM,仅 Windows 可用)
快速开始
1. 安装依赖
# 同步所有依赖(运行时 + 开发工具)
make install
# Windows 上等效命令:
python tasks.py install
项目使用 uv 管理依赖,所有包版本锁定在 uv.lock 中,确保可复现。
| 依赖 | 用途 |
|---|---|
| PyMuPDF | PDF 图片渲染(供多模态 LLM 使用) |
| llama-index | LLM 信息提取(发票识别、差旅信息提取) |
| playwright | 财务系统浏览器自动化 |
| flask | Web 服务 |
| pywin32 | 填写 Word 出库单(fill_consumable_doc) |
2. 准备数据(CLI 模式)
将发票 PDF 或图片(.jpg、.png、.webp、.bmp)放在 scripts/data/ 目录下。
3. 配置
编辑 scripts/config.json(参考 config.example.json):
{
"username": "你的工号",
"password": "你的密码",
"default_name": "默认报销人姓名",
"default_card_no": "默认公务卡号",
"default_person_id": "默认人员编号",
"consumable_storage": "物料存储地"
}
| 字段 | 说明 |
|---|---|
username / password |
信息门户登录凭据 |
default_name |
默认报销人姓名 |
default_card_no |
默认公务卡号 |
default_person_id |
默认人员编号(工号) |
consumable_storage |
出库单「存放地点」列默认值(默认: 躬行楼 C205) |
服务端配置(SSO 地址、报销系统 URL、LLM 参数)通过环境变量提供,有默认值,一般无需修改:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
SSO_LOGIN_URL |
https://tyrz.fynu.edu.cn/sso/login |
SSO 登录地址 |
PORTAL_URL |
https://tyrz.fynu.edu.cn/oshall |
统一信息平台地址 |
REIMBURSE_URL |
http://210.45.32.214:8081 |
报销系统地址 |
REIMBURSE_PAGE |
/expen/common/common?v=4.0 |
普通报销页面路径 |
TRAVEL_PAGE |
/expen/travel/travel?v=4.0 |
差旅报销页面路径 |
LLM_MODEL |
qwen-vl-max |
LLM 模型名称 |
LLM_API_BASE |
http://localhost:8080/v1 |
LLM API 地址 |
LLM_API_KEY |
lm-studio |
LLM API 密钥 |
4. 运行(CLI)
# 全流程(发票提取 → 浏览器填报)
make run
# Windows 等效:
python tasks.py run
# 仅执行某一步
uv run python src/main.py --step invoice # 仅发票提取
uv run python src/main.py --step submit # 仅浏览器填报
# 覆盖配置中的登录凭据
uv run python src/main.py -u 工号 -p 密码
5. 填写易耗品出库单(CLI)
需已生成 invoice_summary.csv,且本机已安装 Microsoft Word:
uv run python -m src.doc.fill_consumable_doc
uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc"
uv run python -m src.doc.fill_consumable_doc --config scripts/config.json # 指定配置文件
uv run python -m src.doc.fill_consumable_doc --no-backup # 不生成 .doc.bak 备份
填写规则概要:
- 表头「日期」使用填写当天的日期(非发票开票日期)
- 从
spec_model解析品名、规格、单位、数量、单价;card_amount写入金额列 - 单价/金额保留两位小数;表格内统一为 宋体五号(10.5 磅)
- 存放地点取自
consumable_storage(默认:躬行楼 C205);购货人/领用人签字、备注保持空白
执行步骤说明
| 步骤 | 命令 | 说明 |
|---|---|---|
| 发票提取 | --step invoice |
扫描 scripts/data/ 目录的 PDF 和图片,生成 invoice_summary.csv |
| 浏览器填报 | --step submit |
登录信息门户 → 报销系统 → 自动填单、上传附件 |
全流程执行时数据在内存中流转,CSV 为参考产物。分步执行时,缓存数据会自动成为下一步的输入。
缓存机制
系统使用 JSON 缓存作为数据中转站,串联整个处理流程:
源文件 (PDF/图片) → LLM 多模态提取 → JSON 缓存 → 匹配/分类/填报
| 缓存文件 | 说明 |
|---|---|
<文件名>.json |
每个源文件的 LLM 提取结果(发票信息、支付记录等) |
match_result.json |
发票与支付记录的匹配结果 |
travel_info.json |
差旅信息(事由、地点、时间等)提取结果 |
缓存位置:CLI 模式为 scripts/data/.invoice_cache/,Web 模式为 src/web/uploads/<session_id>/.invoice_cache/。缓存文件与源文件同名(如 发票1.pdf 对应 .invoice_cache/发票1.json),后续步骤(金额匹配、发票分类、浏览器填报)均从缓存读取结构化数据。删除缓存后下次处理会重新提取。
CSV 字段说明
发票级别 CSV(invoice_summary.csv)使用英文列名:
| 列名 | 说明 |
|---|---|
index |
行号 |
invoice_type |
发票类型:train / hotel / general |
invoice_number |
电子发票号码 |
invoice_date |
开票日期 |
item_name |
货物或应税劳务名称 |
spec_model |
规格型号(差旅发票为出发站→到达站) |
total_amount |
发票含税金额 |
seller_name |
销方名称 |
departure / arrival |
出发站 / 到达站(高铁票专用) |
train_no / ride_date / seat_class |
车次 / 乘车日期 / 座位等级(高铁票专用) |
person_name |
人员姓名 |
card_date / card_no / card_amount |
刷卡日期 / 公务卡号 / 刷卡金额 |
remark |
备注 |
person_id |
工号 |
Web 服务
提供浏览器界面:上传文件 → 自动处理 → 在线编辑 → 下载产物 → 可选提交财务系统。
uv run python src/web/app.py
访问 http://localhost:5000。
推荐使用流程
- 上传 PDF 或图片(或上传已有 CSV)
- 填写配置(账号、密码、姓名、公务卡号、存放地点等),可上传
config.json一键填充 - 点击 开始处理 — 完成发票提取、生成 CSV,系统自动识别发票类型并分类统计
- 普通发票:自动生成 易耗品、出库单.doc,可下载
- 差旅发票(高铁票/酒店住宿):跳过出库单生成,直接进入差旅报销流程
- 在表格中核对、修改发票数据(提交财务系统前会自动保存)
- 确认无误后点击 提交到财务系统
处理流程
上传 PDF 或图片 → LLM 识别文档类型 → 结构化提取 → 分类处理
系统通过 LLM 多模态识别自动判断每张文档的类型,无需手动指定:
| 文档类型 | 处理方式 | 状态 |
|---|---|---|
| 发票(高铁票/酒店住宿/普通发票) | 提取发票信息 → 金额匹配 → 分类 | 已实现 |
| 支付记录(刷卡截图) | 提取刷卡信息 → 与发票匹配 | 已实现 |
| 出差事前申请单 | 提取出差事由、地点、时间 | 已实现 |
| 飞机票 | 同高铁票处理流程 | 计划中 |
功能一览
| 功能 | 说明 |
|---|---|
| 发票提取 | 上传 PDF 或图片后自动完成 |
| 发票类型自动分类 | 高铁票/酒店住宿/普通发票,自动分流处理 |
| 易耗品出库单 | 仅普通发票自动生成 Word,差旅发票跳过 |
| 表格在线编辑 | 处理完成后可修改 CSV 各字段;保存后重新生成出库单 |
| 财务系统填报 | 单独按钮触发,处理阶段不会自动提交 |
| 实时日志 | SSE 推送处理进度 |
| 配置上传 | 支持上传 config.json 填充表单 |
| 手机扫码上传 | 二维码打开移动端页面,拍照上传,PC 端轮询同步 |
Web 端浏览器填报以无头模式运行。未上传 PDF 或图片时,填报阶段会跳过附件上传。 出库单生成需要 Windows + Word + pywin32;若失败,页面会显示具体原因,CSV 等其它产物仍可正常使用。
会话产物
每次上传生成独立会话,产物存放在 src/web/uploads/<session_id>/:
| 产物 | 说明 |
|---|---|
invoice_summary.csv |
发票汇总数据(含 LLM 识别结果) |
payment_records.csv |
支付记录级别数据(含匹配结果) |
travel_applications.json |
出差事前申请单数据(JSON 格式) |
易耗品、出库单.doc |
自动填写的出库单(仅普通发票) |
config.json |
当次会话配置 |
session.log |
处理日志 |
result.json |
处理结果 |
.invoice_cache/ |
LLM 提取结果缓存(JSON 格式,避免重复处理) |
会话缓存机制与 CLI 模式相同,缓存目录中的 JSON 数据是后续匹配、分类和填报的唯一数据来源。
接口说明见 API.md。
开发任务
项目提供统一的任务脚本,支持跨平台使用:
| 任务 | Makefile | tasks.py | 说明 |
|---|---|---|---|
| 安装依赖 | make install |
python tasks.py install |
同步依赖 + 安装 pre-commit |
| 代码检查 | make check |
python tasks.py check |
Ruff lint + 格式化 + MyPy 类型检查 + deptry 依赖检查 |
| 运行测试 | make test |
python tasks.py test |
pytest + 覆盖率报告 |
| 运行 CLI | make run |
python tasks.py run |
执行全流程 |
| 清理缓存 | make clean |
python tasks.py clean |
删除虚拟环境和缓存 |
代码质量
项目配置了完整的代码质量工具链:
- Ruff — 快速 lint 检查和代码格式化(替代 flake8 + isort + black)
- MyPy — 严格模式类型检查(
strict = true) - deptry — 检测未声明、未使用、过时依赖
- pre-commit — 提交前自动运行 Ruff 检查和格式化
所有检查通过后方可提交代码。
注意事项
- 浏览器填报时会打开或使用 Chromium,请勿手动干扰自动化流程
- 调试截图保存在
images/目录 - 项目根目录需保留
易耗品、出库单.doc模板;Web 每次从模板复制到会话目录再填写,不修改原模板 scripts/config.json含敏感信息,请勿提交到公开仓库- 发票类型区分:差旅发票(高铁票/酒店住宿)不会生成易耗品出库单,差旅报销填报流程已完整实现(含差旅信息提取、明细录入、支付方式、补助清单、附件上传)