diff --git a/rpa_contract/contract_create_node.py b/rpa_contract/contract_create_node.py index 899f547..8d2df60 100644 --- a/rpa_contract/contract_create_node.py +++ b/rpa_contract/contract_create_node.py @@ -141,6 +141,8 @@ def build_flow_context(spec: ContractCreateInput, draft: ContractDraft) -> Contr amount=draft.amount, contract_title=draft.contract_title, contract_end_date=draft.contract_end_date, + contract_sign_date=draft.contract_sign_date, + contract_start_date=draft.contract_start_date, is_major_contract=draft.is_major_contract, is_prepayment=draft.is_prepayment, belong_year=draft.belong_year, diff --git a/rpa_contract/contract_draft.py b/rpa_contract/contract_draft.py index 5b8902c..c4f9e1e 100644 --- a/rpa_contract/contract_draft.py +++ b/rpa_contract/contract_draft.py @@ -35,6 +35,8 @@ class ContractDraft: amount: str = "" contract_title: str = "" contract_end_date: str = "" + contract_sign_date: str = "" + contract_start_date: str = "" is_major_contract: str = "否" is_prepayment: str = "否" belong_year: str = "" @@ -87,6 +89,8 @@ def contract_draft_from_dict(data: dict[str, Any]) -> ContractDraft: amount=str(data.get("amount", "")), contract_title=str(data.get("contractTitle") or data.get("docSubject") or ""), contract_end_date=str(data.get("contractEndDate", "")), + contract_sign_date=str(data.get("contractSignDate", "")), + contract_start_date=str(data.get("contractStartDate", "")), is_major_contract=str(data.get("isMajorContract", "否")), is_prepayment=str(data.get("isPrepayment", "否")), belong_year=str(data.get("belongYear", "")), diff --git a/rpa_contract/contract_flow_steps.py b/rpa_contract/contract_flow_steps.py index 5f1f988..9e0eab6 100644 --- a/rpa_contract/contract_flow_steps.py +++ b/rpa_contract/contract_flow_steps.py @@ -8,6 +8,7 @@ from typing import Protocol from accept import AcceptanceReport, accept_log_path, evidence_shot from errors import ( + DiligenceError, LoginFailed, NodeError, PdfMergeUnsupported, @@ -69,6 +70,8 @@ class ContractFlowContext: amount: str = "" contract_title: str = "" contract_end_date: str = "" + contract_sign_date: str = "" + contract_start_date: str = "" is_major_contract: str = "否" is_prepayment: str = "否" belong_year: str = "" @@ -104,6 +107,7 @@ def run_contract_create_flow(browser: BrowserAdapter, ctx: ContractFlowContext) enter_contract_create(browser) select_contract_template(browser, ctx) fill_contract_signatory_info(browser, ctx) + check_customer_diligence(browser, ctx) fill_contract_basic_info(browser, ctx) fill_contract_amount_and_dates(browser, ctx) fill_contract_goods_lines(browser, ctx) @@ -531,14 +535,30 @@ def _select_settlement_type(browser: BrowserAdapter, value: str) -> None: raise NodeError(f"结算方式下拉未选中,仍停留在「==请选择==」,期望「{value}」") +def sign_date_for(ctx: ContractFlowContext) -> str: + return (ctx.contract_sign_date or ctx.order_date or "").strip() + + +def begin_date_for(ctx: ContractFlowContext) -> str: + return (ctx.contract_start_date or ctx.order_date or "").strip() + + +def check_customer_diligence(browser: BrowserAdapter, ctx: ContractFlowContext) -> None: + text = browser.read_body_text() or "" + if "未尽调" in text: + raise DiligenceError("客户未尽调") + + def fill_contract_amount_and_dates(browser: BrowserAdapter, ctx: ContractFlowContext) -> None: amount_type = (ctx.amount_type or "合同标的金额").strip() if amount_type: browser.select_xform(X["amount_type"], amount_type) if ctx.amount: browser.fill_first([F["amount"]], ctx.amount) - browser.fill_first([F["sign_date"]], ctx.order_date) - browser.fill_first([F["begin_date"]], ctx.order_date) + sign_date = sign_date_for(ctx) + begin_date = begin_date_for(ctx) + browser.fill_first([F["sign_date"]], sign_date) + browser.fill_first([F["begin_date"]], begin_date) if ctx.contract_end_date: browser.fill_first([F["end_date"]], ctx.contract_end_date) evidence_shot(browser, "contract-amount-and-dates-filled") @@ -547,8 +567,8 @@ def fill_contract_amount_and_dates(browser: BrowserAdapter, ctx: ContractFlowCon "金额日期", [ ("amount", _try_read(browser, [F["amount"]]), ctx.amount), - ("signDate", _try_read(browser, [F["sign_date"]]), ctx.order_date), - ("beginDate", _try_read(browser, [F["begin_date"]]), ctx.order_date), + ("signDate", _try_read(browser, [F["sign_date"]]), sign_date), + ("beginDate", _try_read(browser, [F["begin_date"]]), begin_date), ("endDate", _try_read(browser, [F["end_date"]]), ctx.contract_end_date), ], browser=browser, diff --git a/rpa_contract/errors.py b/rpa_contract/errors.py index 01ac7a8..54a567c 100644 --- a/rpa_contract/errors.py +++ b/rpa_contract/errors.py @@ -29,3 +29,9 @@ class UnknownRpaError(NodeError): error_code = "RPA_UNKNOWN_ERROR" retryable = True route_role = "OPS" + + +class DiligenceError(NodeError): + error_code = "BIZ_CUSTOMER_NOT_DILIGENCE" + retryable = False + route_role = "PURCHASER" diff --git a/rpa_contract/tests/test_flow_gates.py b/rpa_contract/tests/test_flow_gates.py new file mode 100644 index 0000000..1c99c4b --- /dev/null +++ b/rpa_contract/tests/test_flow_gates.py @@ -0,0 +1,50 @@ +from contract_flow_steps import ( + ContractFlowContext, + begin_date_for, + check_customer_diligence, + sign_date_for, +) +from errors import DiligenceError +import pytest + + +def _ctx(**overrides) -> ContractFlowContext: + data = dict( + contract_url="http://example", + username="u", + password="p", + supplier_name="示例", + order_date="2026-09-28", + source_type="", + ) + data.update(overrides) + return ContractFlowContext(**data) + + +class _FakeBrowser: + def __init__(self, text: str): + self._text = text + + def read_body_text(self) -> str: + return self._text + + +def test_sign_and_begin_fall_back_to_order_date(): + ctx = _ctx() + assert sign_date_for(ctx) == "2026-09-28" + assert begin_date_for(ctx) == "2026-09-28" + + +def test_sign_and_begin_use_contract_dates_when_set(): + ctx = _ctx(contract_sign_date="2026-10-01", contract_start_date="2026-10-02") + assert sign_date_for(ctx) == "2026-10-01" + assert begin_date_for(ctx) == "2026-10-02" + + +def test_diligence_raises_when_page_says_not_done(): + with pytest.raises(DiligenceError, match="客户未尽调"): + check_customer_diligence(_FakeBrowser("客户未尽调,不能起草"), _ctx()) + + +def test_diligence_passes_when_text_absent(): + check_customer_diligence(_FakeBrowser("客户已尽调"), _ctx())