53 lines
7.1 KiB
TypeScript
53 lines
7.1 KiB
TypeScript
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
|
import { normalizeFleetQuoteSource, normalizeFleetQuotePage, type WireSnapshot, type WireQuotePage } from './fleetQuoteCandidates'
|
|
import type { FleetCustomerOption, TripBooking, TripBookingCargo, TripBookingCreate, TripBookingUpdate, TripBookingCommand, TripBookingResult, TripBookingList, TripIncomingEvidence, TripIncomingPreview, TripIncomingStart, TripIncomingAccept, TripBookingBind, TripAcceptedReceipt } from '../bookingTypes'
|
|
const base = '/platform/v1/last-leg/fleet/trips'
|
|
const path = (tripNo: string) => `${base}/${encodeURIComponent(tripNo)}/bookings`
|
|
type N = number | string
|
|
type WireCargo = Omit<TripBookingCargo, 'packageCount' | 'palletSpaces' | 'weightKg' | 'volumeCbm' | 'unitPrice'> & { packageCount: N | null; palletSpaces: N | null; weightKg: N | null; volumeCbm: N | null; unitPrice: N | null }
|
|
export type WireBooking = Omit<TripBooking, 'recordVersion' | 'agreedAmount' | 'cargo' | 'receipt' | 'quoteSource' | 'expectedPalletCount'> & { expectedPalletCount: N | null; recordVersion: N; agreedAmount: N | null; cargo: WireCargo; receipt: WireReceipt | null; quoteSource?: WireSnapshot | null }
|
|
type WireIncoming = Omit<TripIncomingEvidence, 'receivedPackages' | 'pallets'> & { receivedPackages: N; pallets: (Omit<TripIncomingEvidence['pallets'][number], 'packages' | 'weightKg' | 'volumeCbm'> & { packages: N; weightKg: N | null; volumeCbm: N | null })[] }
|
|
type WireReceipt = Omit<TripAcceptedReceipt, 'source' | 'pallets'> & { source: WireIncoming; pallets: { warehousePalletId: string; palletSpaces: N }[] }
|
|
type WireResult = { tripRecordVersion: N; booking: WireBooking; replayed: boolean }
|
|
const number = (value: N) => { const result = Number(value); if (value == null || String(value).trim() === '' || !Number.isFinite(result) || result < 0) throw new Error('外接预订数值无效'); return result }
|
|
const integer = (value: N) => { const result = number(value); if (!Number.isSafeInteger(result)) throw new Error('外接预订版本或数量无效'); return result }
|
|
const nullable = (value: N | null) => value == null ? null : number(value)
|
|
const incoming = (value: WireIncoming): TripIncomingEvidence => ({ ...value, receivedPackages: integer(value.receivedPackages), pallets: value.pallets.map(row => ({ ...row, packages: integer(row.packages), weightKg: nullable(row.weightKg), volumeCbm: nullable(row.volumeCbm) })) })
|
|
export const normalizeBooking = (value: WireBooking): TripBooking => ({ ...value, expectedPalletCount: value.expectedPalletCount == null ? null : integer(value.expectedPalletCount), quoteSource: value.quoteSource == null ? null : normalizeFleetQuoteSource(value.quoteSource), receipt: value.receipt == null ? null : { ...value.receipt, source: incoming(value.receipt.source), pallets: value.receipt.pallets.map(row => ({ ...row, palletSpaces: integer(row.palletSpaces) })) }, recordVersion: integer(value.recordVersion), agreedAmount: nullable(value.agreedAmount), cargo: { ...value.cargo,
|
|
packageCount: value.cargo.packageCount == null ? null : integer(value.cargo.packageCount), palletSpaces: value.cargo.palletSpaces == null ? null : integer(value.cargo.palletSpaces), weightKg: nullable(value.cargo.weightKg), volumeCbm: nullable(value.cargo.volumeCbm), unitPrice: nullable(value.cargo.unitPrice) } })
|
|
const result = (value: WireResult): TripBookingResult => ({ tripRecordVersion: integer(value.tripRecordVersion), booking: normalizeBooking(value.booking), replayed: value.replayed === true })
|
|
export interface TripBookingQuoteCriteria { customerId: string; destinationKey: string; currency: 'CNY' | 'USD'; priceBasis: 'PALLET' | 'ORDER'; page?: number; size?: number }
|
|
export async function listTripBookingQuoteCandidates(tripNo: string, criteria: TripBookingQuoteCriteria) {
|
|
return normalizeFleetQuotePage(unwrapApiResponse(await http.get<ApiEnvelope<WireQuotePage>, ApiEnvelope<WireQuotePage>>(`${path(tripNo)}/quote-candidates`, { params: criteria })))
|
|
}
|
|
export async function listFleetCustomers(keyword?: string): Promise<FleetCustomerOption[]> {
|
|
return unwrapApiResponse(await http.get<ApiEnvelope<FleetCustomerOption[]>, ApiEnvelope<FleetCustomerOption[]>>(`${base}/customer-options`, { params: { keyword } }))
|
|
}
|
|
export async function listTripBookings(tripNo: string): Promise<TripBookingList> {
|
|
const value = unwrapApiResponse(await http.get<ApiEnvelope<{ tripRecordVersion: N; records: WireBooking[] }>, ApiEnvelope<{ tripRecordVersion: N; records: WireBooking[] }>>(path(tripNo)))
|
|
return { tripRecordVersion: integer(value.tripRecordVersion), records: value.records.map(normalizeBooking) }
|
|
}
|
|
export async function createTripBooking(tripNo: string, body: TripBookingCreate): Promise<TripBookingResult> {
|
|
return result(unwrapApiResponse(await http.post<ApiEnvelope<WireResult>, ApiEnvelope<WireResult>>(path(tripNo), body)))
|
|
}
|
|
export async function updateTripBooking(tripNo: string, bookingNo: string, body: TripBookingUpdate): Promise<TripBookingResult> {
|
|
return result(unwrapApiResponse(await http.put<ApiEnvelope<WireResult>, ApiEnvelope<WireResult>>(`${path(tripNo)}/${encodeURIComponent(bookingNo)}`, body)))
|
|
}
|
|
async function command(tripNo: string, bookingNo: string, action: string, body: TripBookingCommand): Promise<TripBookingResult> {
|
|
return result(unwrapApiResponse(await http.post<ApiEnvelope<WireResult>, ApiEnvelope<WireResult>>(`${path(tripNo)}/${encodeURIComponent(bookingNo)}/${action}`, body)))
|
|
}
|
|
export const confirmTripBooking = (tripNo: string, bookingNo: string, body: TripBookingCommand) => command(tripNo, bookingNo, 'confirm', body)
|
|
export const cancelTripBooking = (tripNo: string, bookingNo: string, body: TripBookingCommand) => command(tripNo, bookingNo, 'cancel', body)
|
|
|
|
const bookingPath = (tripNo: string, bookingNo: string) => `${path(tripNo)}/${encodeURIComponent(bookingNo)}`
|
|
export async function getTripIncoming(tripNo: string, bookingNo: string): Promise<TripIncomingPreview> {
|
|
const value = unwrapApiResponse(await http.get<ApiEnvelope<{ tripRecordVersion: N; recordVersion: N; incoming: WireIncoming | null }>, ApiEnvelope<{ tripRecordVersion: N; recordVersion: N; incoming: WireIncoming | null }>>(`${bookingPath(tripNo, bookingNo)}/incoming`))
|
|
return { tripRecordVersion: integer(value.tripRecordVersion), recordVersion: integer(value.recordVersion), incoming: value.incoming == null ? null : incoming(value.incoming) }
|
|
}
|
|
async function incomingCommand(tripNo: string, bookingNo: string, action: string, body: TripIncomingStart | TripIncomingAccept | TripBookingBind): Promise<TripBookingResult> {
|
|
return result(unwrapApiResponse(await http.post<ApiEnvelope<WireResult>, ApiEnvelope<WireResult>>(`${bookingPath(tripNo, bookingNo)}/${action}`, body)))
|
|
}
|
|
export const startTripIncoming = (tripNo: string, bookingNo: string, body: TripIncomingStart) => incomingCommand(tripNo, bookingNo, 'incoming', body)
|
|
export const acceptTripIncoming = (tripNo: string, bookingNo: string, body: TripIncomingAccept) => incomingCommand(tripNo, bookingNo, 'receipt-confirmation', body)
|
|
export const bindTripBookingDispatch = (tripNo: string, bookingNo: string, body: TripBookingBind) => incomingCommand(tripNo, bookingNo, 'dispatch', body)
|