AutoRpa/rpa_contract/run_batch.py

176 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 合同班次入口:领表 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())