feat: 合同班合单录入写表4
This commit is contained in:
parent
71fc1c3b60
commit
58ef8f280e
|
|
@ -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
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
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
|
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 {row["push_status"] for row in repo.table4} == {0}
|
||||||
assert repo.table3[1].process_status == 1
|
assert repo.table3[1].process_status == 1
|
||||||
assert repo.table3[3].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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue