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 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>( `/platform/v1/orders/${encodeURIComponent(orderId)}/fulfillment-tree`, { params: { tenantId } }, ) return normalizeOrderFulfillmentTree(unwrapApiResponse(response), orderId) }