9043886b6f
- 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
180 lines
6.6 KiB
Python
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")
|