176 lines
6.3 KiB
Python
176 lines
6.3 KiB
Python
# 合同班次入口:领表 3、调页面流、写表 4。不推接口 4。
|
||
#
|
||
# 崩在 process_status=3。账密读 CONTRACT_USERNAME / CONTRACT_PASSWORD,缺则失败。
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
|
||
from apa_browser_adapter import ApaBrowserAdapter
|
||
from contract_create_node import CONTRACT_URL, ContractCreateInput, build_flow_context
|
||
from contract_draft import ContractDraft
|
||
from contract_flow_steps import run_contract_create_flow
|
||
from pathlib import Path
|
||
|
||
from rpa_oracle import load_job_env
|
||
from job import outcome_from_run, run_batch
|
||
from persist import KIND_DILIGENCE, KIND_HOLD, KIND_OK, LOOKUP_ONLY_RESULT, ContractOutcome
|
||
from repo import OracleContractRepo
|
||
|
||
DEFAULT_CLAIM_LIMIT = 10
|
||
|
||
|
||
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||
"""解析合同班次参数。不传 --limit 时按行领 10 条,不再全部领走。"""
|
||
parser = argparse.ArgumentParser(description="合同班次:领表 3、按供应商合单、写表 4")
|
||
parser.add_argument(
|
||
"--limit",
|
||
type=int,
|
||
default=DEFAULT_CLAIM_LIMIT,
|
||
help="本班最多领取 N 行,默认 10",
|
||
)
|
||
return parser.parse_args(argv)
|
||
|
||
|
||
def contract_lookup_title(draft: ContractDraft) -> str:
|
||
"""拼查重标题:客商名 + 采购协议 + 签订日。不含货主和起止日,也不是合单键。"""
|
||
who = (draft.customer_name or draft.supplier_name or "").strip()
|
||
sign = (draft.contract_sign_date or draft.order_date or "").replace("-", "")
|
||
return f"{who}采购协议{sign}"
|
||
|
||
|
||
def decide_existing_contracts(
|
||
numbers: list[str], *, lookup_only: bool
|
||
) -> ContractOutcome | None:
|
||
"""按「我的合同」命中行决定这一组。
|
||
|
||
0 条且还可以起草时返回 None,调用方才走 S06–S12。
|
||
恰好 1 条复用该合同号,只落表 4。多条停在处理中,不取最新一条。
|
||
只查号时 0 条也停在处理中。
|
||
"""
|
||
hits = list(numbers)
|
||
if len(hits) > 1:
|
||
return ContractOutcome(KIND_HOLD, None, "我的合同命中多条")
|
||
if len(hits) == 1:
|
||
number = (hits[0] or "").strip()
|
||
if not number:
|
||
return ContractOutcome(KIND_HOLD, None, LOOKUP_ONLY_RESULT)
|
||
return ContractOutcome(KIND_OK, number, None)
|
||
if lookup_only:
|
||
return ContractOutcome(KIND_HOLD, None, LOOKUP_ONLY_RESULT)
|
||
return None
|
||
|
||
|
||
def _pull_back_to_draft(browser) -> None:
|
||
"""一组开始前离开上一张合同的页面,回到起草页。"""
|
||
from contract_common import FORM_PAGE_URL
|
||
|
||
browser.open_url(FORM_PAGE_URL)
|
||
|
||
|
||
def _flow_context(draft: ContractDraft):
|
||
"""按这组草稿建页面上下文。账密仍只认环境变量。"""
|
||
spec = ContractCreateInput(
|
||
job_id="contract-batch",
|
||
supplier_name=draft.supplier_name,
|
||
order_date=draft.order_date,
|
||
source_type=draft.source_type or "",
|
||
submit_mode="submit",
|
||
)
|
||
return build_flow_context(spec, draft)
|
||
|
||
|
||
def _lookup_my_contracts(browser, draft: ContractDraft) -> list[str]:
|
||
"""进起草前查「我的合同」。命中行的合同号原样返回,不取最新一条。"""
|
||
from contract_number import (
|
||
_open_my_contracts,
|
||
_scrape_my_contract_rows,
|
||
_search_my_contracts,
|
||
)
|
||
|
||
ctx = _flow_context(draft)
|
||
_open_my_contracts(browser, ctx)
|
||
_search_my_contracts(
|
||
browser,
|
||
contract_lookup_title(draft),
|
||
(draft.customer_code or "").strip(),
|
||
)
|
||
rows = _scrape_my_contract_rows(browser)
|
||
return [str(row.get("no") or "") for row in rows]
|
||
|
||
|
||
def _create_and_read_number(browser, draft: ContractDraft) -> ContractOutcome:
|
||
"""0 条时走 S06–S12。提交后读不到号则停在处理中,下一班只查号。"""
|
||
_pull_back_to_draft(browser)
|
||
ctx = _flow_context(draft)
|
||
|
||
def run() -> str:
|
||
"""在已打开的浏览器里跑起草并返回合同号。"""
|
||
return run_contract_create_flow(browser, ctx)
|
||
|
||
outcome = outcome_from_run(run)
|
||
if outcome.kind == KIND_OK and (outcome.contract_no or "").strip():
|
||
return outcome
|
||
if outcome.kind == KIND_DILIGENCE:
|
||
return outcome
|
||
if outcome.kind == KIND_FAIL and "未读取到真实合同编号" in (outcome.reason or ""):
|
||
return ContractOutcome(KIND_HOLD, None, LOOKUP_ONLY_RESULT, outcome.error_text)
|
||
return outcome
|
||
|
||
|
||
def production_runner(
|
||
browser: ApaBrowserAdapter,
|
||
draft: ContractDraft,
|
||
*,
|
||
find_contracts=None,
|
||
create=None,
|
||
) -> ContractOutcome:
|
||
"""每个合单组先拉回起草页,再查「我的合同」。
|
||
|
||
查重三态:0 条才走 S06–S12;恰好 1 条只落表 4,不起草第二份;
|
||
多条停在 process_status=3,不新建,也不取最新一条。
|
||
标题是客商名 + 「采购协议」 + 签订日,不含货主和起止日。标题和客商不是合单键。
|
||
提交后读不到号也停在 3,下一班只查号。
|
||
"""
|
||
if find_contracts is None:
|
||
_pull_back_to_draft(browser)
|
||
numbers = _lookup_my_contracts(browser, draft)
|
||
else:
|
||
numbers = list(find_contracts(browser, draft))
|
||
decided = decide_existing_contracts(
|
||
numbers,
|
||
lookup_only=bool(getattr(draft, "lookup_only", False)),
|
||
)
|
||
if decided is not None:
|
||
return decided
|
||
if create is None:
|
||
return _create_and_read_number(browser, draft)
|
||
number = (create(browser, draft) or "").strip()
|
||
if number:
|
||
return ContractOutcome(KIND_OK, number, None)
|
||
return ContractOutcome(KIND_HOLD, None, LOOKUP_ONLY_RESULT)
|
||
|
||
|
||
def main(argv: list[str] | None = None, repo=None, runner=None) -> int:
|
||
"""领表 3、按供应商合单录合同、写表 4。有失败则进程退出码 1。
|
||
|
||
不传 --limit 时最多领 10 行。传入 repo 和 runner 时不打开浏览器。
|
||
"""
|
||
args = parse_args(argv)
|
||
load_job_env(Path(__file__).resolve().parent)
|
||
if repo is None:
|
||
repo = OracleContractRepo.from_env()
|
||
if runner is None:
|
||
browser = ApaBrowserAdapter(url=CONTRACT_URL, flow="CONTRACT_CREATE")
|
||
runner = lambda draft: production_runner(browser, draft)
|
||
summary = run_batch(repo, runner=runner, limit=args.limit)
|
||
print(
|
||
f"claimed={summary.claimed} groups={summary.groups} "
|
||
f"ok={summary.ok} failed={summary.failed}"
|
||
)
|
||
return 0 if summary.failed == 0 else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|