From 58ef8f280e188300a8171309ed67c3d21c7570a5 Mon Sep 17 00:00:00 2001 From: Yuuuu <1569752928@qq.com> Date: Mon, 5 Oct 2026 22:59:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=88=E5=90=8C=E7=8F=AD=E5=90=88?= =?UTF-8?q?=E5=8D=95=E5=BD=95=E5=85=A5=E5=86=99=E8=A1=A84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rpa_contract/job.py | 46 ++++++++++++++++++++++++++ rpa_contract/tests/test_job.py | 60 +++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 rpa_contract/job.py diff --git a/rpa_contract/job.py b/rpa_contract/job.py new file mode 100644 index 0000000..c4e197e --- /dev/null +++ b/rpa_contract/job.py @@ -0,0 +1,46 @@ +"""合同班次:领取表 3 → 按供应商合单 → runner 录合同 → 写表 4。""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Callable + +from contract_draft import ContractDraft +from group import draft_from_group, group_by_supplier, missing_mdm_reason +from persist import KIND_FAIL, KIND_OK, ContractOutcome, build_save + +Runner = Callable[[ContractDraft], ContractOutcome] + + +@dataclass +class JobSummary: + claimed: int = 0 + groups: int = 0 + ok: int = 0 + failed: int = 0 + + +def run_batch(repo, *, runner: Runner, limit: int | None = None) -> JobSummary: + claimed = repo.claim_pending(limit=limit) + summary = JobSummary(claimed=len(claimed), groups=0) + for rows in group_by_supplier(claimed): + summary.groups += 1 + outcome = _run_group(rows, runner) + repo.save_group(build_save(rows, outcome)) + if outcome.kind == KIND_OK and (outcome.contract_no or "").strip(): + summary.ok += 1 + else: + summary.failed += 1 + return summary + + +def _run_group(rows, runner: Runner) -> ContractOutcome: + reason = missing_mdm_reason(rows) + if reason: + return ContractOutcome(KIND_FAIL, None, reason) + try: + outcome = runner(draft_from_group(rows)) + except Exception as exc: + return ContractOutcome(KIND_FAIL, None, str(exc)) + if outcome.kind == KIND_OK and not (outcome.contract_no or "").strip(): + return ContractOutcome(KIND_FAIL, None, "submit 模式未读取到真实合同编号") + return outcome diff --git a/rpa_contract/tests/test_job.py b/rpa_contract/tests/test_job.py index 2e1b5f6..c3663e6 100644 --- a/rpa_contract/tests/test_job.py +++ b/rpa_contract/tests/test_job.py @@ -1,7 +1,8 @@ from datetime import date from decimal import Decimal -from persist import KIND_OK, ContractOutcome, build_save +from job import run_batch +from persist import KIND_DILIGENCE, KIND_OK, ContractOutcome, build_save from repo import MemoryContractRepo, PurchaseContractRow @@ -70,3 +71,60 @@ def test_save_group_inserts_one_table4_row_per_oms_id(): assert {row["push_status"] for row in repo.table4} == {0} assert repo.table3[1].process_status == 1 assert repo.table3[3].process_status == 1 + + +def test_job_merges_same_supplier_and_writes_table4(): + repo = MemoryContractRepo( + [ + _row(), + _row(fid=2, oms_id=2026100502, goods_id="G0002"), + _row(fid=3, oms_id=2026100503, contract_entry_status=0, contract_no="HT-OLD"), + ] + ) + calls: list = [] + + def runner(draft): + calls.append(draft) + return ContractOutcome(KIND_OK, "HT20261005001", None) + + run_batch(repo, runner=runner) + assert len(calls) == 1 + assert [line.goods_code for line in calls[0].goods_lines] == ["G0001", "G0002"] + assert calls[0].customer_code == "DW0100046361" + assert [row["oms_id"] for row in repo.table4] == [2026100501, 2026100502] + assert {row["contract_no"] for row in repo.table4} == {"HT20261005001"} + assert {row["action_code"] for row in repo.table4} == {1} + assert {row["contract_status"] for row in repo.table4} == {1} + assert {row["push_status"] for row in repo.table4} == {0} + assert repo.table3[1].process_status == 1 + assert repo.table3[2].process_status == 1 + assert repo.table3[3].process_status == 0 + + +def test_job_marks_diligence_fail(): + repo = MemoryContractRepo([_row()]) + + def runner(draft): + return ContractOutcome(KIND_DILIGENCE, None, "客户未尽调") + + run_batch(repo, runner=runner) + assert repo.table3[1].process_status == 2 + assert "客户未尽调" in repo.table3[1].process_result + assert repo.table4[0]["contract_status"] == 3 + assert repo.table4[0]["contract_no"] is None + assert repo.table4[0]["contract_fail_reason"] == "客户未尽调" + + +def test_job_skips_runner_when_mdm_missing(): + repo = MemoryContractRepo([_row(supplier_mdm_code="")]) + called = {"n": 0} + + def runner(draft): + called["n"] += 1 + return ContractOutcome(KIND_OK, "HT-X", None) + + run_batch(repo, runner=runner) + assert called["n"] == 0 + assert repo.table3[1].process_status == 2 + assert "缺少供应商MDM码" in repo.table3[1].process_result +