Files
Auto-Finance/src/infra/browser/normal.py
T
wandering 9043886b6f refactor: 架构重组 — doc/bot → core/infra,新增 Agent 调度模块
- src/doc/ 拆分为 src/core/extraction/, matching/, validation/(核心业务逻辑)
- src/bot/ 重命名为 src/infra/browser/(浏览器自动化基础设施)
- fill_consumable_doc.py → src/infra/documents/consumable.py
- 新增 Agent 调度模块:coordinator.py, events.py, session.py,重构 orchestrator.py
- 更新 AGENTS.md、README.md 及所有子目录 README
2026-07-02 18:36:19 +08:00

180 lines
6.6 KiB
Python

"""普通报销填报流程
负责普通发票报销的完整填报步骤:
基本信息 → 总明细 → 支付方式 → 附件上传
"""
from typing import Any
from ... import get_logger
from .base import BaseBot, format_date
log = get_logger("bot.normal")
# ------------------------------------------------------------------
# 普通报销流程
# ------------------------------------------------------------------
def run(
bot: BaseBot,
normal_info: dict[str, Any],
) -> None:
"""执行普通发票报销填报流程
Args:
bot: 已启动并登录的 BaseBot 实例。
normal_info: 普通发票信息字典。
"""
log.info("开始普通发票报销填报...")
log.info("填写基本信息...")
description = normal_info.get("basic_info", {}).get("reimbursement_description", "元器件采购报销")
fill_basic_info(bot, description)
total_invoices = normal_info.get("reimbursement_details", {}).get("total_invoices", 0)
total_amount = normal_info.get("reimbursement_details", {}).get("total_amount", 0)
log.info(f"录入普通发票总明细 (共 {total_invoices} 张, 合计 ¥{total_amount:.2f})...")
add_normal_item(bot, total_invoices, total_amount)
payment_info = normal_info.get("payment_methods", [])
log.info(f"录入普通发票支付信息 (共 {len(payment_info)} 笔)...")
fill_normal_payment(bot, payment_info)
log.info("上传普通发票附件...")
attachment_info = normal_info.get("attachments", [])
upload_normal_attachments(bot, attachment_info)
log.info("普通发票报销填报完成")
# ------------------------------------------------------------------
# 基本信息
# ------------------------------------------------------------------
def fill_basic_info(bot: BaseBot, description: str = "元器件采购报销") -> None:
"""填写基本信息"""
try:
bot.page.fill("#EXPENEXPLAIN", description)
bot.page.click("#PROJECTCODE", timeout=10000)
bot.page.wait_for_timeout(1000)
bot.page.wait_for_selector("#promodal .fixed-table-body tbody tr", timeout=10000)
first_row = bot.page.query_selector("#promodal .fixed-table-body tbody tr")
if first_row:
first_row.click()
bot.page.wait_for_timeout(1000)
bot.page.click("#saveAndNext", timeout=5000)
bot.page.wait_for_timeout(2000)
bot._screenshot("step3_done")
except Exception as e:
log.error(f"填写基本信息失败: {e}")
bot._screenshot("basic_info_error")
raise
# ------------------------------------------------------------------
# 总明细
# ------------------------------------------------------------------
def add_normal_item(bot: BaseBot, total_invoices: int, total_amount: float) -> None:
"""录入普通发票总明细"""
try:
bot.page.click("#insertDetail", timeout=5000)
bot._wait_for('text="经济事项名称"', timeout=5000)
bot.page.click("#economicscode2")
bot.page.wait_for_timeout(1000)
try:
bot.page.wait_for_selector("#econmodal .fixed-table-body tbody tr", timeout=10000)
rows = bot.page.query_selector_all("#econmodal .fixed-table-body tbody tr")
if len(rows) >= 3:
rows[2].click()
bot.page.wait_for_timeout(1000)
except Exception:
pass
bot.page.fill('input[name="expenPwCommondetail.HOWBILLS"]', str(total_invoices))
bot.page.fill("#je_zwzcdz", f"{total_amount:.2f}")
bot.page.click("#detailAdd", timeout=3000)
bot.page.wait_for_timeout(1000)
bot._screenshot("normal_item_done")
except Exception as e:
log.error(f"录入总明细失败: {e}")
bot._screenshot("normal_item_error")
raise
# ------------------------------------------------------------------
# 支付方式
# ------------------------------------------------------------------
def fill_normal_payment(bot: BaseBot, payment_info: list[dict[str, Any]]) -> None:
"""录入普通发票支付信息"""
try:
bot.page.click('text="下一步(支付方式)"', timeout=5000)
bot._wait_for('text="下一步(附件清单)"', timeout=5000)
for info in payment_info:
bot.page.click("#insertPay", timeout=5000)
bot.page.wait_for_timeout(1000)
bot.page.fill("#personid2", bot.config["default_person_id"])
bot.page.fill("#accountname2", bot.config["default_name"])
bot.page.fill("#receiptdate2", format_date(str(info.get("card_date", ""))))
bot.page.fill("#localaccount2", bot.config["default_card_no"])
bot.page.fill("#receiptmoney2", str(info.get("card_amount", 0)))
bot.page.fill("#money2", str(info.get("card_amount", 0)))
bot.page.fill("#merchant2", str(info.get("merchant", "")))
bot.page.fill("#smark2", str(info.get("remark", "")))
bot.page.click("#payAdd", timeout=3000)
bot.page.wait_for_timeout(1000)
except Exception as e:
log.error(f"支付方式录入失败: {e}")
bot._screenshot("normal_payment_error")
raise
bot._screenshot("normal_payment_done")
# ------------------------------------------------------------------
# 附件上传
# ------------------------------------------------------------------
def upload_normal_attachments(bot: BaseBot, attachment_info: list[dict[str, Any]]) -> None:
"""上传普通发票附件"""
log.info(f"上传普通发票附件 (共 {len(attachment_info)} 个)...")
try:
bot.page.click("#next3", timeout=5000)
bot._wait_for("#submit2", timeout=5000)
for info in attachment_info:
attachment_file = bot.work_dir / info["filename"]
bot._wait_for("#insertAcc", timeout=20000)
bot.page.click("#insertAcc", timeout=5000)
bot._wait_for("#fjlx", timeout=5000)
if info.get("attachment_type") == "invoice":
bot.page.select_option("#fjlx", "1")
else:
bot.page.select_option("#fjlx", "2")
bot.page.fill("#fpsmxx", info.get("attachment_desc", ""))
if attachment_file and attachment_file.exists():
bot.page.set_input_files("#file", str(attachment_file))
bot.page.wait_for_timeout(1000)
bot.page.click("#cjtj", timeout=5000)
log.info("上传普通发票附件完成")
except Exception as e:
log.error(f"附件上传失败: {e}")
bot._screenshot("normal_attachment_error")
raise
bot._screenshot("normal_attachment_done")