fix(client): render estimated sailing fields from unified orders
This commit is contained in:
@@ -48,8 +48,14 @@ export interface MerchantUnifiedTransportSummary {
|
|||||||
cabinetNo: string | null
|
cabinetNo: string | null
|
||||||
vesselName: string | null
|
vesselName: string | null
|
||||||
voyageNo: string | null
|
voyageNo: string | null
|
||||||
sailingAt: string | null
|
estimatedSailingAt: string | null
|
||||||
portArrivalAt: string | null
|
actualSailingAt: string | null
|
||||||
|
effectiveSailingAt: string | null
|
||||||
|
effectiveSailingAtEstimated: boolean | null
|
||||||
|
estimatedPortArrivalAt: string | null
|
||||||
|
actualPortArrivalAt: string | null
|
||||||
|
effectivePortArrivalAt: string | null
|
||||||
|
effectivePortArrivalAtEstimated: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MerchantUnifiedOrderRow {
|
export interface MerchantUnifiedOrderRow {
|
||||||
|
|||||||
@@ -1,11 +1,32 @@
|
|||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
import { readFileSync } from 'node:fs'
|
import { readFileSync } from 'node:fs'
|
||||||
import test from 'node:test'
|
import test from 'node:test'
|
||||||
|
import { runInNewContext } from 'node:vm'
|
||||||
|
import ts from 'typescript'
|
||||||
|
|
||||||
const storeSource = readFileSync(new URL('./firstLegOrders.ts', import.meta.url), 'utf8')
|
const storeSource = readFileSync(new URL('./firstLegOrders.ts', import.meta.url), 'utf8')
|
||||||
const apiSource = readFileSync(new URL('../api/firstLeg.ts', import.meta.url), 'utf8')
|
const apiSource = readFileSync(new URL('../api/firstLeg.ts', import.meta.url), 'utf8')
|
||||||
const unifiedApiSource = readFileSync(new URL('../api/unifiedOrders.ts', import.meta.url), 'utf8')
|
const unifiedApiSource = readFileSync(new URL('../api/unifiedOrders.ts', import.meta.url), 'utf8')
|
||||||
|
|
||||||
|
test('renders estimated sailing from the current API without substituting actual departure', () => {
|
||||||
|
const mapper = storeSource.slice(storeSource.indexOf('function businessSummary('), storeSource.indexOf('\nconst unifiedActionLabels'))
|
||||||
|
const javascript = ts.transpileModule(mapper, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText
|
||||||
|
const map = runInNewContext(`${javascript}; businessSummary`)
|
||||||
|
const transport = {
|
||||||
|
vesselName: 'REASSIGNED', voyageNo: 'V02',
|
||||||
|
estimatedSailingAt: '2026-10-07T08:00:00', actualSailingAt: '2026-10-08T09:00:00',
|
||||||
|
effectiveSailingAt: '2026-10-08T09:00:00', effectiveSailingAtEstimated: false,
|
||||||
|
estimatedPortArrivalAt: '2026-10-27T08:00:00', actualPortArrivalAt: null,
|
||||||
|
}
|
||||||
|
const mapped = map({ transport })
|
||||||
|
assert.equal(mapped.estimatedSailingAt, transport.estimatedSailingAt)
|
||||||
|
assert.equal(mapped.estimatedPortArrivalAt, transport.estimatedPortArrivalAt)
|
||||||
|
assert.equal(mapped.vesselName, 'REASSIGNED')
|
||||||
|
assert.equal(mapped.voyageNo, 'V02')
|
||||||
|
assert.equal(map().estimatedSailingAt, '')
|
||||||
|
assert.equal(map({ transport: null }).estimatedPortArrivalAt, '')
|
||||||
|
})
|
||||||
|
|
||||||
test('maps the first-leg aggregate list fields without loading detail rows', () => {
|
test('maps the first-leg aggregate list fields without loading detail rows', () => {
|
||||||
assert.match(apiSource, /interface MerchantFirstLegOrderRow/)
|
assert.match(apiSource, /interface MerchantFirstLegOrderRow/)
|
||||||
assert.match(apiSource, /skuNos: string \| string\[\] \| null/)
|
assert.match(apiSource, /skuNos: string \| string\[\] \| null/)
|
||||||
|
|||||||
@@ -220,8 +220,8 @@ function businessSummary(summary?: MerchantUnifiedOrderRow): FirstLegOrder['busi
|
|||||||
cabinetNo: summary?.transport?.cabinetNo ?? '',
|
cabinetNo: summary?.transport?.cabinetNo ?? '',
|
||||||
vesselName: summary?.transport?.vesselName ?? '',
|
vesselName: summary?.transport?.vesselName ?? '',
|
||||||
voyageNo: summary?.transport?.voyageNo ?? '',
|
voyageNo: summary?.transport?.voyageNo ?? '',
|
||||||
sailingAt: summary?.transport?.sailingAt ?? '',
|
estimatedSailingAt: summary?.transport?.estimatedSailingAt ?? '',
|
||||||
portArrivalAt: summary?.transport?.portArrivalAt ?? '',
|
estimatedPortArrivalAt: summary?.transport?.estimatedPortArrivalAt ?? '',
|
||||||
inboundId: summary?.inboundId == null ? '' : String(summary.inboundId),
|
inboundId: summary?.inboundId == null ? '' : String(summary.inboundId),
|
||||||
inboundVersion: summary?.inboundVersion ?? null,
|
inboundVersion: summary?.inboundVersion ?? null,
|
||||||
inboundCode: summary?.inboundCode ?? '',
|
inboundCode: summary?.inboundCode ?? '',
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ export interface FirstLegOrderBusinessSummary {
|
|||||||
cabinetNo: string
|
cabinetNo: string
|
||||||
vesselName: string
|
vesselName: string
|
||||||
voyageNo: string
|
voyageNo: string
|
||||||
sailingAt: string
|
estimatedSailingAt: string
|
||||||
portArrivalAt: string
|
estimatedPortArrivalAt: string
|
||||||
inboundId: string
|
inboundId: string
|
||||||
inboundVersion: number | null
|
inboundVersion: number | null
|
||||||
inboundCode: string
|
inboundCode: string
|
||||||
|
|||||||
@@ -729,7 +729,7 @@ test('shows authoritative warehouse, loading and sailing fields on desktop and n
|
|||||||
}
|
}
|
||||||
assert.match(source, /businessSummary\.cabinetNo \|\| .*businessSummary\.containerNo/)
|
assert.match(source, /businessSummary\.cabinetNo \|\| .*businessSummary\.containerNo/)
|
||||||
assert.match(source, /businessSummary\.vesselName.*businessSummary\.voyageNo/s)
|
assert.match(source, /businessSummary\.vesselName.*businessSummary\.voyageNo/s)
|
||||||
assert.match(source, /businessSummary\.sailingAt/)
|
assert.match(source, /businessSummary\.estimatedSailingAt/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('loads complete order detail before editing and rejects an unavailable historical warehouse', () => {
|
test('loads complete order detail before editing and rejects an unavailable historical warehouse', () => {
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ const selectableOrderColumns: DataTableColumn[] = [
|
|||||||
{ key: 'expectedShipDate', label: '期望出运', width: 132, align: 'center', headerAlign: 'center', slotName: 'expectedShipDate' },
|
{ key: 'expectedShipDate', label: '期望出运', width: 132, align: 'center', headerAlign: 'center', slotName: 'expectedShipDate' },
|
||||||
{ key: 'cabinetNo', label: '柜号 / 柜次', width: 150, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'cabinetNo' },
|
{ key: 'cabinetNo', label: '柜号 / 柜次', width: 150, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'cabinetNo' },
|
||||||
{ key: 'vesselVoyage', label: '船名 / 航次', width: 160, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'vesselVoyage' },
|
{ key: 'vesselVoyage', label: '船名 / 航次', width: 160, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'vesselVoyage' },
|
||||||
{ key: 'sailingAt', label: '预计船期', width: 164, align: 'center', headerAlign: 'center', slotName: 'sailingAt' },
|
{ key: 'estimatedSailingAt', label: '预计船期', width: 164, align: 'center', headerAlign: 'center', slotName: 'estimatedSailingAt' },
|
||||||
{ key: 'createdAt', label: '创建时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'createdAt' },
|
{ key: 'createdAt', label: '创建时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'createdAt' },
|
||||||
{ key: 'updatedAt', label: '更新时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'updatedAt' },
|
{ key: 'updatedAt', label: '更新时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'updatedAt' },
|
||||||
]
|
]
|
||||||
@@ -572,7 +572,7 @@ const progressSummaryItems = computed(() => {
|
|||||||
const cabinetProgressItems = computed(() => {
|
const cabinetProgressItems = computed(() => {
|
||||||
const summary = detailOrder.value?.businessSummary
|
const summary = detailOrder.value?.businessSummary
|
||||||
if (!summary || (!summary.cabinetNo && !summary.containerNo)) return []
|
if (!summary || (!summary.cabinetNo && !summary.containerNo)) return []
|
||||||
return [{ cabinetId: summary.cabinetNo || summary.containerNo, cabinetNo: summary.cabinetNo || summary.containerNo, status: summary.loadingStatus || '待更新', vesselName: summary.vesselName, voyageNo: summary.voyageNo, sailingAt: summary.sailingAt }]
|
return [{ cabinetId: summary.cabinetNo || summary.containerNo, cabinetNo: summary.cabinetNo || summary.containerNo, status: summary.loadingStatus || '待更新', vesselName: summary.vesselName, voyageNo: summary.voyageNo, estimatedSailingAt: summary.estimatedSailingAt }]
|
||||||
})
|
})
|
||||||
const selectedWarehouse = computed(() => availableWarehouses.value.find((item) => item.id === form.receivingWarehouseId))
|
const selectedWarehouse = computed(() => availableWarehouses.value.find((item) => item.id === form.receivingWarehouseId))
|
||||||
const selectedDestinationWarehouse = computed(() => store.warehouses.find((item) => item.id === form.destinationWarehouseId && item.warehouseType === form.destinationType))
|
const selectedDestinationWarehouse = computed(() => store.warehouses.find((item) => item.id === form.destinationWarehouseId && item.warehouseType === form.destinationType))
|
||||||
@@ -2147,7 +2147,7 @@ async function searchOrders(model: QueryModel) {
|
|||||||
<template #expectedShipDate="{ row }">{{ (row as FirstLegOrder).expectedDepartureDate ? formatDateTime((row as FirstLegOrder).expectedDepartureDate) : '-' }}</template>
|
<template #expectedShipDate="{ row }">{{ (row as FirstLegOrder).expectedDepartureDate ? formatDateTime((row as FirstLegOrder).expectedDepartureDate) : '-' }}</template>
|
||||||
<template #cabinetNo="{ row }"><span class="table-ellipsis table-code" :title="displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo)">{{ displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo) }}</span></template>
|
<template #cabinetNo="{ row }"><span class="table-ellipsis table-code" :title="displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo)">{{ displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo) }}</span></template>
|
||||||
<template #vesselVoyage="{ row }"><span class="table-ellipsis" :title="displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · '))">{{ displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · ')) }}</span></template>
|
<template #vesselVoyage="{ row }"><span class="table-ellipsis" :title="displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · '))">{{ displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · ')) }}</span></template>
|
||||||
<template #sailingAt="{ row }">{{ (row as FirstLegOrder).businessSummary.sailingAt ? formatDateTime((row as FirstLegOrder).businessSummary.sailingAt) : '-' }}</template>
|
<template #estimatedSailingAt="{ row }">{{ (row as FirstLegOrder).businessSummary.estimatedSailingAt ? formatDateTime((row as FirstLegOrder).businessSummary.estimatedSailingAt) : '-' }}</template>
|
||||||
<template #createdAt="{ row }">{{ formatDateTime((row as FirstLegOrder).createdAt) }}</template>
|
<template #createdAt="{ row }">{{ formatDateTime((row as FirstLegOrder).createdAt) }}</template>
|
||||||
<template #updatedAt="{ row }">{{ formatDateTime((row as FirstLegOrder).updatedAt) }}</template>
|
<template #updatedAt="{ row }">{{ formatDateTime((row as FirstLegOrder).updatedAt) }}</template>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -2395,7 +2395,7 @@ async function searchOrders(model: QueryModel) {
|
|||||||
<el-table v-if="cabinetProgressItems.length" :data="cabinetProgressItems" border size="small" class="cabinet-progress-table" row-key="cabinetId">
|
<el-table v-if="cabinetProgressItems.length" :data="cabinetProgressItems" border size="small" class="cabinet-progress-table" row-key="cabinetId">
|
||||||
<el-table-column prop="cabinetNo" label="柜号 / 柜次" min-width="150"><template #default="{ row }"><span class="table-code">{{ row.cabinetNo || '—' }}</span></template></el-table-column>
|
<el-table-column prop="cabinetNo" label="柜号 / 柜次" min-width="150"><template #default="{ row }"><span class="table-code">{{ row.cabinetNo || '—' }}</span></template></el-table-column>
|
||||||
<el-table-column label="船名 / 航次" min-width="180"><template #default="{ row }">{{ [row.vesselName, row.voyageNo].filter(Boolean).join(' · ') || '待更新' }}</template></el-table-column>
|
<el-table-column label="船名 / 航次" min-width="180"><template #default="{ row }">{{ [row.vesselName, row.voyageNo].filter(Boolean).join(' · ') || '待更新' }}</template></el-table-column>
|
||||||
<el-table-column label="预计船期" width="170"><template #default="{ row }">{{ row.sailingAt ? formatDateTime(row.sailingAt) : '待更新' }}</template></el-table-column>
|
<el-table-column label="预计船期" width="170"><template #default="{ row }">{{ row.estimatedSailingAt ? formatDateTime(row.estimatedSailingAt) : '待更新' }}</template></el-table-column>
|
||||||
<el-table-column prop="status" label="当前节点" width="120"><template #default="{ row }"><el-tag size="small" effect="plain">{{ row.status || '待更新' }}</el-tag></template></el-table-column>
|
<el-table-column prop="status" label="当前节点" width="120"><template #default="{ row }"><el-tag size="small" effect="plain">{{ row.status || '待更新' }}</el-tag></template></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<p v-else class="detail-inline-empty">尚未分配柜次,后续柜次节点将在这里同步。</p>
|
<p v-else class="detail-inline-empty">尚未分配柜次,后续柜次节点将在这里同步。</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user