170 lines
5.4 KiB
Python
170 lines
5.4 KiB
Python
"""运输计划打印数据聚合(一车多卸)"""
|
|
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
|