"""普通报销填报流程 负责普通发票报销的完整填报步骤: 基本信息 → 总明细 → 支付方式 → 附件上传 """ 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.open_tab("basic") 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: if not bot.open_tab("details"): raise RuntimeError("切不到报销明细标签页") 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: # 不靠「下一步」:续填时页面停在基本信息,#next3 之类别的标签上的按钮根本不可见 if not bot.open_tab("payment"): bot.click_if_visible('text="下一步(支付方式)"') if not bot.tab_ready("payment"): raise RuntimeError("切不到支付方式标签页") 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: if not bot.open_tab("attachments"): bot.click_if_visible("#next3") if not bot.tab_ready("attachments"): raise RuntimeError("切不到附件清单标签页") bot._wait_for("#submit2", timeout=5000) for info in attachment_info: bot.upload_attachment( str(info["filename"]), str(info.get("attachment_type", "invoice")), str(info.get("attachment_desc", "")), ) landed = bot.attachment_rows() if landed < len(attachment_info): log.warning( "附件清单只数到 %s 行,预期 %s 个(提交都确认过,可能被页面折叠)", landed, len(attachment_info) ) log.info("上传普通发票附件完成(逐个确认提交,清单 %s 行)", landed) except Exception as e: log.error(f"附件上传失败: {e}") bot._screenshot("normal_attachment_error") raise bot._screenshot("normal_attachment_done")