"""客户装/送货物流默认记忆""" from __future__ import annotations import logging from decimal import Decimal from typing import Any, Mapping, Optional, Sequence from sqlalchemy.orm import Session from app.core.utils import generate_uuid from app.models.pl_models import PlCustomerStopPref logger = logging.getLogger(__name__) STOP_LOAD = "load" STOP_DELIVERY = "delivery" def _as_str(value: Any) -> str: return str(value or "").strip() def _row_to_item(stop_type: str, row: Mapping[str, Any]) -> Optional[dict]: customer_id = _as_str(row.get("customer_id") or row.get("customerId")) if not customer_id: return None lng = row.get("lng") lat = row.get("lat") return { "customer_id": customer_id, "customer_name": _as_str(row.get("customer_name") or row.get("customerName")) or None, "stop_type": stop_type, "address": (row.get("address") if row.get("address") is not None else "") or "", "lng": lng, "lat": lat, "contact": (row.get("contact") if row.get("contact") is not None else "") or "", "notes": (row.get("notes") if row.get("notes") is not None else "") or "", } def build_stop_pref_upsert_items( *, load: Optional[Mapping[str, Any]], deliveries: Optional[Sequence[Mapping[str, Any]]], ) -> list[dict]: """抽出待 upsert 条目;同客户同 stop_type 后者覆盖(保持列表中每种键一条)。""" merged: dict[tuple[str, str], dict] = {} if load: item = _row_to_item(STOP_LOAD, load) if item: merged[(item["customer_id"], item["stop_type"])] = item for row in deliveries or []: item = _row_to_item(STOP_DELIVERY, row) if item: merged[(item["customer_id"], item["stop_type"])] = item return list(merged.values()) def get_customer_stop_pref( db: Session, tenant_no: str, customer_id: str, stop_type: str, ) -> Optional[PlCustomerStopPref]: cid = _as_str(customer_id) st = _as_str(stop_type) if not cid or st not in (STOP_LOAD, STOP_DELIVERY): return None return ( db.query(PlCustomerStopPref) .filter( PlCustomerStopPref.tenant_no == tenant_no, PlCustomerStopPref.customer_id == cid, PlCustomerStopPref.stop_type == st, PlCustomerStopPref.del_flag == 0, ) .first() ) def upsert_customer_stop_pref( db: Session, *, tenant_no: str, item: Mapping[str, Any], user_id: Optional[str], dept_id: Optional[str], ) -> None: customer_id = _as_str(item.get("customer_id")) stop_type = _as_str(item.get("stop_type")) if not customer_id or stop_type not in (STOP_LOAD, STOP_DELIVERY): return row = get_customer_stop_pref(db, tenant_no, customer_id, stop_type) lng = item.get("lng") lat = item.get("lat") if row is None: row = PlCustomerStopPref( id=generate_uuid(), tenant_no=tenant_no, customer_id=customer_id, create_by=user_id, create_dept_id=dept_id or "", ) db.add(row) row.customer_name = item.get("customer_name") row.stop_type = stop_type row.address = item.get("address") if item.get("address") is not None else "" row.lng = Decimal(str(lng)) if lng is not None and str(lng) != "" else None row.lat = Decimal(str(lat)) if lat is not None and str(lat) != "" else None row.contact = item.get("contact") if item.get("contact") is not None else "" row.notes = item.get("notes") if item.get("notes") is not None else "" row.update_by = user_id row.del_flag = 0 def upsert_prefs_from_plan_stops( db: Session, *, tenant_no: str, load: Optional[Mapping[str, Any]], deliveries: Optional[Sequence[Mapping[str, Any]]], user_id: Optional[str], dept_id: Optional[str], ) -> None: """供 plan 保存调用;异常由调用方用 savepoint 吞掉。""" for item in build_stop_pref_upsert_items(load=load, deliveries=deliveries): upsert_customer_stop_pref( db, tenant_no=tenant_no, item=item, user_id=user_id, dept_id=dept_id, ) def safe_upsert_prefs_from_plan_stops( db: Session, *, tenant_no: str, load: Optional[Any], deliveries: Optional[Sequence[Any]], user_id: Optional[str], dept_id: Optional[str], ) -> None: """savepoint 包裹;失败只记日志,不抛给计划主流程。""" load_map = None if load is not None: load_map = load.model_dump(by_alias=False) if hasattr(load, "model_dump") else dict(load) delivery_maps = [] for d in deliveries or []: delivery_maps.append( d.model_dump(by_alias=False) if hasattr(d, "model_dump") else dict(d) ) try: with db.begin_nested(): upsert_prefs_from_plan_stops( db, tenant_no=tenant_no, load=load_map, deliveries=delivery_maps, user_id=user_id, dept_id=dept_id, ) except Exception: logger.exception( "upsert customer stop pref failed tenant=%s (plan save continues)", tenant_no, )