完成差旅发票录入流程

This commit is contained in:
wandering
2026-06-11 19:22:34 +08:00
parent 263d542903
commit 87c7b2d5be
62 changed files with 3033 additions and 2756 deletions

154
README.md
View File

@@ -1,6 +1,6 @@
# 财务报销自动化
自动从 PDF 发票提取信息,生成发票汇总表与易耗品出库单,并可选在财务系统中自动填报报销单。
自动从 PDF 发票或图片中提取信息,生成发票汇总表与易耗品出库单,并可选在财务系统中自动填报报销单。
**支持发票类型区分**:系统自动识别高铁票、酒店住宿等差旅发票与普通发票。差旅发票不生成易耗品出库单,走差旅报销流程;普通发票生成出库单,走普通报销流程。
@@ -11,8 +11,9 @@
├── uv.lock # 依赖锁定文件
├── Makefile # 任务脚本(跨平台)
├── tasks.py # 任务脚本Windows 兼容)
├── config.json # 配置文件(登录凭据、默认值等)
├── config.example.json # 配置示例
├── .pre-commit-config.yaml # pre-commit 钩子配置
├── .env.example # 环境变量示例SSO 地址、LLM 配置等)
├── config.example.json # 用户配置示例
├── 易耗品、出库单.doc # 易耗品出库单 Word 模板
├── src/
│ ├── __init__.py # 包初始化 / 日志器
@@ -22,8 +23,9 @@
│ ├── main.py # CLI 入口
│ ├── doc/ # 文档处理模块
│ │ ├── extractor.py # 编排入口:串联 PDF 读取 → LLM 提取 → 分类
│ │ ├── pdf.py # PDF 文件发现与文本提取
│ │ ├── pdf.py # PDF 图片渲染PyMuPDF供多模态 LLM 使用)
│ │ ├── llm_extractor.py # LLM 信息提取
│ │ ├── matcher.py # 数据匹配与校验
│ │ ├── invoice.py # 发票类型常量、分类逻辑、CSV 读写工具
│ │ ├── fill_consumable_doc.py # 将 CSV 填入易耗品出库单Word COM
│ │ ├── prompt.py # LLM 提示词模板
@@ -33,19 +35,24 @@
│ ├── templates/
│ │ ├── index.html # PC 端主页
│ │ └── mobile_upload.html # 移动端扫码上传
│ ├── static/ # 前端 CSS / JS
│ ├── static/
│ │ ├── css/ # 样式文件
│ │ └── js/ # 前端脚本
│ └── uploads/ # 按会话隔离的上传与产物目录
├── scripts/ # CLI 数据目录
│ ├── data/ # 发票源文件、config.json 与 .invoice_cache 缓存
│ └── test_*.py # 测试脚本
├── tests/ # 测试目录
├── *.pdf # 发票 PDFCLI 模式,放在项目根目录
├── invoice_summary.csv # 发票汇总表CLI 产物)
└── images/ # 浏览器调试截图
├── docs/ # 用户文档API 说明、操作指南等
├── images/ # 浏览器调试截图
└── *.pdf / *.jpg / *.png # 发票 PDF 或图片CLI 模式,放在 scripts/data/
```
## 数据流
```mermaid
flowchart TB
PDF[PDF 发票] --> Extract[extractor 提取]
PDF[PDF 发票 / 图片] --> Extract[extractor 提取]
Extract --> Classify{发票类型分类}
Classify -->|差旅发票| Travel[高铁票 / 酒店住宿]
Classify -->|普通发票| General[普通发票]
@@ -54,7 +61,7 @@ flowchart TB
CSV -->|仅普通发票| Fill[fill_consumable_doc]
Fill --> Doc[易耗品、出库单.doc]
CSV --> Bot[bot 浏览器自动化]
Bot -->|差旅模式| Submit_T[差旅报销填报<br/>TODO]
Bot -->|差旅模式| Submit_T[差旅报销填报]
Bot -->|普通模式| Submit_G[普通报销填报]
```
@@ -79,18 +86,19 @@ python tasks.py install
| 依赖 | 用途 |
|------|------|
| pdfplumber | PDF 发票文本提取 |
| PyMuPDF | PDF 图片渲染(供多模态 LLM 使用) |
| llama-index | LLM 信息提取(发票识别、差旅信息提取) |
| playwright | 财务系统浏览器自动化 |
| flask | Web 服务 |
| pywin32 | 填写 Word 出库单(`fill_consumable_doc` |
### 2. 准备数据CLI 模式)
将发票 PDF 放在项目根目录下。
将发票 PDF 或图片(`.jpg``.png``.webp``.bmp`)放在 `scripts/data/` 目录下。
### 3. 配置
编辑 `config.json`
编辑 `scripts/config.json`(参考 `config.example.json`
```json
{
@@ -99,7 +107,7 @@ python tasks.py install
"default_name": "默认报销人姓名",
"default_card_no": "默认公务卡号",
"default_person_id": "默认人员编号",
"consumable_storage": "躬行楼 C205"
"consumable_storage": "物料存储地"
}
```
@@ -109,8 +117,20 @@ python tasks.py install
| `default_name` | 默认报销人姓名 |
| `default_card_no` | 默认公务卡号 |
| `default_person_id` | 默认人员编号(工号) |
| `consumable_storage` | 出库单「存放地点」列默认值 |
| `sso_login_url` 等 | 系统 URL一般无需修改 |
| `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
@@ -135,39 +155,62 @@ uv run python src/main.py -u 工号 -p 密码
```bash
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`;购货人/领用人签字、备注保持空白
- 存放地点取自 `consumable_storage`(默认: `躬行楼 C205`;购货人/领用人签字、备注保持空白
## 执行步骤说明
| 步骤 | 命令 | 说明 |
|------|------|------|
| 发票提取 | `--step invoice` | 扫描目录 PDF生成 `invoice_summary.csv` |
| 发票提取 | `--step invoice` | 扫描 `scripts/data/` 目录 PDF 和图片,生成 `invoice_summary.csv` |
| 浏览器填报 | `--step submit` | 登录信息门户 → 报销系统 → 自动填单、上传附件 |
> 分步执行时,上一步的 CSV 会自动成为下一步的输入。
> 全流程执行时数据在内存中流转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`)使用英文列名:
| 列名 | 说明 |
|------|------|
| 序号 | 行号 |
| 发票类型 | 自动识别:`高铁票` / `酒店住宿` / `普通发票` |
| 发票号码 | 电子发票号码 |
| 开票日期 | 开票日期 |
| 项目名称 / 规格型号 | 货物或应税劳务信息(差旅发票为出发站→到达站) |
| 价税合计 | 发票含税金额 |
| 销售方名称 | 销方名称 |
| 出发站 / 到达站 / 车次 / 乘车日期 / 座位等级 | 高铁票专用字段 |
| 人员姓名 / 刷卡日期 / 公务卡号 / 刷卡金额 | 可手工或 Web 端编辑补充 |
| 备注 / 工号 | 可手工或 Web 端编辑补充 |
| `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 服务
@@ -181,7 +224,7 @@ uv run python src/web/app.py
### 推荐使用流程
1. 上传 PDF或上传已有 CSV
1. 上传 PDF 或图片(或上传已有 CSV
2. 填写配置(账号、密码、姓名、公务卡号、存放地点等),可上传 `config.json` 一键填充
3. 点击 **开始处理** — 完成发票提取、生成 CSV系统自动识别发票类型并分类统计
4. **普通发票**:自动生成 **易耗品、出库单.doc**,可下载
@@ -189,30 +232,24 @@ uv run python src/web/app.py
6. 在表格中核对、修改发票数据(提交财务系统前会自动保存)
7. 确认无误后点击 **提交到财务系统**
### 处理模式
### 处理流程
| 模式 | 入口 | 说明 |
|------|------|------|
| **PDF 模式** | 上传 PDF | 提取发票 → 生成 CSV + 自动分类 → 普通发票生成出库单 |
| **CSV 快捷模式** | 仅上传 CSV | 跳过提取,读取 CSV 中的发票类型,按需生成出库单 |
上传 PDF 或图片 → LLM 识别文档类型 → 结构化提取 → 分类处理
### 发票类型区分
系统通过 LLM 多模态识别自动判断每张文档的类型,无需手动指定:
系统自动识别以下发票类型并按类型分流:
| 发票类型 | 识别依据 | 出库单 | 填报模式 |
|----------|----------|--------|----------|
| **高铁票** | 含"电子客票"、"中国铁路"、"12306"等关键词 | 不生成 | 差旅报销TODO |
| **酒店住宿** | 含"住宿费"、"餐饮服务"、"租赁服务"等关键词 | 不生成 | 差旅报销TODO |
| **普通发票** | 其他所有发票 | 自动生成 | 普通报销(已实现) |
当同一批次同时包含差旅发票和普通发票时,系统会为普通发票生成出库单,并在填报时分别处理。
| 文档类型 | 处理方式 | 状态 |
|----------|----------|------|
| 发票(高铁票/酒店住宿/普通发票) | 提取发票信息 → 金额匹配 → 分类 | 已实现 |
| 支付记录(刷卡截图) | 提取刷卡信息 → 与发票匹配 | 已实现 |
| 出差事前申请单 | 提取出差事由、地点、时间 | 已实现 |
| 飞机票 | 同高铁票处理流程 | 计划中 |
### 功能一览
| 功能 | 说明 |
|------|------|
| 发票提取 | 上传 PDF 后自动完成 |
| 发票提取 | 上传 PDF 或图片后自动完成 |
| 发票类型自动分类 | 高铁票/酒店住宿/普通发票,自动分流处理 |
| 易耗品出库单 | 仅普通发票自动生成 Word差旅发票跳过 |
| 表格在线编辑 | 处理完成后可修改 CSV 各字段;保存后重新生成出库单 |
@@ -221,9 +258,26 @@ uv run python src/web/app.py
| 配置上传 | 支持上传 `config.json` 填充表单 |
| 手机扫码上传 | 二维码打开移动端页面拍照上传PC 端轮询同步 |
> Web 端浏览器填报以无头模式运行。未上传 PDF 时,填报阶段会跳过附件上传。
> 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](./API.md)。
## 开发任务
@@ -254,5 +308,5 @@ uv run python src/web/app.py
- 浏览器填报时会打开或使用 Chromium请勿手动干扰自动化流程
- 调试截图保存在 `images/` 目录
- 项目根目录需保留 `易耗品、出库单.doc` 模板Web 每次从模板复制到会话目录再填写,不修改原模板
- `config.json` 含敏感信息,请勿提交到公开仓库
- **发票类型区分**:差旅发票(高铁票/酒店住宿)不会生成易耗品出库单,当前差旅报销填报流程仍在开发中TODO
- `scripts/config.json` 含敏感信息,请勿提交到公开仓库
- **发票类型区分**:差旅发票(高铁票/酒店住宿)不会生成易耗品出库单,差旅报销填报流程已完整实现(含差旅信息提取、明细录入、支付方式、补助清单、附件上传