"""地磅模块 — CRM 产品解析与快照写入。""" from __future__ import annotations from dataclasses import dataclass from fastapi import HTTPException, status from sqlalchemy.orm import Session from app.crud.crm.product import get_product @dataclass(frozen=True) class ProductSnapshot: product_id: str product_name: str specification: str | None def resolve_crm_product( db: Session, tenant_no: str, product_id: str, *, require_on_shelf: bool = True, ) -> ProductSnapshot: """校验 CRM 产品并返回名称/规格快照。新建单据 product_id 必填。 require_on_shelf=False 用于运输计划联动地磅等历史单据场景: 合同/计划中的产品可能已下架,但仍允许按主数据快照继续业务。 """ pid = (product_id or "").strip() if not pid: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="请选择 CRM 产品", ) row = get_product(db, pid) if row is None or row.tenant_no != tenant_no: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="产品不存在或不可用", ) if require_on_shelf and row.status != "on": raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="产品已下架,请重新选择", ) name = (row.product_name or "").strip() if not name: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="产品名称无效", ) model = (row.model or "").strip() or None return ProductSnapshot(product_id=row.id, product_name=name, specification=model) def apply_product_snapshot_to_plan(plan, snapshot: ProductSnapshot) -> None: plan.product_id = snapshot.product_id plan.product = snapshot.product_name plan.specification = snapshot.specification def apply_product_snapshot_to_inventory(row, snapshot: ProductSnapshot) -> None: row.product_id = snapshot.product_id row.product = snapshot.product_name row.specification = snapshot.specification def apply_product_snapshot_to_weighing(row, snapshot: ProductSnapshot) -> None: row.product_id = snapshot.product_id row.cargo_name = snapshot.product_name row.specification = snapshot.specification