# 接口 3 整批校验与落表。不跑合同作业。 # # 入参 camelCase,落表 snake_case。幂等键是对方 fid。 # 请求里不录合同。 from __future__ import annotations from . import messages from .parse import parse_date, parse_decimal, parse_fid, parse_int, parse_oms_id, parse_str _STRING_FIELDS = ( ("supplierId", "supplier_id", 20), ("supplierName", "supplier_name", 200), ("goodsId", "goods_id", 20), ("goodsName", "goods_name", 200), ("regNo", "reg_no", 100), ("spec", "spec", 100), ("unit", "unit", 20), ("goodsSourceId", "goods_source_id", 20), ("purchaserId", "purchaser_id", 20), ("purchaserName", "purchaser_name", 100), ("plannerId", "planner_id", 20), ("plannerName", "planner_name", 100), ("orgCode", "org_code", 10), ("goodsMdmCode", "goods_mdm_code", 100), ("supplierMdmCode", "supplier_mdm_code", 100), ) _DATE_FIELDS = ( ("contractSignDate", "contract_sign_date"), ("contractStartDate", "contract_start_date"), ("contractEndDate", "contract_end_date"), ) _DECIMAL_FIELDS = ( ("qty", "qty"), ("purchaseTaxRate", "purchase_tax_rate"), ("unitPrice", "unit_price"), ) # 整批接收采购合同:先校验再落库,返回与入参下标对齐的结果。 def receive_purchase_contracts(rows: list, repo) -> list[dict]: results: list[dict | None] = [None] * len(rows) pending: list[tuple[int, dict]] = [] seen: set[int] = set() for index, row in enumerate(rows): decided = _classify(row, seen) if "resultCode" in decided: results[index] = decided else: pending.append((index, decided)) if pending: _persist(results, pending, repo) return [item for item in results if item is not None] # 内存判定:对象/字段/同批 fid 冲突当场出结果,通过则返回待插入记录。 def _classify(row: object, seen: set[int]) -> dict: if not isinstance(row, dict): return _result(None, "REJECTED", messages.ROW_NOT_OBJECT) fid, fid_error = parse_fid(row.get("fid")) if fid is not None and fid in seen: return _result(fid, "CONFLICT", messages.CONTRACT_CONFLICT) if fid is not None: seen.add(fid) parsed, error = _parse_row(row, fid, fid_error) if error: _camel, message = error return _result(row.get("fid"), "REJECTED", message) return parsed # 对通过校验的行一次查重、一次插入,把 DUPLICATE / ACCEPTED 填回 results。 def _persist(results: list[dict | None], pending: list[tuple[int, dict]], repo) -> None: existing = repo.existing_fids([record["fid"] for _index, record in pending]) to_insert: list[dict] = [] insert_at: list[int] = [] for index, record in pending: if record["fid"] in existing: results[index] = _result( record["fid"], "DUPLICATE", messages.CONTRACT_DUPLICATE ) else: to_insert.append(record) insert_at.append(index) collided = repo.insert_many(to_insert) if to_insert else set() for index, record in zip(insert_at, to_insert): if record["fid"] in collided: results[index] = _result( record["fid"], "DUPLICATE", messages.CONTRACT_DUPLICATE ) else: results[index] = _result( record["fid"], "ACCEPTED", messages.CONTRACT_ACCEPTED ) # 把一条明细收成表 3 行。失败返回 (None, (字段, 说明))。 def _parse_row(row: dict, fid: int | None, fid_error: str | None): if fid_error: return None, ("fid", fid_error) oms_id, oms_error = parse_oms_id(row.get("omsId")) if oms_error: return None, ("omsId", oms_error) record: dict = {"fid": fid, "oms_id": oms_id, "process_status": 0} for camel, snake, max_len in _STRING_FIELDS: value, error = parse_str(row.get(camel), camel, max_len) if error: return None, (camel, error) record[snake] = value for camel, snake in _DATE_FIELDS: value, error = parse_date(row.get(camel), camel) if error: return None, (camel, error) record[snake] = value if record["contract_end_date"] < record["contract_start_date"]: return None, ("contractEndDate", "contractEndDate 不得早于 contractStartDate") payment_term, error = parse_int(row.get("paymentTerm"), "paymentTerm") if error: return None, ("paymentTerm", error) record["payment_term"] = payment_term for camel, snake in _DECIMAL_FIELDS: value, error = parse_decimal(row.get(camel), camel) if error: return None, (camel, error) record[snake] = value customer_type, error = parse_int(row.get("customerType"), "customerType") if error: return None, ("customerType", error) record["customer_type"] = customer_type entry_status, error = _parse_entry_status(row.get("contractEntryStatus")) if error: return None, ("contractEntryStatus", error) record["contract_entry_status"] = entry_status contract_no, error = _parse_contract_no(row.get("contractNo"), entry_status) if error: return None, ("contractNo", error) record["contract_no"] = contract_no return record, None # 录入合同状态:缺省当 None;有值必须是 0 或 1。 def _parse_entry_status(value) -> tuple[int | None, str | None]: if value is None or value == "": return None, None if type(value) is int and value in (0, 1): return value, None return None, "contractEntryStatus 必须是 0 或 1" # contractEntryStatus=0 时合同号必填;其它情况可空。 def _parse_contract_no(value, entry_status: int | None) -> tuple[str | None, str | None]: if entry_status == 0: return parse_str(value, "contractNo", 40) if value is None or value == "": return None, None return parse_str(value, "contractNo", 40) # 拼一条接口 3 的 results[] 项。 def _result(fid, result_code: str, message: str) -> dict: return {"fid": fid, "resultCode": result_code, "message": message}