Files
shanxia2.0/backend/app/schemas/pl/transport.py
T
2026-08-27 14:23:18 +08:00

549 lines
30 KiB
Python

"""运输计划管理 - 运输 Schema"""
from datetime import date, datetime
from decimal import Decimal
from typing import Optional
from pydantic import BaseModel, Field
from app.schemas.pl.datetime_parse import OptionalPlanDateTime
from app.schemas.pl.io_order import BrandOption, BrandQtyItem
from app.schemas.pl.resource_snapshot import PlanResourceSnapshotMixin
# =============================================================================
# 运输 Schema
# =============================================================================
class TransportBase(BaseModel):
vehicle_id: Optional[str] = Field(default=None, alias="vehicleId", description="车辆ID")
license_plate: Optional[str] = Field(default=None, max_length=20, alias="licensePlate", description="车牌号")
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机ID")
driver_name: Optional[str] = Field(default=None, max_length=100, alias="driverName", description="司机姓名")
driver_phone: Optional[str] = Field(default=None, max_length=32, alias="driverPhone", description="司机电话")
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName", description="承运商名称")
transport_medium: Optional[str] = Field(default=None, max_length=100, alias="transportMedium", description="运输介质")
start_point: Optional[str] = Field(default=None, max_length=200, alias="startPoint", description="运输起点")
end_point: Optional[str] = Field(default=None, max_length=200, alias="endPoint", description="运输终点")
distance_km: Optional[Decimal] = Field(default=None, alias="distanceKm", description="运输公里数")
unit: Optional[str] = Field(default=None, max_length=20, description="计量单位")
transport_qty: Optional[Decimal] = Field(default=None, alias="transportQty", description="运输量")
freight_price: Optional[Decimal] = Field(default=None, alias="freightPrice", description="运输单价")
freight_amount: Decimal = Field(default=Decimal("0.00"), alias="freightAmount", description="运输费用")
freight_type: str = Field(default="by_weight", alias="freightType", description="运费计算方式")
has_invoice: int = Field(default=0, alias="hasInvoice", description="是否开票")
qr_expire_date: Optional[date] = Field(default=None, alias="qrExpireDate", description="二维码有效期")
transport_status: str = Field(default="draft", alias="transportStatus", description="运输状态")
approval_status: str = Field(default="none", alias="approvalStatus", description="审批状态")
remark: Optional[str] = None
class TransportCreate(TransportBase):
plan_ids: Optional[list[str]] = Field(default=None, alias="planIds", description="关联计划ID列表")
model_config = {"populate_by_name": True}
class TransportStopDibangOption(BaseModel):
stop_type: str = Field(..., alias="stopType", description="停靠段类型 load-装货 delivery-送货")
stop_id: str = Field(..., alias="stopId", description="停靠段ID")
generate_dibang: bool = Field(..., alias="generateDibang", description="是否生成地磅单")
model_config = {"populate_by_name": True}
class TransportArrange(BaseModel):
vehicle_id: str = Field(..., alias="vehicleId", description="车辆ID")
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机ID(可选)")
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
transport_medium: Optional[str] = Field(default=None, alias="transportMedium", description="运输介质")
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress", description="取货地址")
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng", description="取货经度")
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat", description="取货纬度")
pickup_time: OptionalPlanDateTime = Field(default=None, alias="pickupTime", description="取货时间")
pickup_contact: Optional[str] = Field(default=None, max_length=200, alias="pickupContact", description="取货联系方式")
pickup_notes: Optional[str] = Field(default=None, alias="pickupNotes", description="取货注意事项")
destination_address: Optional[str] = Field(default=None, alias="destinationAddress", description="送货地址")
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng", description="送货经度")
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat", description="送货纬度")
delivery_time: OptionalPlanDateTime = Field(default=None, alias="deliveryTime", description="送达时间")
delivery_contact: Optional[str] = Field(default=None, max_length=200, alias="deliveryContact", description="送货联系方式")
destination_notes: Optional[str] = Field(default=None, alias="destinationNotes", description="送货注意事项")
start_point: Optional[str] = Field(default=None, alias="startPoint", description="运输起点(兼容,等同取货地址)")
end_point: Optional[str] = Field(default=None, alias="endPoint", description="运输终点(兼容,等同送货地址)")
distance_km: Optional[Decimal] = Field(default=None, alias="distanceKm", description="运输公里数")
unit: Optional[str] = Field(default=None, description="计量单位")
freight_type: str = Field(default="by_weight", alias="freightType", description="运费计算方式 by_weight/by_trip")
freight_price: Optional[Decimal] = Field(default=None, alias="freightPrice", description="运输单价")
freight_amount: Optional[Decimal] = Field(default=None, alias="freightAmount", description="运费金额")
has_invoice: Optional[int] = Field(default=None, alias="hasInvoice", description="是否开票(已废弃,由运费结算维护)")
remark: Optional[str] = None
generate_dibang_plan: bool = Field(default=False, alias="generateDibangPlan", description="是否生成地磅单(兼容旧计划)")
stop_dibang_options: Optional[list[TransportStopDibangOption]] = Field(
default=None,
alias="stopDibangOptions",
description="按段地磅勾选(一车多卸)",
)
model_config = {"populate_by_name": True}
class TransportInventoryLinkRead(BaseModel):
plan_product_id: str = Field(alias="planProductId")
product_name: str = Field(alias="productName")
db_purchase_plan_id: str = Field(alias="dbPurchasePlanId")
db_inventory_id: str = Field(alias="dbInventoryId")
batch_no: str = Field(alias="batchNo")
plan_no: str = Field(alias="planNo")
stop_type: Optional[str] = Field(default=None, alias="stopType")
stop_id: Optional[str] = Field(default=None, alias="stopId")
model_config = {"populate_by_name": True}
class TransportSiteLinkRead(BaseModel):
plan_product_id: str = Field(alias="planProductId")
product_name: str = Field(alias="productName")
site_link_id: str = Field(alias="siteLinkId")
plan_no: str = Field(alias="planNo")
business_type: str = Field(alias="businessType")
site_access_token: Optional[str] = Field(default=None, alias="siteAccessToken")
site_url: Optional[str] = Field(default=None, alias="siteUrl")
site_feedback_remark: Optional[str] = Field(default=None, alias="siteFeedbackRemark")
site_feedback_images: list[str] = Field(default_factory=list, alias="siteFeedbackImages")
site_feedback_time: Optional[datetime] = Field(default=None, alias="siteFeedbackTime")
model_config = {"populate_by_name": True}
class TransportUpdate(BaseModel):
vehicle_id: Optional[str] = Field(default=None, alias="vehicleId")
license_plate: Optional[str] = Field(default=None, max_length=20, alias="licensePlate")
driver_id: Optional[str] = Field(default=None, alias="driverId")
driver_name: Optional[str] = Field(default=None, max_length=100, alias="driverName")
driver_phone: Optional[str] = Field(default=None, max_length=32, alias="driverPhone")
carrier_id: Optional[str] = Field(default=None, alias="carrierId")
carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName")
transport_medium: Optional[str] = Field(default=None, max_length=100, alias="transportMedium")
start_point: Optional[str] = Field(default=None, max_length=200, alias="startPoint")
end_point: Optional[str] = Field(default=None, max_length=200, alias="endPoint")
distance_km: Optional[Decimal] = Field(default=None, alias="distanceKm")
unit: Optional[str] = Field(default=None, max_length=20)
freight_price: Optional[Decimal] = Field(default=None, alias="freightPrice")
freight_amount: Optional[Decimal] = Field(default=None, alias="freightAmount")
freight_type: Optional[str] = Field(default=None, alias="freightType")
has_invoice: Optional[int] = Field(default=None, alias="hasInvoice")
qr_expire_date: Optional[date] = Field(default=None, alias="qrExpireDate")
transport_status: Optional[str] = Field(default=None, alias="transportStatus")
approval_status: Optional[str] = Field(default=None, alias="approvalStatus")
remark: Optional[str] = None
model_config = {"populate_by_name": True}
class TransportDriverProductFeedbackRead(BaseModel):
plan_product_id: str = Field(alias="planProductId")
product_name: str = Field(default="", alias="productName")
product_no: Optional[str] = Field(default=None, alias="productNo")
model: Optional[str] = None
unit: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
brand_mode: Optional[str] = Field(default=None, alias="brandMode")
brand_id: Optional[str] = Field(default=None, alias="brandId")
brand_name: Optional[str] = Field(default=None, alias="brandName")
actual_qty: Optional[Decimal] = Field(default=None, alias="actualQty")
brand_summary: Optional[str] = Field(default=None, alias="brandSummary")
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class TransportStopFeedbackRead(BaseModel):
stop_type: str = Field(alias="stopType")
stop_id: str = Field(alias="stopId")
label: str
customer_name: str = Field(alias="customerName")
business_type: Optional[str] = Field(default=None, alias="businessType")
quantity: Optional[Decimal] = None
attachment_urls: list[str] = Field(default_factory=list, alias="attachmentUrls")
products: list["TransportDriverStopProductFeedbackRead"] = Field(default_factory=list)
remark: Optional[str] = None
feedback_time: Optional[datetime] = Field(default=None, alias="feedbackTime")
model_config = {"populate_by_name": True}
class TransportDriverStopProductFeedbackRead(BaseModel):
stop_product_id: str = Field(alias="stopProductId")
plan_product_id: str = Field(default="", alias="planProductId")
product_name: str = Field(default="", alias="productName")
model: Optional[str] = None
unit: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
quantity: Optional[Decimal] = None
actual_qty: Optional[Decimal] = Field(default=None, alias="actualQty")
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class TransportRead(TransportBase, PlanResourceSnapshotMixin):
id: str
transport_no: str = Field(alias="transportNo")
qr_code: Optional[str] = Field(default=None, alias="qrCode")
feedback_time: Optional[datetime] = Field(default=None, alias="feedbackTime")
feedback_qty: Optional[Decimal] = Field(default=None, alias="feedbackQty")
feedback_images: Optional[str] = Field(default=None, alias="feedbackImages")
feedback_remark: Optional[str] = Field(default=None, alias="feedbackRemark")
driver_product_feedbacks: list[TransportDriverProductFeedbackRead] = Field(
default_factory=list, alias="driverProductFeedbacks"
)
driver_stop_feedbacks: list[TransportStopFeedbackRead] = Field(
default_factory=list, alias="driverStopFeedbacks"
)
site_stop_feedbacks: list[TransportStopFeedbackRead] = Field(
default_factory=list, alias="siteStopFeedbacks"
)
tenant_no: str = Field(alias="tenantNo")
order_num: Optional[int] = Field(default=None, alias="orderNum")
create_by: Optional[str] = Field(default=None, alias="createBy")
create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
create_time: datetime = Field(alias="createTime")
update_by: Optional[str] = Field(default=None, alias="updateBy")
update_time: datetime = Field(alias="updateTime")
del_flag: int = Field(default=0, alias="delFlag")
model_config = {"from_attributes": True, "populate_by_name": True}
class TransportIoOrderBrief(BaseModel):
kind: str
order_id: str = Field(alias="orderId")
order_no: str = Field(alias="orderNo")
site_access_token: Optional[str] = Field(default=None, alias="siteAccessToken")
site_url: Optional[str] = Field(default=None, alias="siteUrl")
status: Optional[str] = None
model_config = {"populate_by_name": True}
class TransportArrangeResult(TransportRead):
driver_access_token: Optional[str] = Field(default=None, alias="driverAccessToken")
driver_url: Optional[str] = Field(default=None, alias="driverUrl")
site_records: list[TransportSiteLinkRead] = Field(default_factory=list, alias="siteRecords")
inventory_records: list[TransportInventoryLinkRead] = Field(
default_factory=list, alias="inventoryRecords"
)
io_order: Optional[TransportIoOrderBrief] = Field(default=None, alias="ioOrder")
model_config = {"from_attributes": True, "populate_by_name": True}
class PlanArrangeInfoRead(BaseModel):
requires_site_link: bool = Field(default=False, alias="requiresSiteLink")
driver_url: Optional[str] = Field(default=None, alias="driverUrl")
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
site_records: list[TransportSiteLinkRead] = Field(default_factory=list, alias="siteRecords")
inventory_records: list[TransportInventoryLinkRead] = Field(
default_factory=list, alias="inventoryRecords"
)
io_order: Optional[TransportIoOrderBrief] = Field(default=None, alias="ioOrder")
model_config = {"populate_by_name": True}
class TransportWithPlans(TransportRead):
plan_ids: list[str] = Field(default_factory=list, alias="planIds")
# =============================================================================
# 司机扫码回传
# =============================================================================
class TransportFeedback(BaseModel):
transport_no: str = Field(..., alias="transportNo", description="运输编号")
verify_code: Optional[str] = Field(default=None, alias="verifyCode", description="验证码")
feedback_qty: Optional[Decimal] = Field(default=None, alias="feedbackQty", description="回传数量")
feedback_images: Optional[list[str]] = Field(default=None, alias="feedbackImages", description="回传图片URLs")
feedback_remark: Optional[str] = Field(default=None, alias="feedbackRemark", description="回传备注")
model_config = {"populate_by_name": True}
class PublicTransportProductFeedbackItem(BaseModel):
plan_product_id: str = Field(..., alias="planProductId")
actual_qty: Decimal = Field(..., alias="actualQty", ge=0)
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class PublicTransportFeedback(BaseModel):
feedback_images: Optional[list[str]] = Field(default=None, alias="feedbackImages")
feedback_remark: Optional[str] = Field(default=None, alias="feedbackRemark")
feedback_qty: Optional[Decimal] = Field(default=None, alias="feedbackQty", ge=0, description="回传数量")
products: Optional[list[PublicTransportProductFeedbackItem]] = None
model_config = {"populate_by_name": True}
class PublicTransportDriverStopProductFeedbackItem(BaseModel):
stop_product_id: str = Field(..., alias="stopProductId")
actual_qty: Decimal = Field(..., alias="actualQty", ge=0)
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class PublicTransportDriverStopFeedback(BaseModel):
stop_type: str = Field(..., alias="stopType")
stop_id: str = Field(..., alias="stopId")
attachment_urls: list[str] = Field(default_factory=list, alias="attachmentUrls")
products: Optional[list[PublicTransportDriverStopProductFeedbackItem]] = None
remark: Optional[str] = None
model_config = {"populate_by_name": True}
class PublicTransportStopProductRead(BaseModel):
stop_product_id: str = Field(alias="stopProductId")
plan_product_id: str = Field(default="", alias="planProductId")
product_name: str = Field(alias="productName")
alias_name: Optional[str] = Field(default=None, alias="aliasName")
model: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
unit: Optional[str] = None
quantity: Optional[Decimal] = None
remark: Optional[str] = Field(default=None, alias="remark")
brand_mode: Optional[str] = Field(default=None, alias="brandMode")
brand_id: Optional[str] = Field(default=None, alias="brandId")
brand_name: Optional[str] = Field(default=None, alias="brandName")
brand_options: Optional[list[BrandOption]] = Field(default=None, alias="brandOptions")
actual_qty: Optional[Decimal] = Field(default=None, alias="actualQty")
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class PublicTransportSiteStopFeedback(BaseModel):
stop_type: str = Field(..., alias="stopType")
stop_id: str = Field(..., alias="stopId")
quantity: Optional[Decimal] = Field(default=None, ge=0)
attachment_urls: Optional[list[str]] = Field(default=None, alias="attachmentUrls")
remark: Optional[str] = None
model_config = {"populate_by_name": True}
class PublicTransportStopRead(BaseModel):
stop_type: str = Field(alias="stopType")
stop_id: str = Field(alias="stopId")
seq: int = 0
label: str
customer_name: str = Field(alias="customerName")
address: Optional[str] = None
contact: Optional[str] = None
notes: Optional[str] = None
lng: Optional[Decimal] = None
lat: Optional[Decimal] = None
business_type: Optional[str] = Field(default=None, alias="businessType")
plan_qty: Optional[Decimal] = Field(default=None, alias="planQty")
quantity: Optional[Decimal] = None
show_product_qty: bool = Field(default=False, alias="showProductQty")
products: list[PublicTransportStopProductRead] = Field(default_factory=list)
attachment_urls: list[str] = Field(default_factory=list, alias="attachmentUrls")
remark: Optional[str] = None
feedback_time: Optional[datetime] = Field(default=None, alias="feedbackTime")
model_config = {"populate_by_name": True}
class PublicTransportPlanProductRead(BaseModel):
plan_product_id: str = Field(default="", alias="planProductId")
product_name: str = Field(alias="productName")
alias_name: Optional[str] = Field(default=None, alias="aliasName", description="产品别名")
model: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
unit: Optional[str] = None
quantity: Optional[Decimal] = None
remark: Optional[str] = Field(default=None, alias="remark", description="运输计划产品备注")
brand_mode: Optional[str] = Field(default=None, alias="brandMode")
brand_id: Optional[str] = Field(default=None, alias="brandId")
brand_name: Optional[str] = Field(default=None, alias="brandName")
brand_options: Optional[list[BrandOption]] = Field(default=None, alias="brandOptions")
actual_qty: Optional[Decimal] = Field(default=None, alias="actualQty")
brand_qtys: Optional[list[BrandQtyItem]] = Field(default=None, alias="brandQtys")
model_config = {"populate_by_name": True}
class PublicTransportPersonnelRead(BaseModel):
personnel_name: str = Field(alias="personnelName")
phone_no: Optional[str] = Field(default=None, alias="phoneNo")
role: Optional[str] = None
model_config = {"populate_by_name": True}
class PublicTransportPageRead(BaseModel):
plan_no: str = Field(alias="planNo")
plan_date: Optional[str] = Field(default=None, alias="planDate")
plan_type: str = Field(alias="planType")
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress")
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng")
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat")
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime")
pickup_contact: Optional[str] = Field(default=None, alias="pickupContact")
pickup_notes: Optional[str] = Field(default=None, alias="pickupNotes")
destination_name: Optional[str] = Field(default=None, alias="destinationName")
destination_address: Optional[str] = Field(default=None, alias="destinationAddress")
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng")
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat")
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime")
delivery_contact: Optional[str] = Field(default=None, alias="deliveryContact")
destination_notes: Optional[str] = Field(default=None, alias="destinationNotes")
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
driver_name: Optional[str] = Field(default=None, alias="driverName")
driver_phone: Optional[str] = Field(default=None, alias="driverPhone")
other_personnel: list[PublicTransportPersonnelRead] = Field(
default_factory=list, alias="otherPersonnel"
)
products: list[PublicTransportPlanProductRead] = Field(default_factory=list)
stops: list[PublicTransportStopRead] = Field(default_factory=list)
feedback_remark: Optional[str] = Field(default=None, alias="feedbackRemark")
feedback_images: list[str] = Field(default_factory=list, alias="feedbackImages")
feedback_qty: Optional[Decimal] = Field(default=None, alias="feedbackQty", description="回传数量")
feedback_time: Optional[datetime] = Field(default=None, alias="feedbackTime")
plan_voided: bool = Field(default=False, alias="planVoided")
plan_archived: bool = Field(default=False, alias="planArchived")
model_config = {"populate_by_name": True}
class PublicTransportHistoryProductRead(BaseModel):
product_name: str = Field(alias="productName")
model: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
quantity: Optional[Decimal] = None
unit: Optional[str] = None
model_config = {"populate_by_name": True}
class PublicTransportHistoryStopProductRead(BaseModel):
product_name: str = Field(alias="productName")
model: Optional[str] = None
package_spec: Optional[str] = Field(default=None, alias="packageSpec")
quantity: Optional[Decimal] = None
unit: Optional[str] = None
model_config = {"populate_by_name": True}
class PublicTransportHistoryStopRead(BaseModel):
stop_type: str = Field(alias="stopType")
seq: int = 0
label: str
customer_name: str = Field(alias="customerName")
address: Optional[str] = None
stop_time: Optional[datetime] = Field(default=None, alias="stopTime")
products: list[PublicTransportHistoryStopProductRead] = Field(default_factory=list)
model_config = {"populate_by_name": True}
class PublicTransportHistoryRecordRead(BaseModel):
plan_id: str = Field(alias="planId")
plan_no: str = Field(alias="planNo")
transport_id: str = Field(alias="transportId")
driver_access_token: Optional[str] = Field(default=None, alias="driverAccessToken")
driver_url: Optional[str] = Field(default=None, alias="driverUrl")
site_access_token: Optional[str] = Field(default=None, alias="siteAccessToken")
site_url: Optional[str] = Field(default=None, alias="siteUrl")
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime")
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime")
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress")
destination_address: Optional[str] = Field(default=None, alias="destinationAddress")
products: list[PublicTransportHistoryProductRead] = Field(default_factory=list)
stops: list[PublicTransportHistoryStopRead] = Field(default_factory=list)
plan_voided: bool = Field(default=False, alias="planVoided")
plan_archived: bool = Field(default=False, alias="planArchived")
status_label: str = Field(default="进行中", alias="statusLabel")
model_config = {"populate_by_name": True}
class PublicTransportHistoryPageRead(BaseModel):
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
date: str
keyword: Optional[str] = None
records: list[PublicTransportHistoryRecordRead] = Field(default_factory=list)
model_config = {"populate_by_name": True}
class PublicTransportMultiStopSitePageRead(BaseModel):
plan_no: str = Field(alias="planNo")
plan_date: Optional[str] = Field(default=None, alias="planDate")
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
vehicle_no: Optional[str] = Field(default=None, alias="vehicleNo")
plan_voided: bool = Field(default=False, alias="planVoided")
plan_archived: bool = Field(default=False, alias="planArchived")
stops: list[PublicTransportStopRead] = Field(default_factory=list)
model_config = {"populate_by_name": True}
class PublicTransportSitePageRead(BaseModel):
business_type: str = Field(alias="businessType")
date: Optional[str] = None
partner_company: str = Field(alias="partnerCompany")
vehicle_no: str = Field(alias="vehicleNo")
plan_no: Optional[str] = Field(default=None, alias="planNo")
remark: Optional[str] = None
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress")
pickup_lng: Optional[Decimal] = Field(default=None, alias="pickupLng")
pickup_lat: Optional[Decimal] = Field(default=None, alias="pickupLat")
pickup_time: Optional[datetime] = Field(default=None, alias="pickupTime")
pickup_contact: Optional[str] = Field(default=None, alias="pickupContact")
pickup_notes: Optional[str] = Field(default=None, alias="pickupNotes")
destination_name: Optional[str] = Field(default=None, alias="destinationName")
destination_address: Optional[str] = Field(default=None, alias="destinationAddress")
destination_lng: Optional[Decimal] = Field(default=None, alias="destinationLng")
destination_lat: Optional[Decimal] = Field(default=None, alias="destinationLat")
delivery_time: Optional[datetime] = Field(default=None, alias="deliveryTime")
delivery_contact: Optional[str] = Field(default=None, alias="deliveryContact")
destination_notes: Optional[str] = Field(default=None, alias="destinationNotes")
products: list[PublicTransportPlanProductRead] = Field(default_factory=list)
site_feedback_remark: Optional[str] = Field(default=None, alias="siteFeedbackRemark")
site_feedback_images: list[str] = Field(default_factory=list, alias="siteFeedbackImages")
site_feedback_time: Optional[datetime] = Field(default=None, alias="siteFeedbackTime")
voided: bool = False
archived: bool = False
model_config = {"populate_by_name": True}
# =============================================================================
# 审批
# =============================================================================
class ApprovalSubmit(BaseModel):
approver_ids: list[str] = Field(..., alias="approverIds", description="审批人ID列表(按顺序)")
model_config = {"populate_by_name": True}
class ApprovalRequest(BaseModel):
action: str = Field(..., description="审批动作 approve/reject")
comment: Optional[str] = Field(default=None, description="审批意见")
model_config = {"populate_by_name": True}
class ApprovalRecordRead(BaseModel):
id: str
transport_id: str = Field(alias="transportId")
approver_id: str = Field(alias="approverId")
approver_name: str = Field(alias="approverName")
approval_order: int = Field(alias="approvalOrder")
status: str
comment: Optional[str] = None
approval_time: Optional[datetime] = Field(default=None, alias="approvalTime")
tenant_no: str = Field(alias="tenantNo")
create_time: datetime = Field(alias="createTime")
model_config = {"from_attributes": True, "populate_by_name": True}
# =============================================================================
# 列表查询
# =============================================================================
class TransportListQuery(BaseModel):
q: Optional[str] = Field(default=None, description="模糊搜索")
transport_no: Optional[str] = Field(default=None, alias="transportNo", description="运输编号")
transport_status: Optional[str] = Field(default=None, alias="transportStatus", description="运输状态")
approval_status: Optional[str] = Field(default=None, alias="approvalStatus", description="审批状态")
vehicle_id: Optional[str] = Field(default=None, alias="vehicleId", description="车辆ID")
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机ID")
page: int = Field(default=1, ge=1)
page_size: int = Field(default=20, ge=1, le=500, alias="pageSize")
model_config = {"populate_by_name": True}
class TransportListResponse(BaseModel):
total: int
list: list[TransportRead]