"""接口 1 逐条校验与落表。不跑选品。 入参 camelCase,落表 snake_case。长度跟 VARCHAR2。 单条结果: - ACCEPTED:入库,deal_flag=0,等选品作业捞 - DUPLICATE:库里已有同一 omsId,不更新、不重跑 - CONFLICT:同一请求里 omsId 出现过(前一条哪怕被拒,后一条也算冲突) - REJECTED:字段不过,不入库 混批 HTTP 仍 200。omsId / 数量必须是 JSON 整数,不把 "10" 当 10。 """ from __future__ import annotations from datetime import date, datetime from decimal import Decimal, InvalidOperation from .repo import DuplicateOmsId # (接口字段, 表列, 最大字符数) _STRING_FIELDS = ( ("barCode", "bar_code", 40), ("goodsId", "goods_id", 20), ("goodsName", "goods_name", 200), ("goodsSpec", "goods_spec", 100), ("manufacturer", "manufacturer", 100), ("approvalNo", "approval_no", 100), ("unit", "unit", 20), ("customerId", "customer_id", 20), ("customerName", "customer_name", 200), ("orgCode", "org_code", 10), ("goodsMdmCode", "goods_mdm_code", 100), ("customerMdmCode", "customer_mdm_code", 100), ) _INT_FIELDS = ( ("saleQty", "sale_qty"), ("planPurchaseQty", "plan_purchase_qty"), ) def receive_sales_goods(rows: list, repo) -> list[dict]: seen: set[int] = set() results: list[dict] = [] for row in rows: results.append(_receive_one(row, repo, seen)) return results def _receive_one(row: object, repo, seen: set[int]) -> dict: if not isinstance(row, dict): return _result(None, "REJECTED", "明细必须是对象") oms_id, oms_error = _parse_oms_id(row.get("omsId")) if oms_id is not None and oms_id in seen: return _result(oms_id, "CONFLICT", "同一请求中 omsId 重复") if oms_id is not None: seen.add(oms_id) parsed, error = _parse_row(row, oms_id, oms_error) if error: _camel, message = error return _result(row.get("omsId"), "REJECTED", message) try: repo.insert(parsed) except DuplicateOmsId: return _result(parsed["oms_id"], "DUPLICATE", "订单中心序号已存在,未重复入库") return _result(parsed["oms_id"], "ACCEPTED", "接收成功,等待RPA处理") def _parse_row(row: dict, oms_id: int | None, oms_error: str | None): if oms_error: return None, ("omsId", oms_error) record: dict = {"oms_id": oms_id, "deal_flag": 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 order_date, error = _parse_date(row.get("orderDate")) if error: return None, ("orderDate", error) record["order_date"] = order_date sale_price, error = _parse_decimal(row.get("salePrice"), "salePrice") if error: return None, ("salePrice", error) record["sale_price"] = sale_price for camel, snake in _INT_FIELDS: value, error = _parse_int(row.get(camel), camel) if error: return None, (camel, error) record[snake] = value return record, None def _parse_oms_id(value) -> tuple[int | None, str | None]: if value is None or value == "": return None, "缺少必填字段 omsId" # bool 是 int 子类,必须用 type(value) is int。oms_id 列 number(20)。 if type(value) is int and 0 < value < 10**20: return value, None return None, "omsId 必须是整数" def _parse_str(value, camel: str, max_len: int) -> tuple[str | None, str | None]: if value is None: return None, f"缺少必填字段 {camel}" if not isinstance(value, str): return None, f"{camel} 必须是字符串" text = value.strip() if not text: return None, f"缺少必填字段 {camel}" if len(text) > max_len: return None, f"{camel} 超出长度" return text, None def _parse_int(value, camel: str) -> tuple[int | None, str | None]: if value is None or value == "": return None, f"缺少必填字段 {camel}" if type(value) is int: return value, None return None, f"{camel} 必须是整数" def _parse_decimal(value, camel: str) -> tuple[Decimal | None, str | None]: if value is None or value == "": return None, f"缺少必填字段 {camel}" if type(value) is bool: return None, f"{camel} 必须是数字" try: if isinstance(value, Decimal): amount = value elif isinstance(value, str): amount = Decimal(value.strip()) elif type(value) is int: amount = Decimal(value) elif isinstance(value, float): amount = Decimal(str(value)) else: return None, f"{camel} 必须是数字" except (InvalidOperation, ValueError): return None, f"{camel} 必须是数字" return amount, None def _parse_date(value) -> tuple[date | None, str | None]: if value is None or value == "": return None, "缺少必填字段 orderDate" if isinstance(value, datetime): return value.date(), None if isinstance(value, date): return value, None if not isinstance(value, str): return None, "orderDate 必须是 YYYY-MM-DD" try: return date.fromisoformat(value.strip()), None except ValueError: return None, "orderDate 必须是 YYYY-MM-DD" def _result(oms_id, result_code: str, message: str) -> dict: return {"omsId": oms_id, "resultCode": result_code, "message": message}