234 lines
7.1 KiB
Python
234 lines
7.1 KiB
Python
"""运输计划列表:聚合司机/现场/出入库现场等单据附件。"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Optional
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.pl_models import (
|
|
PlInboundOrder,
|
|
PlOutboundOrder,
|
|
PlPlan,
|
|
PlPlanDelivery,
|
|
PlTransport,
|
|
PlTransportDriverStopFeedback,
|
|
PlTransportSiteLink,
|
|
PlTransportSiteStopFeedback,
|
|
)
|
|
|
|
|
|
def _parse_image_urls(raw: Optional[str]) -> list[str]:
|
|
if not raw:
|
|
return []
|
|
text = str(raw).strip()
|
|
if not text:
|
|
return []
|
|
try:
|
|
parsed = json.loads(text)
|
|
if isinstance(parsed, list):
|
|
return [str(item).strip() for item in parsed if str(item).strip()]
|
|
except json.JSONDecodeError:
|
|
pass
|
|
return [item.strip() for item in text.split(",") if item.strip()]
|
|
|
|
|
|
def _short_label(text: str, max_len: int = 8) -> str:
|
|
value = (text or "").strip()
|
|
if not value:
|
|
return "现场"
|
|
if len(value) <= max_len:
|
|
return value
|
|
return f"{value[: max_len - 1]}…"
|
|
|
|
|
|
def _stop_source_label(
|
|
stop_type: str,
|
|
stop_id: str,
|
|
source: str,
|
|
delivery_seq_by_id: dict[str, int],
|
|
) -> str:
|
|
normalized = (stop_type or "").strip()
|
|
if normalized == "load":
|
|
return f"装货·{source}"
|
|
seq = delivery_seq_by_id.get((stop_id or "").strip())
|
|
if seq:
|
|
return f"送货#{seq}·{source}"
|
|
return f"送货·{source}"
|
|
|
|
|
|
def _stop_sort_key(
|
|
stop_type: str,
|
|
stop_id: str,
|
|
source: str,
|
|
delivery_seq_by_id: dict[str, int],
|
|
) -> tuple[int, int, int, str]:
|
|
normalized = (stop_type or "").strip()
|
|
stop_rank = 0 if normalized == "load" else 1
|
|
seq = delivery_seq_by_id.get((stop_id or "").strip(), 999) if normalized != "load" else 0
|
|
source_rank = 0 if source == "司机" else 1
|
|
return (stop_rank, seq, source_rank, stop_id or "")
|
|
|
|
|
|
def load_plan_document_attachments_by_plan_ids(
|
|
db: Session,
|
|
*,
|
|
tenant_no: str,
|
|
plan_ids: list[str],
|
|
) -> dict[str, list[dict[str, str]]]:
|
|
if not plan_ids:
|
|
return {}
|
|
|
|
result: dict[str, list[dict[str, str]]] = {plan_id: [] for plan_id in plan_ids}
|
|
seen: dict[str, set[str]] = {plan_id: set() for plan_id in plan_ids}
|
|
pending: dict[str, list[tuple[tuple[int, int, int, str], str, list[str]]]] = {
|
|
plan_id: [] for plan_id in plan_ids
|
|
}
|
|
|
|
def add(plan_id: str, label: str, urls: list[str], sort_key: tuple[int, int, int, str]) -> None:
|
|
if plan_id not in pending:
|
|
return
|
|
cleaned = [url for url in urls if url]
|
|
if not cleaned:
|
|
return
|
|
pending[plan_id].append((sort_key, label, cleaned))
|
|
|
|
def flush(plan_id: str) -> None:
|
|
bucket = result.get(plan_id)
|
|
if bucket is None:
|
|
return
|
|
for _, label, urls in sorted(pending.get(plan_id, []), key=lambda item: item[0]):
|
|
for url in urls:
|
|
if url in seen[plan_id]:
|
|
continue
|
|
seen[plan_id].add(url)
|
|
bucket.append({"url": url, "label": label})
|
|
|
|
delivery_seq_by_id: dict[str, int] = {}
|
|
delivery_rows = (
|
|
db.query(PlPlanDelivery.id, PlPlanDelivery.plan_id, PlPlanDelivery.seq)
|
|
.filter(
|
|
PlPlanDelivery.plan_id.in_(plan_ids),
|
|
PlPlanDelivery.tenant_no == tenant_no,
|
|
PlPlanDelivery.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for delivery_id, _plan_id, seq in delivery_rows:
|
|
delivery_seq_by_id[str(delivery_id)] = int(seq or 0)
|
|
|
|
plan_rows = (
|
|
db.query(PlPlan.id, PlPlan.transport_id)
|
|
.filter(
|
|
PlPlan.id.in_(plan_ids),
|
|
PlPlan.tenant_no == tenant_no,
|
|
PlPlan.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
transport_ids = [transport_id for _, transport_id in plan_rows if transport_id]
|
|
plan_by_transport = {
|
|
transport_id: plan_id for plan_id, transport_id in plan_rows if transport_id
|
|
}
|
|
|
|
if transport_ids:
|
|
transports = (
|
|
db.query(PlTransport)
|
|
.filter(
|
|
PlTransport.id.in_(transport_ids),
|
|
PlTransport.tenant_no == tenant_no,
|
|
PlTransport.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for transport in transports:
|
|
plan_id = plan_by_transport.get(transport.id)
|
|
if not plan_id:
|
|
continue
|
|
add(
|
|
plan_id,
|
|
"司机",
|
|
_parse_image_urls(transport.feedback_images),
|
|
(0, 0, 0, "transport"),
|
|
)
|
|
|
|
driver_rows = (
|
|
db.query(PlTransportDriverStopFeedback)
|
|
.filter(
|
|
PlTransportDriverStopFeedback.plan_id.in_(plan_ids),
|
|
PlTransportDriverStopFeedback.tenant_no == tenant_no,
|
|
PlTransportDriverStopFeedback.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for row in driver_rows:
|
|
label = _stop_source_label(row.stop_type, row.stop_id, "司机", delivery_seq_by_id)
|
|
add(
|
|
row.plan_id,
|
|
label,
|
|
_parse_image_urls(row.attachment_urls),
|
|
_stop_sort_key(row.stop_type, row.stop_id, "司机", delivery_seq_by_id),
|
|
)
|
|
|
|
site_stop_rows = (
|
|
db.query(PlTransportSiteStopFeedback)
|
|
.filter(
|
|
PlTransportSiteStopFeedback.plan_id.in_(plan_ids),
|
|
PlTransportSiteStopFeedback.tenant_no == tenant_no,
|
|
PlTransportSiteStopFeedback.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for row in site_stop_rows:
|
|
label = _stop_source_label(row.stop_type, row.stop_id, "现场", delivery_seq_by_id)
|
|
add(
|
|
row.plan_id,
|
|
label,
|
|
_parse_image_urls(row.attachment_urls),
|
|
_stop_sort_key(row.stop_type, row.stop_id, "现场", delivery_seq_by_id),
|
|
)
|
|
|
|
site_rows = (
|
|
db.query(PlTransportSiteLink)
|
|
.filter(
|
|
PlTransportSiteLink.plan_id.in_(plan_ids),
|
|
PlTransportSiteLink.tenant_no == tenant_no,
|
|
PlTransportSiteLink.del_flag == 0,
|
|
)
|
|
.order_by(PlTransportSiteLink.order_num, PlTransportSiteLink.create_time)
|
|
.all()
|
|
)
|
|
for row in site_rows:
|
|
product = (row.product or "").strip()
|
|
label = f"现场·{_short_label(product)}" if product else "现场"
|
|
add(row.plan_id, label, _parse_image_urls(row.site_feedback_images), (2, 0, 1, row.id or ""))
|
|
|
|
inbound_rows = (
|
|
db.query(PlInboundOrder)
|
|
.filter(
|
|
PlInboundOrder.plan_id.in_(plan_ids),
|
|
PlInboundOrder.tenant_no == tenant_no,
|
|
PlInboundOrder.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for row in inbound_rows:
|
|
add(row.plan_id, "入库现场", _parse_image_urls(row.site_feedback_images), (3, 0, 1, row.id or ""))
|
|
|
|
outbound_rows = (
|
|
db.query(PlOutboundOrder)
|
|
.filter(
|
|
PlOutboundOrder.plan_id.in_(plan_ids),
|
|
PlOutboundOrder.tenant_no == tenant_no,
|
|
PlOutboundOrder.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for row in outbound_rows:
|
|
add(row.plan_id, "出库现场", _parse_image_urls(row.site_feedback_images), (3, 0, 2, row.id or ""))
|
|
|
|
for plan_id in plan_ids:
|
|
flush(plan_id)
|
|
|
|
return result
|