Files
admin-web/src/modules/unified-orders/api/orderFulfillment.ts
T

124 lines
5.2 KiB
TypeScript

import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
export interface UnifiedOrderFulfillmentFragment {
orderId: string
orderNo: string | null
splitSequence: number | null
splitRole: string | null
plannedPackageCount: number | null
pickedPackageCount: number | null
unpickedPackageCount: number | null
loadingPlanId: string | null
containerBookingId: string | null
sourceOrderId: string | null
status: string | null
historical: boolean
}
export interface UnifiedOrderFulfillmentTree {
rootOrderId: string
rootOrderNo: string | null
rootVersion: number | null
originalPackageCount: number | null
cumulativeLoadedPackageCount: number | null
currentRemainderPackageCount: number | null
fragments: UnifiedOrderFulfillmentFragment[]
historicalTwoChildModel: boolean
}
type UnknownRecord = Record<string, unknown>
function record(value: unknown): UnknownRecord {
return value && typeof value === 'object' ? value as UnknownRecord : {}
}
function stringValue(value: unknown) {
return value === null || value === undefined || value === '' ? null : String(value)
}
function numberValue(value: unknown) {
if (value === null || value === undefined || value === '') return null
const parsed = Number(value)
return Number.isFinite(parsed) ? Math.trunc(parsed) : null
}
function firstNumber(source: UnknownRecord, keys: string[]) {
for (const key of keys) {
const value = numberValue(source[key])
if (value !== null) return value
}
return null
}
function firstString(source: UnknownRecord, keys: string[]) {
for (const key of keys) {
const value = stringValue(source[key])
if (value !== null) return value
}
return null
}
export function normalizeOrderFulfillmentTree(payload: unknown, fallbackOrderId: string): UnifiedOrderFulfillmentTree {
const raw = record(payload)
const rootOrderId = firstString(raw, ['rootOrderId', 'parentOrderId', 'orderId']) || fallbackOrderId
const rootOrderNo = firstString(raw, ['rootOrderNo', 'parentOrderNo', 'orderNo'])
const children = Array.isArray(raw.fragments) ? raw.fragments : Array.isArray(raw.children) ? raw.children : []
const legacy = raw.historicalTwoChildModel === true
|| children.some((item) => {
const child = record(item)
const role = firstString(child, ['splitRole', 'role'])
const childId = firstString(child, ['orderId', 'childOrderId'])
const sourceId = firstString(child, ['sourceOrderId', 'parentOrderId'])
return role === 'LOADED' && childId !== null && sourceId !== null && childId !== sourceId
})
const original = firstNumber(raw, ['originalPackageCount', 'sourcePackageCount'])
?? (() => {
const allocated = firstNumber(raw, ['cumulativeLoadedPackageCount', 'allocatedPackageCount', 'loadedPackageCount'])
const remainder = firstNumber(raw, ['currentRemainderPackageCount', 'remainingPackageCount'])
return allocated === null || remainder === null ? null : allocated + remainder
})()
const cumulativeLoaded = firstNumber(raw, ['cumulativeLoadedPackageCount', 'allocatedPackageCount', 'loadedPackageCount'])
const currentRemainder = firstNumber(raw, ['currentRemainderPackageCount', 'remainingPackageCount'])
const fragments = children.map((item): UnifiedOrderFulfillmentFragment => {
const child = record(item)
const role = firstString(child, ['splitRole', 'fragmentRole', 'role'])
const planned = firstNumber(child, ['plannedPackageCount', 'sourcePackageCount', 'packageCount'])
const picked = firstNumber(child, ['pickedPackageCount', 'loadedPackageCount'])
?? (role === 'LOADED' ? firstNumber(child, ['packageCount']) : 0)
const unpicked = firstNumber(child, ['unpickedPackageCount', 'remainingPackageCount'])
?? (planned === null || picked === null ? null : Math.max(0, planned - picked))
return {
orderId: firstString(child, ['orderId', 'childOrderId', 'sourceOrderId']) || '',
orderNo: firstString(child, ['orderNo', 'childOrderNo', 'sourceOrderNo']),
splitSequence: firstNumber(child, ['splitSequence', 'sequence']),
splitRole: role,
plannedPackageCount: planned,
pickedPackageCount: picked,
unpickedPackageCount: unpicked,
loadingPlanId: firstString(child, ['loadingPlanId']),
containerBookingId: firstString(child, ['containerBookingId']),
sourceOrderId: firstString(child, ['sourceOrderId', 'parentOrderId']),
status: firstString(child, ['status', 'orderStatus', 'processStatus']),
historical: legacy,
}
}).filter((item) => item.orderId)
return {
rootOrderId,
rootOrderNo,
rootVersion: firstNumber(raw, ['rootVersion', 'parentVersion']),
originalPackageCount: original,
cumulativeLoadedPackageCount: cumulativeLoaded,
currentRemainderPackageCount: currentRemainder,
fragments,
historicalTwoChildModel: legacy,
}
}
export async function getPlatformOrderFulfillmentTree(tenantId: string, orderId: string) {
const response = await http.get<ApiEnvelope<unknown>, ApiEnvelope<unknown>>(
`/platform/v1/orders/${encodeURIComponent(orderId)}/fulfillment-tree`,
{ params: { tenantId } },
)
return normalizeOrderFulfillmentTree(unwrapApiResponse(response), orderId)
}