2026-08-06 13:56:04 +08:00
|
|
|
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
|
|
|
|
|
|
|
|
|
const inboundBasePath = '/platform/v1/inbounds'
|
|
|
|
|
|
2026-08-10 12:05:20 +08:00
|
|
|
export interface OutboundGateItem { code: string; name: string; passed: boolean; actual?: string | null; requirement?: string | null }
|
|
|
|
|
export interface OutboundDispatchAggregate {
|
2026-08-10 15:36:53 +08:00
|
|
|
orderId: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
orderNo: string | null
|
2026-08-10 15:36:53 +08:00
|
|
|
inboundId: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
inboundCode: string | null
|
2026-08-10 15:36:53 +08:00
|
|
|
containerBookingId: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
containerNo: string | null
|
2026-08-10 15:36:53 +08:00
|
|
|
loadingPlanId: string | null
|
|
|
|
|
loadingPlanStatus: string | null
|
|
|
|
|
oceanBookingId: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
oceanBookingStatus: string | null
|
2026-08-10 15:36:53 +08:00
|
|
|
truckAppointmentId: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
truckAppointmentStatus: string | null
|
|
|
|
|
customsStatus?: string | null
|
|
|
|
|
sailingStatus?: string | null
|
|
|
|
|
gate: { passed: boolean; items: OutboundGateItem[] } | null
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 15:36:53 +08:00
|
|
|
export interface ContainerBooking {
|
|
|
|
|
id: string
|
|
|
|
|
containerNo: string | null
|
|
|
|
|
cabinetNo: string | null
|
|
|
|
|
warehouseId: string | null
|
|
|
|
|
warehouseName: string | null
|
|
|
|
|
destinationWarehouseCode: string | null
|
|
|
|
|
destinationWarehouseName: string | null
|
|
|
|
|
capacityCbm: number | null
|
|
|
|
|
maxWeightKg: number | null
|
|
|
|
|
version: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SmartLoadingCandidate {
|
|
|
|
|
accountId: string
|
|
|
|
|
inboundId: string
|
|
|
|
|
inboundDetailId: string
|
|
|
|
|
orderId: string
|
|
|
|
|
orderNo: string | null
|
|
|
|
|
inboundCode: string | null
|
|
|
|
|
warehouseId: string
|
|
|
|
|
destinationWarehouseId: string | null
|
|
|
|
|
destinationWarehouseCode: string | null
|
|
|
|
|
destinationWarehouseName: string | null
|
|
|
|
|
customerName: string | null
|
|
|
|
|
goodsName: string | null
|
|
|
|
|
availablePackageCount: number
|
|
|
|
|
unitWeightKg: number
|
|
|
|
|
unitVolumeCbm: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SmartLoadingPlanItem {
|
|
|
|
|
id: string
|
|
|
|
|
loadingPlanId: string
|
|
|
|
|
accountId: string
|
|
|
|
|
customerName: string | null
|
|
|
|
|
inboundId: string
|
|
|
|
|
inboundDetailId: string
|
|
|
|
|
orderId: string
|
|
|
|
|
orderNo: string | null
|
|
|
|
|
inboundCode: string | null
|
|
|
|
|
destinationWarehouseCode: string | null
|
|
|
|
|
destinationWarehouseName: string | null
|
|
|
|
|
skuCode: string | null
|
|
|
|
|
productNameZh: string | null
|
|
|
|
|
packageCount: number
|
|
|
|
|
grossWeightKg: number
|
|
|
|
|
volumeCbm: number
|
|
|
|
|
loadSequence: number | null
|
|
|
|
|
version: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SmartLoadingPlan {
|
|
|
|
|
id: string
|
|
|
|
|
planNo: string | null
|
|
|
|
|
status: string
|
|
|
|
|
bookingId: string
|
|
|
|
|
containerNo: string | null
|
|
|
|
|
voyageNo: string | null
|
|
|
|
|
version: number
|
|
|
|
|
items: SmartLoadingPlanItem[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateSmartLoadingPlanRequest { bookingId: string; remark?: string }
|
|
|
|
|
export interface AssignSmartLoadingItemRequest {
|
|
|
|
|
inboundId: string
|
|
|
|
|
inboundDetailId: string
|
|
|
|
|
packageCount: number
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
operationId: string
|
|
|
|
|
}
|
|
|
|
|
export interface RecommendSmartLoadingRequest { expectedVersion: number; operationId: string }
|
|
|
|
|
export interface AdjustSmartLoadingItemRequest {
|
|
|
|
|
packageCount: number
|
|
|
|
|
expectedPlanVersion: number
|
|
|
|
|
expectedItemVersion: number
|
|
|
|
|
operationId: string
|
|
|
|
|
}
|
|
|
|
|
export interface DeleteSmartLoadingItemRequest {
|
|
|
|
|
expectedPlanVersion: number
|
|
|
|
|
expectedItemVersion: number
|
|
|
|
|
operationId: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 13:56:04 +08:00
|
|
|
export type PreloadStatus = 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | string
|
2026-08-10 12:05:20 +08:00
|
|
|
/** 五类装柜取证。旧接口仍使用 PHOTO_STEP_* / SEAL 编码,页面统一映射为业务名称。 */
|
2026-08-06 13:56:04 +08:00
|
|
|
export type EvidenceType = 'PHOTO_STEP_1' | 'PHOTO_STEP_2' | 'PHOTO_STEP_3' | 'PHOTO_STEP_4' | 'SEAL'
|
|
|
|
|
|
|
|
|
|
export interface PreloadItem {
|
|
|
|
|
containerCode: string | null
|
|
|
|
|
cargoReference: string | null
|
|
|
|
|
packageCount: number | null
|
|
|
|
|
volumeCbm: number | null
|
|
|
|
|
remark: string | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface PreloadPlan {
|
2026-08-10 15:36:53 +08:00
|
|
|
id: string
|
|
|
|
|
inboundId: string
|
|
|
|
|
tenantId: string
|
2026-08-06 13:56:04 +08:00
|
|
|
status: PreloadStatus
|
|
|
|
|
version: number
|
|
|
|
|
items: PreloadItem[]
|
|
|
|
|
remark: string | null
|
|
|
|
|
createdAt: string | null
|
|
|
|
|
updatedAt: string | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SplitActionResponse {
|
|
|
|
|
mainStatus: string
|
|
|
|
|
preloadPlan: PreloadPlan | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface PickingTask {
|
|
|
|
|
id: number | string
|
|
|
|
|
inboundId: number | string
|
|
|
|
|
status: string
|
|
|
|
|
version: number
|
|
|
|
|
expectedCount: number | null
|
|
|
|
|
pickedCount: number | null
|
|
|
|
|
remark: string | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface Evidence {
|
|
|
|
|
id: number | string
|
|
|
|
|
inboundId: number | string
|
|
|
|
|
tenantId: number | string
|
|
|
|
|
evidenceType: EvidenceType
|
|
|
|
|
fileId: number | string | null
|
|
|
|
|
fileName: string | null
|
|
|
|
|
sealNo: string | null
|
|
|
|
|
remark: string | null
|
|
|
|
|
version: number
|
2026-08-10 12:05:20 +08:00
|
|
|
status: 'ACTIVE' | 'VOIDED' | string
|
2026-08-06 13:56:04 +08:00
|
|
|
createdAt: string | null
|
|
|
|
|
updatedAt: string | null
|
2026-08-10 12:05:20 +08:00
|
|
|
voidReason?: string | null
|
|
|
|
|
voidedBy?: number | string | null
|
|
|
|
|
voidedByName?: string | null
|
|
|
|
|
voidedAt?: string | null
|
2026-08-06 13:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface EvidenceList {
|
|
|
|
|
inboundId: number | string
|
|
|
|
|
evidences: Evidence[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LoadingActionResponse {
|
|
|
|
|
mainStatus: string
|
|
|
|
|
evidences: EvidenceList | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface PickingRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
expectedCount: number
|
|
|
|
|
pickedCount: number
|
|
|
|
|
remark?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SmartSplitRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
items: PreloadItem[]
|
|
|
|
|
remark?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ConfirmPreloadRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface UpsertEvidenceRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
evidenceType: EvidenceType
|
|
|
|
|
fileId?: number | string
|
|
|
|
|
fileName?: string
|
|
|
|
|
sealNo?: string
|
|
|
|
|
remark?: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 12:05:20 +08:00
|
|
|
export interface UploadEvidenceResult {
|
|
|
|
|
fileId: number | string
|
|
|
|
|
fileName: string | null
|
|
|
|
|
contentType?: string | null
|
|
|
|
|
sizeBytes?: number | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface VoidEvidenceRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
reason: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 13:56:04 +08:00
|
|
|
export interface CompleteLoadingRequest {
|
|
|
|
|
expectedVersion: number
|
|
|
|
|
remark?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function query(tenantId: string | number) {
|
|
|
|
|
return { tenantId: String(tenantId) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function idempotencyKey(operation: string, inboundId: string | number) {
|
|
|
|
|
return `admin-${operation}-${inboundId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 15:36:53 +08:00
|
|
|
function operationId(operation: string, resourceId: string | number) {
|
|
|
|
|
return `admin-${operation}-${resourceId}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nullableId(value: number | string | null | undefined) {
|
|
|
|
|
return value === null || value === undefined || value === '' ? null : String(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function numberValue(value: number | string | null | undefined, fallback = 0) {
|
|
|
|
|
const parsed = Number(value)
|
|
|
|
|
return Number.isFinite(parsed) ? parsed : fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeContainer(raw: Record<string, unknown>): ContainerBooking {
|
|
|
|
|
return {
|
|
|
|
|
id: String(raw.id),
|
|
|
|
|
containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
|
|
|
|
cabinetNo: raw.cabinetNo == null ? null : String(raw.cabinetNo),
|
|
|
|
|
warehouseId: nullableId(raw.warehouseId as number | string | null),
|
|
|
|
|
warehouseName: raw.warehouseName == null ? null : String(raw.warehouseName),
|
|
|
|
|
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
|
|
|
|
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
|
|
|
|
capacityCbm: raw.capacityCbm == null ? null : numberValue(raw.capacityCbm as number | string),
|
|
|
|
|
maxWeightKg: raw.maxWeightKg == null ? null : numberValue(raw.maxWeightKg as number | string),
|
|
|
|
|
version: numberValue(raw.version as number | string),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeCandidate(raw: Record<string, unknown>): SmartLoadingCandidate {
|
|
|
|
|
return {
|
|
|
|
|
accountId: String(raw.accountId), inboundId: String(raw.inboundId), inboundDetailId: String(raw.inboundDetailId),
|
|
|
|
|
orderId: String(raw.orderId), orderNo: raw.orderNo == null ? null : String(raw.orderNo),
|
|
|
|
|
inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode), warehouseId: String(raw.warehouseId),
|
|
|
|
|
destinationWarehouseId: nullableId(raw.destinationWarehouseId as number | string | null),
|
|
|
|
|
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
|
|
|
|
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
|
|
|
|
customerName: raw.customerName == null ? null : String(raw.customerName),
|
|
|
|
|
goodsName: raw.goodsName == null ? null : String(raw.goodsName),
|
|
|
|
|
availablePackageCount: numberValue(raw.availablePackageCount as number | string),
|
|
|
|
|
unitWeightKg: numberValue(raw.unitWeightKg as number | string), unitVolumeCbm: numberValue(raw.unitVolumeCbm as number | string),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeSmartLoadingItem(raw: Record<string, unknown>): SmartLoadingPlanItem {
|
|
|
|
|
return {
|
|
|
|
|
id: String(raw.id), loadingPlanId: String(raw.loadingPlanId), accountId: String(raw.accountId),
|
|
|
|
|
customerName: raw.customerName == null ? null : String(raw.customerName), inboundId: String(raw.inboundId),
|
|
|
|
|
inboundDetailId: String(raw.inboundDetailId), orderId: String(raw.orderId),
|
|
|
|
|
orderNo: raw.orderNo == null ? null : String(raw.orderNo), inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode),
|
|
|
|
|
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
|
|
|
|
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
|
|
|
|
skuCode: raw.skuCode == null ? null : String(raw.skuCode), productNameZh: raw.productNameZh == null ? null : String(raw.productNameZh),
|
|
|
|
|
packageCount: numberValue(raw.packageCount as number | string), grossWeightKg: numberValue(raw.grossWeightKg as number | string),
|
|
|
|
|
volumeCbm: numberValue(raw.volumeCbm as number | string), loadSequence: raw.loadSequence == null ? null : numberValue(raw.loadSequence as number | string),
|
|
|
|
|
version: numberValue(raw.version as number | string),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeSmartLoadingPlan(raw: Record<string, unknown>): SmartLoadingPlan {
|
|
|
|
|
return {
|
|
|
|
|
id: String(raw.id), planNo: raw.planNo == null ? null : String(raw.planNo), status: String(raw.status || ''),
|
|
|
|
|
bookingId: String(raw.bookingId), containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
|
|
|
|
voyageNo: raw.voyageNo == null ? null : String(raw.voyageNo), version: numberValue(raw.version as number | string),
|
|
|
|
|
items: Array.isArray(raw.items) ? raw.items.map((item) => normalizeSmartLoadingItem(item as Record<string, unknown>)) : [],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeOutboundDispatch(raw: Record<string, unknown>): OutboundDispatchAggregate {
|
|
|
|
|
return {
|
|
|
|
|
orderId: nullableId(raw.orderId as number | string | null),
|
|
|
|
|
orderNo: raw.orderNo == null ? null : String(raw.orderNo),
|
|
|
|
|
inboundId: nullableId(raw.inboundId as number | string | null),
|
|
|
|
|
inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode),
|
|
|
|
|
containerBookingId: nullableId(raw.containerBookingId as number | string | null),
|
|
|
|
|
containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
|
|
|
|
loadingPlanId: nullableId(raw.loadingPlanId as number | string | null),
|
|
|
|
|
loadingPlanStatus: raw.loadingPlanStatus == null ? null : String(raw.loadingPlanStatus),
|
|
|
|
|
oceanBookingId: nullableId(raw.oceanBookingId as number | string | null),
|
|
|
|
|
oceanBookingStatus: raw.oceanBookingStatus == null ? null : String(raw.oceanBookingStatus),
|
|
|
|
|
truckAppointmentId: nullableId(raw.truckAppointmentId as number | string | null),
|
|
|
|
|
truckAppointmentStatus: raw.truckAppointmentStatus == null ? null : String(raw.truckAppointmentStatus),
|
|
|
|
|
customsStatus: raw.customsStatus == null ? null : String(raw.customsStatus),
|
|
|
|
|
sailingStatus: raw.sailingStatus == null ? null : String(raw.sailingStatus),
|
|
|
|
|
gate: raw.gate == null ? null : raw.gate as OutboundDispatchAggregate['gate'],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizePreloadPlan(raw: Record<string, unknown>): PreloadPlan {
|
|
|
|
|
return {
|
|
|
|
|
id: String(raw.id), inboundId: String(raw.inboundId), tenantId: String(raw.tenantId),
|
|
|
|
|
status: String(raw.status || ''), version: numberValue(raw.version as number | string),
|
|
|
|
|
items: Array.isArray(raw.items) ? raw.items.map((item) => ({ ...(item as PreloadItem) })) : [],
|
|
|
|
|
remark: raw.remark == null ? null : String(raw.remark),
|
|
|
|
|
createdAt: raw.createdAt == null ? null : String(raw.createdAt),
|
|
|
|
|
updatedAt: raw.updatedAt == null ? null : String(raw.updatedAt),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeSplitAction(raw: Record<string, unknown>): SplitActionResponse {
|
|
|
|
|
return {
|
|
|
|
|
mainStatus: String(raw.mainStatus || ''),
|
|
|
|
|
preloadPlan: raw.preloadPlan == null ? null : normalizePreloadPlan(raw.preloadPlan as Record<string, unknown>),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const smartLoadingBasePath = '/platform/v1/first-leg/smart-loading'
|
|
|
|
|
|
|
|
|
|
export async function getContainerBooking(bookingId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`/platform/v1/containers/${bookingId}`,
|
|
|
|
|
)
|
|
|
|
|
return normalizeContainer(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function listSmartLoadingCandidates(warehouseId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>[]>, ApiEnvelope<Record<string, unknown>[]>>(
|
|
|
|
|
`${smartLoadingBasePath}/candidates`, { params: { warehouseId: String(warehouseId) } },
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response).map(normalizeCandidate)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getSmartLoadingPlan(planId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}`,
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getSmartLoadingPlanByBooking(bookingId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/by-booking/${bookingId}`,
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function createSmartLoadingPlan(bookingId: string | number, remark?: string) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans`, { bookingId: String(bookingId), remark: remark || undefined },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function assignSmartLoadingItem(planId: string | number, request: Omit<AssignSmartLoadingItemRequest, 'operationId'>) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}/assignments`,
|
|
|
|
|
{ ...request, operationId: operationId('smart-loading-assign', planId) },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function recommendSmartLoading(planId: string | number, expectedVersion: number) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}/recommend`,
|
|
|
|
|
{ expectedVersion, operationId: operationId('smart-loading-recommend', planId) },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function adjustSmartLoadingItem(
|
|
|
|
|
planId: string | number,
|
|
|
|
|
itemId: string | number,
|
|
|
|
|
request: Omit<AdjustSmartLoadingItemRequest, 'operationId'>,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.patch<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}/items/${itemId}`,
|
|
|
|
|
{ ...request, operationId: operationId('smart-loading-adjust', itemId) },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function deleteSmartLoadingItem(
|
|
|
|
|
planId: string | number,
|
|
|
|
|
itemId: string | number,
|
|
|
|
|
request: Omit<DeleteSmartLoadingItemRequest, 'operationId'>,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.delete<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}/items/${itemId}`,
|
|
|
|
|
{ data: { ...request, operationId: operationId('smart-loading-delete', itemId) } },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function pushSmartLoadingPreload(planId: string | number, expectedVersion: number) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
|
|
|
|
`${smartLoadingBasePath}/plans/${planId}/push-preload`, { expectedVersion },
|
|
|
|
|
{ headers: { 'Idempotency-Key': idempotencyKey('smart-loading-push', planId) } },
|
|
|
|
|
)
|
|
|
|
|
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 13:56:04 +08:00
|
|
|
function writeOptions(operation: string, inboundId: string | number, tenantId: string | number) {
|
|
|
|
|
return { params: query(tenantId), headers: { 'Idempotency-Key': idempotencyKey(operation, inboundId) } }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getPreload(tenantId: string | number, inboundId: string | number) {
|
2026-08-10 15:36:53 +08:00
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
2026-08-06 13:56:04 +08:00
|
|
|
`${inboundBasePath}/${inboundId}/preload`, { params: query(tenantId) },
|
|
|
|
|
)
|
2026-08-10 15:36:53 +08:00
|
|
|
return normalizePreloadPlan(unwrapApiResponse(response))
|
2026-08-06 13:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-08-10 12:05:20 +08:00
|
|
|
export async function getOutboundDispatch(tenantId: string | number, orderId?: string | number, inboundId?: string | number) {
|
2026-08-10 15:36:53 +08:00
|
|
|
const response = await http.get<ApiEnvelope<Record<string, unknown>[]>, ApiEnvelope<Record<string, unknown>[]>>(
|
2026-08-10 12:05:20 +08:00
|
|
|
'/platform/v1/first-leg/outbound-dispatches',
|
|
|
|
|
{ params: { tenantId: String(tenantId), ...(orderId ? { orderId: String(orderId) } : {}), ...(inboundId ? { inboundId: String(inboundId) } : {}) } },
|
|
|
|
|
)
|
|
|
|
|
const result = unwrapApiResponse(response)
|
2026-08-10 15:36:53 +08:00
|
|
|
return result?.[0] ? normalizeOutboundDispatch(result[0]) : null
|
2026-08-10 12:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-08-06 13:56:04 +08:00
|
|
|
export async function smartSplit(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: SmartSplitRequest,
|
|
|
|
|
) {
|
2026-08-10 15:36:53 +08:00
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
2026-08-06 13:56:04 +08:00
|
|
|
`${inboundBasePath}/${inboundId}/split/smart`, request, writeOptions('smart-split', inboundId, tenantId),
|
|
|
|
|
)
|
2026-08-10 15:36:53 +08:00
|
|
|
return normalizeSplitAction(unwrapApiResponse(response))
|
2026-08-06 13:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function confirmingPreload(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: ConfirmPreloadRequest,
|
|
|
|
|
) {
|
2026-08-10 15:36:53 +08:00
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
2026-08-06 13:56:04 +08:00
|
|
|
`${inboundBasePath}/${inboundId}/preload/confirming`, request,
|
|
|
|
|
writeOptions('preload-confirming', inboundId, tenantId),
|
|
|
|
|
)
|
2026-08-10 15:36:53 +08:00
|
|
|
return normalizeSplitAction(unwrapApiResponse(response))
|
2026-08-06 13:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function confirmPreload(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: ConfirmPreloadRequest,
|
|
|
|
|
) {
|
2026-08-10 15:36:53 +08:00
|
|
|
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
2026-08-06 13:56:04 +08:00
|
|
|
`${inboundBasePath}/${inboundId}/preload/confirm`, request,
|
|
|
|
|
writeOptions('preload-confirm', inboundId, tenantId),
|
|
|
|
|
)
|
2026-08-10 15:36:53 +08:00
|
|
|
return normalizeSplitAction(unwrapApiResponse(response))
|
2026-08-06 13:56:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getPicking(tenantId: string | number, inboundId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/picking`, { params: query(tenantId) },
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function startPicking(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: PickingRequest,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/picking/start`, request,
|
|
|
|
|
writeOptions('picking-start', inboundId, tenantId),
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function completePicking(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: PickingRequest,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/picking/complete`, request,
|
|
|
|
|
writeOptions('picking-complete', inboundId, tenantId),
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getEvidence(tenantId: string | number, inboundId: string | number) {
|
|
|
|
|
const response = await http.get<ApiEnvelope<EvidenceList>, ApiEnvelope<EvidenceList>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/evidence`, { params: query(tenantId) },
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function upsertEvidence(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: UpsertEvidenceRequest,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/evidence`, request,
|
|
|
|
|
writeOptions(`evidence-${request.evidenceType}`, inboundId, tenantId),
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 12:05:20 +08:00
|
|
|
/** 真实 File 服务上传;返回的 fileId 才能提交证据,不接受本地文件名冒充。 */
|
|
|
|
|
export async function uploadEvidenceFile(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
evidenceType: EvidenceType,
|
|
|
|
|
file: File,
|
|
|
|
|
) {
|
|
|
|
|
const body = new FormData()
|
|
|
|
|
body.append('evidenceType', evidenceType)
|
|
|
|
|
body.append('file', file)
|
|
|
|
|
const response = await http.post<ApiEnvelope<UploadEvidenceResult>, ApiEnvelope<UploadEvidenceResult>>(
|
|
|
|
|
`/platform/v1/files/loading-evidence/${tenantId}/${inboundId}`,
|
|
|
|
|
body,
|
|
|
|
|
{ headers: { 'Content-Type': 'multipart/form-data' } },
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-11 11:15:44 +08:00
|
|
|
export function downloadEvidenceFile(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
fileId: string | number,
|
|
|
|
|
) {
|
|
|
|
|
return http.get<Blob, Blob>(
|
|
|
|
|
`/platform/v1/files/loading-evidence/${encodeURIComponent(String(tenantId))}/${encodeURIComponent(String(inboundId))}/${encodeURIComponent(String(fileId))}`,
|
|
|
|
|
{ responseType: 'blob' },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 12:05:20 +08:00
|
|
|
export async function voidEvidence(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
evidenceId: string | number,
|
|
|
|
|
request: VoidEvidenceRequest,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/evidence/${evidenceId}/void`,
|
|
|
|
|
request,
|
|
|
|
|
writeOptions('evidence-void', inboundId, tenantId),
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 13:56:04 +08:00
|
|
|
export async function completeLoading(
|
|
|
|
|
tenantId: string | number,
|
|
|
|
|
inboundId: string | number,
|
|
|
|
|
request: CompleteLoadingRequest,
|
|
|
|
|
) {
|
|
|
|
|
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
|
|
|
|
|
`${inboundBasePath}/${inboundId}/loading/complete`, request,
|
|
|
|
|
writeOptions('loading-complete', inboundId, tenantId),
|
|
|
|
|
)
|
|
|
|
|
return unwrapApiResponse(response)
|
|
|
|
|
}
|