"""运输计划打印数据聚合(一车多卸)""" from __future__ import annotations from decimal import Decimal from typing import Optional from sqlalchemy.orm import Session from app.schemas.pl.plan import ( DeliveryPrintCustomerOption, DeliverySlipPrintRead, PlanDeliveryRead, PlanStopProductRead, ) from app.services.pl_plan_stop_service import load_plan_stops_grouped def _customer_key(customer_id: Optional[str], customer_name: Optional[str]) -> str: cid = (customer_id or "").strip() if cid: return f"id:{cid}" name = (customer_name or "").strip() if name: return f"name:{name}" return "" def _resolve_customer_id(customer_id: Optional[str], customer_name: Optional[str]) -> str: cid = (customer_id or "").strip() if cid: return cid return (customer_name or "").strip() def list_delivery_print_customers( deliveries: list[PlanDeliveryRead], ) -> list[DeliveryPrintCustomerOption]: grouped: dict[str, dict] = {} for delivery in deliveries: key = _customer_key(delivery.customer_id, delivery.customer_name) if not key: continue if key not in grouped: grouped[key] = { "customer_id": _resolve_customer_id(delivery.customer_id, delivery.customer_name), "customer_name": (delivery.customer_name or "").strip() or "—", "delivery_count": 0, "min_seq": delivery.seq or 9999, } grouped[key]["delivery_count"] += 1 grouped[key]["min_seq"] = min(grouped[key]["min_seq"], delivery.seq or 9999) rows = sorted(grouped.values(), key=lambda item: (item["min_seq"], item["customer_name"])) return [ DeliveryPrintCustomerOption( customer_id=item["customer_id"], customer_name=item["customer_name"], delivery_count=item["delivery_count"], ) for item in rows ] def _delivery_matches_customer( delivery: PlanDeliveryRead, customer_id: str, ) -> bool: target = (customer_id or "").strip() if not target: return False delivery_id = (delivery.customer_id or "").strip() if delivery_id and delivery_id == target: return True delivery_name = (delivery.customer_name or "").strip() return not delivery_id and delivery_name == target def _merge_stop_products(products: list[PlanStopProductRead]) -> list[PlanStopProductRead]: merged: dict[str, PlanStopProductRead] = {} order: list[str] = [] for product in products: key = (product.product_id or "").strip() or product.id if key not in merged: merged[key] = product.model_copy(deep=True) order.append(key) continue existing = merged[key] for field in ("plan_qty", "outbound_qty", "inbound_qty"): left = getattr(existing, field, None) right = getattr(product, field, None) if left is None and right is None: continue total = (left or Decimal("0")) + (right or Decimal("0")) setattr(existing, field, total) if not (existing.remark or "").strip() and (product.remark or "").strip(): existing.remark = product.remark return [merged[key] for key in order] def build_delivery_slip_print( db: Session, *, tenant_no: str, plan_id: str, plan_no: str, plan_date: Optional[str], customer_id: str, ) -> DeliverySlipPrintRead: stops = load_plan_stops_grouped(db, tenant_no, [plan_id]).get(plan_id) if not stops: raise ValueError("计划未配置送货段") _load, deliveries = stops if not deliveries: raise ValueError("计划未配置送货段") matched = [item for item in deliveries if _delivery_matches_customer(item, customer_id)] if not matched: raise ValueError("未找到匹配的送货客户") matched.sort(key=lambda item: (item.seq or 9999, item.create_time)) primary = matched[0] products: list[PlanStopProductRead] = [] for delivery in matched: products.extend(delivery.products or []) merged_products = _merge_stop_products(products) return DeliverySlipPrintRead( plan_id=plan_id, plan_no=plan_no or "", plan_date=plan_date, customer_id=_resolve_customer_id(primary.customer_id, primary.customer_name), receiving_unit=(primary.customer_name or "").strip(), receive_contact=(primary.contact or "").strip(), delivery_address=(primary.address or "").strip() or None, products=merged_products, ) def get_delivery_slip_print_context( db: Session, *, tenant_no: str, plan_id: str, plan_no: str, plan_date: Optional[str], customer_id: Optional[str] = None, ) -> DeliverySlipPrintRead: stops = load_plan_stops_grouped(db, tenant_no, [plan_id]).get(plan_id) deliveries = stops[1] if stops else [] customers = list_delivery_print_customers(deliveries) if not customer_id: return DeliverySlipPrintRead( plan_id=plan_id, plan_no=plan_no or "", plan_date=plan_date, customers=customers, ) detail = build_delivery_slip_print( db, tenant_no=tenant_no, plan_id=plan_id, plan_no=plan_no, plan_date=plan_date, customer_id=customer_id, ) detail.customers = customers return detail