# 阜阳师范大学财务报销系统 - 自动化脚本操作指南 > 适用场景:日常报销录入(基于 `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` 定位失败步骤 |