166 lines
5.6 KiB
Python
166 lines
5.6 KiB
Python
"""运输计划列表:批量计算「复制给司机 / 现场 / 磅单二维码」按钮显示标志。
|
|
|
|
语义与查看弹窗 / usePlPlanArrangeActions / build_site_records_for_arranged_plan 对齐:
|
|
- 已安排且未取消,且存在司机链接或现场记录或地磅出入库关联时,显示「复制给司机」
|
|
- 有现场链接记录(含运输单按段 site_access_token、出入库单现场令牌回退)时显示「复制给现场」
|
|
- 有勾选生成地磅单的出入库关联(db_purchase_plan_id 非空)时显示「复制磅单二维码」
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Iterable
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.pl_models import (
|
|
PlInboundOrder,
|
|
PlOutboundOrder,
|
|
PlPlan,
|
|
PlTransport,
|
|
PlTransportDbLink,
|
|
PlTransportSiteLink,
|
|
)
|
|
from app.services.pl_dibang_bridge_service import plan_has_load_stop
|
|
from app.services.pl_internal_customer import plan_requires_site_link
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PlanArrangeCopyFlags:
|
|
can_copy_to_driver: bool = False
|
|
can_copy_to_site: bool = False
|
|
can_copy_pound_qrcode: bool = False
|
|
|
|
|
|
def load_plan_arrange_copy_flags(
|
|
db: Session,
|
|
tenant_no: str,
|
|
plans: Iterable[PlPlan],
|
|
) -> dict[str, PlanArrangeCopyFlags]:
|
|
plan_list = list(plans)
|
|
empty = PlanArrangeCopyFlags()
|
|
result: dict[str, PlanArrangeCopyFlags] = {plan.id: empty for plan in plan_list}
|
|
|
|
candidates = [
|
|
plan
|
|
for plan in plan_list
|
|
if plan.transport_status == "arranged"
|
|
and plan.follow_status != "cancelled"
|
|
and (plan.transport_id or "").strip()
|
|
]
|
|
if not candidates:
|
|
return result
|
|
|
|
plan_ids = [plan.id for plan in candidates]
|
|
transport_ids = list({plan.transport_id for plan in candidates if plan.transport_id})
|
|
|
|
transports = (
|
|
db.query(PlTransport)
|
|
.filter(
|
|
PlTransport.id.in_(transport_ids),
|
|
PlTransport.tenant_no == tenant_no,
|
|
PlTransport.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
transport_by_id = {
|
|
row.id: row for row in transports if row.transport_status != "cancelled"
|
|
}
|
|
|
|
site_plan_ids: set[str] = {
|
|
plan_id
|
|
for (plan_id,) in db.query(PlTransportSiteLink.plan_id)
|
|
.filter(
|
|
PlTransportSiteLink.tenant_no == tenant_no,
|
|
PlTransportSiteLink.plan_id.in_(plan_ids),
|
|
PlTransportSiteLink.del_flag == 0,
|
|
)
|
|
.distinct()
|
|
.all()
|
|
}
|
|
|
|
inventory_plan_ids: set[str] = set()
|
|
dibang_plan_ids: set[str] = set()
|
|
for plan_id, db_purchase_plan_id in (
|
|
db.query(PlTransportDbLink.plan_id, PlTransportDbLink.db_purchase_plan_id)
|
|
.filter(
|
|
PlTransportDbLink.tenant_no == tenant_no,
|
|
PlTransportDbLink.plan_id.in_(plan_ids),
|
|
PlTransportDbLink.del_flag == 0,
|
|
)
|
|
.all()
|
|
):
|
|
inventory_plan_ids.add(plan_id)
|
|
if (db_purchase_plan_id or "").strip():
|
|
dibang_plan_ids.add(plan_id)
|
|
|
|
# 与 get_plan_arrange_info 一致:无 site_link 时,用未作废出入库单的现场令牌回退
|
|
inbound_ids = [
|
|
plan.id
|
|
for plan in candidates
|
|
if plan.plan_type == "inbound" and plan.id not in site_plan_ids
|
|
]
|
|
if inbound_ids:
|
|
for plan_id, site_access_token, status in (
|
|
db.query(PlInboundOrder.plan_id, PlInboundOrder.site_access_token, PlInboundOrder.status)
|
|
.filter(
|
|
PlInboundOrder.tenant_no == tenant_no,
|
|
PlInboundOrder.plan_id.in_(inbound_ids),
|
|
PlInboundOrder.del_flag == 0,
|
|
)
|
|
.all()
|
|
):
|
|
if status != "voided" and (site_access_token or "").strip():
|
|
site_plan_ids.add(plan_id)
|
|
|
|
outbound_ids = [
|
|
plan.id
|
|
for plan in candidates
|
|
if plan.plan_type == "outbound" and plan.id not in site_plan_ids
|
|
]
|
|
if outbound_ids:
|
|
for plan_id, site_access_token, status in (
|
|
db.query(
|
|
PlOutboundOrder.plan_id, PlOutboundOrder.site_access_token, PlOutboundOrder.status
|
|
)
|
|
.filter(
|
|
PlOutboundOrder.tenant_no == tenant_no,
|
|
PlOutboundOrder.plan_id.in_(outbound_ids),
|
|
PlOutboundOrder.del_flag == 0,
|
|
)
|
|
.all()
|
|
):
|
|
if status != "voided" and (site_access_token or "").strip():
|
|
site_plan_ids.add(plan_id)
|
|
|
|
# 与 build_site_records_for_arranged_plan 一致:无 site_link 行时,装货段 + 运输单 site_access_token
|
|
for plan in candidates:
|
|
if plan.id in site_plan_ids:
|
|
continue
|
|
if not plan_requires_site_link(db, tenant_no=tenant_no, plan=plan):
|
|
continue
|
|
transport = transport_by_id.get(plan.transport_id or "")
|
|
if not transport:
|
|
continue
|
|
if not plan_has_load_stop(db, tenant_no, plan.id):
|
|
continue
|
|
if (transport.site_access_token or "").strip():
|
|
site_plan_ids.add(plan.id)
|
|
|
|
for plan in candidates:
|
|
transport = transport_by_id.get(plan.transport_id or "")
|
|
if not transport:
|
|
continue
|
|
has_driver = bool((transport.driver_access_token or "").strip())
|
|
has_site = plan.id in site_plan_ids
|
|
has_inventory = plan.id in inventory_plan_ids
|
|
has_dibang = plan.id in dibang_plan_ids
|
|
if not (has_driver or has_site or has_inventory):
|
|
continue
|
|
result[plan.id] = PlanArrangeCopyFlags(
|
|
can_copy_to_driver=True,
|
|
can_copy_to_site=has_site,
|
|
can_copy_pound_qrcode=has_dibang,
|
|
)
|
|
|
|
return result
|