AutoRpa/rpa_rebate/tests/test_header.py

296 lines
11 KiB
Python

from accept import accept_log_path
from apa_browser_adapter import chrome_debug_port, chrome_session_flow, resolve_chrome_profile_dir
from rebate_flow_steps import (
CONTACT_DEFAULT,
LOGIN,
PAY_MODE_NEXT_MONTH_N,
PLANNER_DEFAULT,
RebateFlowContext,
_pick_person,
build_header_draft,
fill_rebate_header,
header_draft_from_table3,
import_lines_from_table3,
run_header_flow,
)
class FakeBrowser:
def __init__(self, *, table_text="", body_text="", login_body="", order_no=""):
self.table_text = table_text
self.body_text = body_text
self.login_body = login_body or body_text
self.order_no = order_no
self.opened: list[str] = []
self.filled: list[tuple[list[str], str]] = []
self.selected: list[tuple[list[str], str]] = []
self.clicked: list[list[str]] = []
self.script_count = 0
def open_url(self, url: str) -> None:
self.opened.append(url)
def fill_first(self, selectors: list[str], value: str) -> None:
self.filled.append((list(selectors), value))
def click_first(self, selectors: list[str]) -> None:
self.clicked.append(list(selectors))
def click_native(self, selectors: list[str]) -> None:
self.click_first(selectors)
def wait_for_text(self, text: str, timeout_seconds: int = 15) -> None:
haystack = f"{self.body_text}\n{self.table_text}\n{self.login_body}"
if text not in haystack:
raise TimeoutError(text)
def wait_for_element(self, selectors: list[str], timeout_seconds: int = 15) -> None:
return None
def element_exists(self, selectors: list[str]) -> bool:
return True
def read_body_text(self) -> str:
return self.body_text
def execute_script(self, script: str, *args):
self.script_count += 1
text = script or ""
if "OPEN_HEADER_ROW" in text:
return {"ok": True, "via": "row"}
if "SELECT_HEADER_ROW" in text:
return {"ok": True, "text": str(args[0] if args else "")}
if "HEADER_SELECTION" in text:
no = str(args[0] if args else "")
return {"ok": True, "cells": [no], "contract": no, "checkedIdx": 0, "pager": "共1条"}
if "CLOSE_HEADER_QUERY" in text or "CLOSE_DETAIL_CARD" in text:
return {"ok": True}
if "CLICK_HEADER_TOOLBAR" in text:
self.clicked.append([str(args[0] if args else "")])
return {"ok": True}
if "DETAIL_TOAST" in text:
return {"texts": []}
if "HEADER_CARD" in text:
return False
if "OPEN_LABELED_PICKER" in text:
return {"ok": True}
if "FILL_HEADER_LOOKUP" in text or "PICK_HEADER_LOOKUP" in text:
return {"ok": True}
if "CONFIRM_HEADER_CARD" in text:
return {"ok": True, "via": "确认"}
if "SET_HEADER_CELL" in text:
return {"ok": False}
if "SET_LABELED" in text:
label = args[0] if args else ""
value = args[1] if len(args) > 1 else ""
self.filled.append(([str(label)], str(value)))
return True
if "READ_REBATE_ORDER_NO" in text:
return self.order_no
return self.table_text
def screenshot(self, name: str) -> str:
return name
def read_first(self, selectors: list[str], value_name: str = "") -> str:
return self.order_no
def switch_to_frame(self, selectors: list[str]) -> None:
return None
def switch_to_default_content(self) -> None:
return None
def select_first(self, selectors: list[str], value: str) -> None:
self.selected.append((list(selectors), value))
def select_xform(self, name: str, value: str) -> None:
return None
def upload_file_first(self, selectors: list[str], file_path: str) -> None:
return None
def _ctx() -> RebateFlowContext:
return RebateFlowContext(rebate_url="http://example", username="u", password="p")
def _draft():
return build_header_draft(
planner_name="李四",
supplier_code="DW0100046361",
rebate_unit_code="901",
today="2026-10-09",
)
def test_header_draft_from_table3_uses_mdm_and_planner():
draft = header_draft_from_table3(
[
{
"supplier_id": "SUP001",
"supplier_mdm_code": "DW0100046361",
"planner_name": "李四",
"purchaser_name": "张三",
"org_code": "901",
"goods_id": "4000018689",
"qty": "10",
"unit_price": "10",
"oms_id": "20260928000003",
}
],
today="2026-10-12",
)
assert draft.supplier_code == "DW0100046361"
assert draft.planner_name == PLANNER_DEFAULT
assert draft.rebate_unit_code == "901"
assert draft.contact_name == CONTACT_DEFAULT
lines = import_lines_from_table3(
[
{
"goods_id": "4000018689",
"qty": "10",
"unit_price": "10",
"oms_id": "20260928000003",
"goods_source_id": "SRC1",
}
]
)
assert lines[0].goods_id == "4000018689"
assert lines[0].order_no == "20260928000003"
assert lines[0].tax_rate == "0.13"
assert lines[0].net_price == "8.8496"
def test_header_defaults_follow_mapping_not_scheme_55():
draft = _draft()
assert draft.planner_name == "李四"
assert draft.pay_mode == PAY_MODE_NEXT_MONTH_N
assert draft.protocol_start == "2026-10-09"
assert draft.protocol_end == "2026-10-09"
assert draft.rebate_contract == "否"
assert draft.settle_mode == "否"
def test_header_draft_defaults_planner():
draft = build_header_draft(planner_name="", supplier_code="S1", rebate_unit_code="901")
assert draft.planner_name == PLANNER_DEFAULT
assert draft.contact_name == CONTACT_DEFAULT
def test_fill_header_uses_mapping_values():
browser = FakeBrowser(
table_text="HT1 重庆亿昊",
body_text="返利合约 计划员 承付模式 保存",
)
fill_rebate_header(browser, _draft())
filled_values = [value for _, value in browser.filled]
selected_values = [value for _, value in browser.selected]
assert "李四" in filled_values
assert "2026-10-09" in filled_values
assert PAY_MODE_NEXT_MONTH_N in selected_values
assert "否" in selected_values
assert "廖建峰" not in filled_values
assert "账期" not in selected_values
assert "2026-12-31" not in filled_values
def test_header_flow_writes_accept_with_order_no(tmp_path, monkeypatch):
monkeypatch.delenv("RPA_ACCEPT_LOG", raising=False)
monkeypatch.setattr("accept.SCREENSHOT_ROOT", tmp_path)
browser = FakeBrowser(
table_text="HT1 重庆亿昊",
body_text="采购协议管理 合同编号 查询 返利合约 承付模式 计划员 保存 总单号 FL20261009001",
login_body="采购协议管理",
order_no="FL20261009001",
)
order_no = run_header_flow(browser, _ctx(), "HT1", _draft())
assert order_no == "FL20261009001"
text = accept_log_path("REBATE_HEADER").read_text(encoding="utf-8")
assert "ACCEPT SUMMARY REBATE_HEADER 8/8 ok" in text
assert "ACCEPT S09 ok 读总单号" in text
assert "rebateOrderNo=FL20261009001" in text
assert "planner=李四" in text
assert f"payMode={PAY_MODE_NEXT_MONTH_N}" in text
assert "protocolEnd=2026-10-09" in text
def test_header_reuses_logged_in_session(tmp_path, monkeypatch):
monkeypatch.delenv("RPA_ACCEPT_LOG", raising=False)
monkeypatch.setattr("accept.SCREENSHOT_ROOT", tmp_path)
browser = FakeBrowser(
table_text="HT1 重庆亿昊",
body_text="采购协议管理 待审批 合同编号 查询 返利合约 承付模式 计划员 保存",
login_body="采购协议管理",
order_no="FL1",
)
run_header_flow(browser, _ctx(), "HT1", _draft())
login_fills = [value for selectors, value in browser.filled if selectors == LOGIN["user"]]
assert login_fills == []
assert "u" not in [value for _, value in browser.filled]
def test_header_chrome_session_reuses_find():
assert chrome_session_flow("REBATE_HEADER") == "REBATE_FIND"
assert resolve_chrome_profile_dir("REBATE_HEADER") == resolve_chrome_profile_dir("REBATE_FIND")
assert chrome_debug_port("REBATE_HEADER") == chrome_debug_port("REBATE_FIND")
class PlannerPickBrowser:
"""计划员三个点打开自己的选择层,不拿货源选择当层。"""
def __init__(self):
self.planner_name = ""
self.planner_id = ""
self.layer_open = False
self.scripts: list[str] = []
def execute_script(self, script: str, *args):
text = script or ""
self.scripts.append(text)
if "READ_LABELED_INPUT" in text:
title = str(args[0] if args else "")
if title == "计划员ID":
return {"value": self.planner_id}
if title == "计划员":
return {"value": self.planner_name}
return {"value": ""}
if "CLOSE_HEADER_QUERY" in text:
return {"ok": True}
if "OPEN_LABELED_PICKER" in text:
self.layer_open = True
return {"ok": True, "via": "more-outline"}
if "PERSON_PICKER_VISIBLE" in text:
return self.layer_open
if "FOCUS_PERSON_FIELD" in text:
return {"ok": True, "ph": "请输入计划员", "title": "计划员选择"}
if "CLICK_PERSON_QUERY" in text:
return {"ok": True, "via": "query"}
if "PERSON_PICKER_ROWS" in text:
return {"ok": True, "match": True, "count": 1, "row": "廖建琦"}
if "PICK_PERSON_ROW" in text:
self.planner_name = "廖建琦"
self.planner_id = "U1001"
self.layer_open = False
return {"ok": True, "text": "廖建琦"}
if "CLOSE_PERSON_PICKER" in text:
self.layer_open = False
return {"ok": True, "via": "close"}
if "SCAN_LOOKUP_OVERLAYS" in text:
return ["计划员选择"] if self.layer_open else []
if "SOURCE_CHOICE" in text or "CLOSE_SOURCE_CHOICE" in text:
return False
return None
def test_pick_person_selects_liao_and_binds_id():
browser = PlannerPickBrowser()
_pick_person(browser, "计划员", PLANNER_DEFAULT)
assert browser.planner_name == "廖建琦"
assert browser.planner_id == "U1001"
joined = "\n".join(browser.scripts)
assert "FOCUS_PERSON_FIELD" in joined
assert "PICK_PERSON_ROW" in joined
assert "FOCUS_SOURCE_FIELD" not in joined
assert "PICK_SOURCE_ROW" not in joined