Files
Auto-Finance/报销操作指南.md

302 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阜阳师范大学财务报销系统 - 自动化脚本操作指南
> 适用场景:日常报销录入(基于 `reimburse.py` 脚本)
> 最后更新2026-05-23
---
## 一、系统概览
```
整体流程:
信息门户(SSO登录) → 财务系统入口 → 单点登录页 → 网络报销 → 日常报销录入
(tyrz.fynu.edu.cn) (点击"财务系统") (新标签页) (a:has(img)) (/expen/common/common)
目标系统: http://210.45.32.214:8081
用户: 王建锋 (工号: 202407021)
```
### 关键 URL
| 系统 | URL | 说明 |
|------|-----|------|
| SSO 登录 | `https://tyrz.fynu.edu.cn/sso/login` | 统一认证入口 |
| 信息门户 | `https://tyrz.fynu.edu.cn/oshall` | 登录后跳转目标 |
| 报销系统 | `http://210.45.32.214:8081` | 网络报销主系统 |
| 日常报销录入 | `/expen/common/common?v=4.0` | 目标录入页面 |
---
## 二、数据准备
### 2.1 发票数据 CSV
脚本从 `invoice_summary.csv`GBK 编码读取发票数据CSV 需包含以下列:
| 列名 | 说明 | 示例 |
|------|------|------|
| 序号 | 发票序号 | 1, 2, 3... |
| 发票号码 | 发票编号 | 26442000005432652421 |
| 开票日期 | 发票开具日期 | 2026/5/18 |
| 项目名称 | 采购项目名称 | 电阻一批 |
| 规格型号 | 规格型号 | — |
| 价税合计 | 发票金额 | 2900.00 |
| 销售方名称 | 商户/销售方 | 佛山市泓宇芯科技有限公司 |
| 人员姓名 | 报销人 | 王建锋(默认值) |
| 刷卡日期 | 公务卡消费日期 | 2026/5/18 → 自动转为 2026-05-18 |
| 公务卡号 | 公务卡卡号 | 6282880139161682默认值 |
| 刷卡金额 | 实际刷卡金额 | 2900.00 |
| 备注 | 备注信息 | — |
| 工号 | 人员工号 | 202407021默认值 |
### 2.2 附件文件
脚本自动扫描当前工作目录下所有 `.pdf` 文件,按文件名排序后与发票一一对应上传。确保 PDF 文件名与发票顺序一致。
---
## 三、脚本执行流程
### 运行方式
```bash
python reimburse.py --data invoice_summary.csv
```
支持命令行覆盖配置:
```bash
python reimburse.py \
--data invoice_summary.csv \
--username 202407021 \
--password "your_password" \
--user-data-dir browser_profile
```
### 执行步骤
脚本按以下顺序自动执行,截图保存在 `images/` 目录:
```
Step 0: 登录信息门户
├── 访问 SSO 登录页
├── 填写工号 + 密码
├── 勾选用户协议复选框
├── 点击"登录"按钮
└── 等待跳转到信息门户 (zs-uip/oshall/portal)
Step 1: 进入报销系统
├── 点击"财务系统"快捷入口
├── 等待新标签页打开 (含 dddl 或 210.45.32.214)
├── 切换到新标签页
├── 关闭旧标签页
├── 通过 a:has(img[src*="wlbx"]) 定位"网络报销"链接
├── 提取链接 URL 并导航
├── 等待"报销录入"文本出现
└── 导航到 /expen/common/common?v=4.0
Step 2: 创建新报销单
├── 等待 2 秒
├── 点击 button:has-text("新增")
└── 等待表单加载 3 秒
Step 3: 填写基本信息
├── 填写 #EXPENEXPLAIN → "元器件采购报销"
├── 点击 #PROJECTCODE 打开项目选择弹窗
├── 在 #promodal .fixed-table-body tbody tr 中点击第一行
└── 点击 #saveAndNext 进入下一步
Step 4: 录入报销明细(一条总明细)
├── 计算所有发票的刷卡金额合计
├── 点击 #insertDetail 打开增加明细弹窗
├── 点击 #economicscode2 打开经济科目选择
├── 在 #econmodal 中选择第 3 行经济科目
├── 填写单据数 = 发票张数
├── 填写报销总金额 = 刷卡金额合计
└── 点击 #detailAdd 确认
Step 5: 录入支付方式(逐张发票)
├── 点击 "下一步(支付方式)"
├── 对每张发票循环:
│ ├── 点击 #insertPay
│ ├── 填写 #personid2 (工号)
│ ├── 填写 #accountname2 (姓名)
│ ├── 填写 #receiptdate2 (刷卡日期)
│ ├── 填写 #localaccount2 (固定卡号: 6282880139161682)
│ ├── 填写 #receiptmoney2 (刷卡金额)
│ ├── 填写 #money2 (实报金额 = 刷卡金额)
│ ├── 填写 #merchant2 (销售方名称)
│ ├── 填写 #smark2 (备注)
│ └── 点击 #payAdd 确认
└── 所有发票录入完成
Step 6: 上传附件(逐张发票)
├── 点击 #next3 切换到附件清单页面
├── 对每张发票循环:
│ ├── 点击 #insertAcc 打开附件弹窗
│ ├── select_option #fjlx → '1' (发票类型)
│ ├── 填写 #fpsmxx (项目名称 - 发票号码)
│ ├── set_input_files #file (对应 PDF 文件)
│ └── 点击 #cjtj 确认
└── 所有附件上传完成
提交阶段:
└── 点击 #submit (当前已注释,需手动取消注释)
```
---
## 四、页面元素速查
### 基本信息页 (Step 3)
| 字段 | 选择器 | 操作 |
|------|--------|------|
| 报销说明 | `#EXPENEXPLAIN` | fill |
| 项目代码 | `#PROJECTCODE` | click → 弹窗选择 |
| 项目弹窗 | `#promodal .fixed-table-body tbody tr` | 点击第一行 |
| 下一步按钮 | `#saveAndNext` | click |
### 报销明细页 (Step 4)
| 字段 | 选择器 | 操作 |
|------|--------|------|
| 增加按钮 | `#insertDetail` | click |
| 经济事项代码 | `#economicscode2` | click → 弹窗选择 |
| 经济科目弹窗 | `#econmodal .fixed-table-body tbody tr` | 点击第 3 行 |
| 单据数 | `input[name="expenPwCommondetail.HOWBILLS"]` | fill |
| 报销总金额 | `#je_zwzcdz` | fill |
| 确定按钮 | `#detailAdd` | click |
### 支付方式页 (Step 5)
| 字段 | 选择器 | 操作 |
|------|--------|------|
| 增加按钮 | `#insertPay` | click |
| 人员编号 | `#personid2` | fill |
| 人员姓名 | `#accountname2` | fill |
| 刷卡日期 | `#receiptdate2` | fill |
| 公务卡号 | `#localaccount2` | fill (固定值) |
| 刷卡金额 | `#receiptmoney2` | fill |
| 实报金额 | `#money2` | fill |
| 商户 | `#merchant2` | fill |
| 备注 | `#smark2` | fill |
| 确定按钮 | `#payAdd` | click |
### 附件清单页 (Step 6)
| 字段 | 选择器 | 操作 |
|------|--------|------|
| 增加按钮 | `#insertAcc` | click |
| 附件类型 | `#fjlx` | select_option → '1'(发票) |
| 附件说明 | `#fpsmxx` | fill |
| 文件上传 | `#file` | set_input_files |
| 确定按钮 | `#cjtj` | click |
### 提交
| 操作 | 选择器 |
|------|--------|
| 提交按钮 | `#submit` |
| 提交按钮(备用) | `#submit2` |
---
## 五、数据流向
```
invoice_summary.csv (GBK)
▼ load_invoice_data()
├── 读取 CSV 行
├── 日期格式转换 (2026/5/18 → 2026-05-18)
├── 填充默认值 (姓名/卡号/工号)
└── 输出: list[dict]
▼ add_reimburse_items()
├── 计算: card_amount = sum(所有发票刷卡金额)
├── 单据数 = len(invoices)
└── 录入 1 条总明细
▼ fill_payment()
└── 对每张发票录入 1 条支付记录
▼ upload_attachments()
├── 扫描 *.pdf 文件
└── 按索引匹配发票 → PDF逐张上传
```
---
## 六、关键设计说明
### 6.1 浏览器复用
脚本使用 `launch_persistent_context` 持久化浏览器上下文,登录状态保存在 `browser_profile/` 目录。再次运行时复用已有会话,无需重复登录。
### 6.2 明细录入策略
脚本采用"一条总明细"策略:将所有发票合并为一条报销明细,报销总金额为所有发票刷卡金额之和,单据数为发票总张数。支付方式则逐张发票分别录入,每张发票对应一条支付记录。
### 6.3 经济科目选择
脚本在经济科目弹窗中固定选择第 3 行。如需更改科目,修改 `rows[2]` 的索引即可。
### 6.4 项目选择
脚本在项目选择弹窗中固定选择第 1 行。如需更改项目,修改 `first_row` 的选择逻辑即可。
### 6.5 网络报销链接动态获取
单点登录页的"网络报销"链接参数每次不同,脚本通过 `a:has(img[src*="wlbx"])` 精确定位链接,动态提取 `href` 属性后导航,不硬编码 URL。
### 6.6 提交控制
脚本默认注释了 `bot.submit()` 调用。完成所有录入后停留在附件清单页面,需人工确认数据无误后,取消注释 `bot.submit()` 再运行,或手动点击提交按钮。
---
## 七、日志与调试
### 日志输出
- 控制台实时输出DEBUG 级别)
- 文件日志:`reimburse.log`UTF-8 编码)
### 截图保存
每个关键步骤自动截图到 `images/` 目录:
| 截图文件 | 对应步骤 |
|----------|----------|
| `debug_portal_loaded.png` | 登录成功 |
| `debug_step3_project_modal.png` | 项目弹窗打开 |
| `debug_step3_project_selected.png` | 项目选择完成 |
| `debug_step3_done.png` | 基本信息完成 |
| `debug_after_add_click.png` | 点击新增后 |
| `debug_item_total.png` | 总明细录入完成 |
| `debug_step5_done.png` | 支付方式完成 |
| `debug_step6_done.png` | 附件上传完成 |
| `debug_submitted.png` | 提交完成 |
| `debug_error.png` | 异常状态 |
### 超时设置
- 页面默认超时30 秒
- 登录门户等待:最多 30 秒
- 单点登录页等待:最多 15 秒
---
## 八、常见问题
| 问题 | 原因 | 解决方案 |
|------|------|----------|
| 登录超时 | SSO 需要手动验证码/微信扫码 | 手动完成验证后脚本继续 |
| 未找到财务系统入口 | 门户页面结构变化 | 检查 `images/debug_*` 截图定位 |
| 经济科目选择失败 | 弹窗加载延迟 | 检查超时设置,增加等待时间 |
| 附件上传失败 | PDF 文件不存在或路径错误 | 确认 PDF 在当前工作目录 |
| 金额不匹配 | 明细合计 ≠ 支付合计 | 检查 CSV 数据中刷卡金额 |
| 提交被拦截 | 必填项为空 | 检查 `reimburse.log` 定位失败步骤 |