Merge branch 'dev' of http://git.buggoglobal.com/html/admin-web into release/test
This commit is contained in:
@@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { http } from '@/shared/api/http'
|
||||
import {
|
||||
adjustSmartLoadingItem, assignPendingReviewSmartLoadingOrder, assignSmartLoadingGroup, assignSmartLoadingItem, getContainer,
|
||||
adjustSmartLoadingItem, assignPendingReviewSmartLoadingOrder, assignSmartLoadingGroup, assignSmartLoadingItem, assignSmartLoadingPallet, getContainer,
|
||||
listContainers, listSmartLoadingCandidates, listSmartLoadingWorkbenchContainers, removeAllUnpickedSmartLoadingItems, removeSmartLoadingGroup, removeSmartLoadingItem,
|
||||
getContainerClearance, downloadContainerClearance, uploadContainerClearance, completeContainerClearance,
|
||||
getContainerOverview, updateContainerPickupCode, markContainerException, cancelContainerException, uploadContainerDocument, downloadContainerDocument, downloadContainerPdaPhoto,
|
||||
@@ -52,6 +52,35 @@ describe('platform container loading API', () => {
|
||||
expect(readFileSync(resolve(process.cwd(), 'src/modules/container-loading/api/platformContainerLoading.ts'), 'utf8')).not.toContain('/transport')
|
||||
})
|
||||
|
||||
it.each(['DOMESTIC', 'OVERSEAS'] as const)('reads the %s detail envelope and exact long identifiers in one request', async (workbench) => {
|
||||
vi.mocked(http.get).mockResolvedValueOnce({ code: '200', data: {
|
||||
container: { ...container, id: '9007199254740993', planId: '9007199254740995' },
|
||||
workbenchStage: 'PRE_LOADING', allowedActions: ['EDIT_CONTAINER'], statusSummary: {}, actualFacts: null, terminalDrayage: null,
|
||||
} })
|
||||
expect(await getContainer('9007199254740993', workbench)).toMatchObject({ id: '9007199254740993', planId: '9007199254740995', allowedActions: ['EDIT_CONTAINER'] })
|
||||
expect(http.get).toHaveBeenCalledExactlyOnceWith(`/platform/v1/containers/${workbench.toLowerCase()}/9007199254740993`)
|
||||
})
|
||||
|
||||
it('keeps empty overseas history pages and query filters without fetching individual details', async () => {
|
||||
vi.mocked(http.get).mockResolvedValueOnce({ code: '200', data: { records: [], page: 2, size: 20, total: 0, pages: 0 } })
|
||||
const query = { page: 2, size: 20, warehouseId: '9007199254740993', cabinetNo: 'CB-TEST', scope: 'HISTORY' as const }
|
||||
expect(await listContainers(query, 'OVERSEAS')).toEqual({ records: [], page: 2, size: 20, total: 0, pages: 0 })
|
||||
expect(http.get).toHaveBeenCalledExactlyOnceWith('/platform/v1/containers/overseas', { params: query })
|
||||
})
|
||||
|
||||
it('propagates a failed workbench request without retrying retired routes', async () => {
|
||||
const failure = new Error('workbench unavailable')
|
||||
vi.mocked(http.get).mockRejectedValueOnce(failure)
|
||||
await expect(listContainers({ page: 1, size: 20 })).rejects.toBe(failure)
|
||||
expect(http.get).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('rejects a retired flat response instead of rendering incorrect container rows', async () => {
|
||||
vi.mocked(http.get).mockResolvedValueOnce({ code: '200', data: { records: [container], page: 1, size: 20, total: 1, pages: 1 } })
|
||||
await expect(listContainers({ page: 1, size: 20 })).rejects.toThrow()
|
||||
expect(http.get).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('uses the frozen simulation create and ocean booking link contracts', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', data: { ...container, oceanBookingId: 101, oceanBookingRequirementId: 202 } })
|
||||
vi.mocked(http.delete).mockResolvedValue({ code: '200', data: { ...container, oceanBookingId: null, oceanBookingRequirementId: null } })
|
||||
@@ -91,6 +120,17 @@ describe('platform container loading API', () => {
|
||||
expect(vi.mocked(http.delete).mock.calls[0]?.[1]).toMatchObject({ data: { expectedPlanVersion: 9, expectedItemVersion: 10, operationId: expect.stringContaining('remove-') } })
|
||||
})
|
||||
|
||||
it('assigns a sealed pallet through the whole-pallet command', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', data: plan })
|
||||
|
||||
await assignSmartLoadingPallet('10', { palletId: '9007199254740998', expectedVersion: 5 })
|
||||
|
||||
expect(http.post).toHaveBeenCalledWith(
|
||||
'/platform/v1/first-leg/smart-loading/plans/10/pallet-assignments',
|
||||
{ palletId: '9007199254740998', expectedVersion: 5, operationId: expect.stringContaining('assign-pallet-') },
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the dedicated provisional assignment command for a pending-review order', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', data: plan })
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ import type {
|
||||
ContainerOverviewDocument,
|
||||
ContainerWorkbenchKind,
|
||||
ContainerWorkbenchRow,
|
||||
ContainerWorkbenchItem,
|
||||
ContainerWorkbenchPage,
|
||||
} from '../types'
|
||||
|
||||
const containerPath = '/platform/v1/containers'
|
||||
@@ -98,11 +100,10 @@ function container(raw: ContainerBookingResource): ContainerBookingResource {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeWorkbenchRow(raw: ContainerWorkbenchRow | ({ container: ContainerBookingResource } & Partial<ContainerWorkbenchRow>)): ContainerWorkbenchRow {
|
||||
const source = 'container' in raw ? raw.container : raw
|
||||
function normalizeWorkbenchRow(raw: ContainerWorkbenchItem): ContainerWorkbenchRow {
|
||||
const drayage = raw.terminalDrayage
|
||||
return {
|
||||
...container(source),
|
||||
...container(raw.container),
|
||||
workbenchStage: raw.workbenchStage || null,
|
||||
statusSummary: raw.statusSummary || {},
|
||||
allowedActions: raw.allowedActions || [],
|
||||
@@ -128,6 +129,8 @@ function item(raw: SmartLoadingItem): SmartLoadingItem {
|
||||
id: id(raw.id), loadingPlanId: id(raw.loadingPlanId), accountId: id(raw.accountId),
|
||||
inboundId: id(raw.inboundId), inboundDetailId: id(raw.inboundDetailId), orderId: id(raw.orderId),
|
||||
destinationWarehouseId: optionalId(raw.destinationWarehouseId), version: numberValue(raw.version),
|
||||
palletId: optionalId(raw.palletId), palletNo: raw.palletNo || null,
|
||||
unitType: raw.unitType, indivisible: Boolean(raw.indivisible),
|
||||
packageCount: numberValue(raw.packageCount),
|
||||
pickedPackageCount: numberValue(raw.pickedPackageCount),
|
||||
minPackageCount: numberValue(raw.minPackageCount, 1),
|
||||
@@ -162,9 +165,9 @@ function plan(raw: SmartLoadingPlan): SmartLoadingPlan {
|
||||
|
||||
export async function listContainers(query: ContainerQuery, workbench: ContainerWorkbenchKind = 'DOMESTIC'): Promise<ContainerPage> {
|
||||
const params = { ...query, scope: query.scope || 'ACTIVE' }
|
||||
const response = await http.get<ApiEnvelope<ContainerPage>, ApiEnvelope<ContainerPage>>(`${containerPath}/${workbench.toLowerCase()}`, { params })
|
||||
const response = await http.get<ApiEnvelope<ContainerWorkbenchPage>, ApiEnvelope<ContainerWorkbenchPage>>(`${containerPath}/${workbench.toLowerCase()}`, { params })
|
||||
const result = unwrapApiResponse(response)
|
||||
return { ...result, records: (result.records || []).map((record) => normalizeWorkbenchRow(record as ContainerWorkbenchRow)), page: numberValue(result.page, query.page), size: numberValue(result.size, query.size), total: numberValue(result.total), pages: numberValue(result.pages) }
|
||||
return { ...result, records: result.records.map(normalizeWorkbenchRow), page: numberValue(result.page, query.page), size: numberValue(result.size, query.size), total: numberValue(result.total), pages: numberValue(result.pages) }
|
||||
}
|
||||
|
||||
export async function getCustomerContainerTransitSummary(customerId: string): Promise<CustomerContainerTransitSummary> {
|
||||
@@ -180,7 +183,7 @@ export async function getCustomerContainerTransitSummary(customerId: string): Pr
|
||||
}
|
||||
|
||||
export async function getContainer(containerId: string, workbench: ContainerWorkbenchKind = 'DOMESTIC'): Promise<ContainerWorkbenchRow> {
|
||||
const response = await http.get<ApiEnvelope<ContainerWorkbenchRow>, ApiEnvelope<ContainerWorkbenchRow>>(`${containerPath}/${workbench.toLowerCase()}/${encodeURIComponent(containerId)}`)
|
||||
const response = await http.get<ApiEnvelope<ContainerWorkbenchItem>, ApiEnvelope<ContainerWorkbenchItem>>(`${containerPath}/${workbench.toLowerCase()}/${encodeURIComponent(containerId)}`)
|
||||
return normalizeWorkbenchRow(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
@@ -303,7 +306,8 @@ export async function listSmartLoadingCandidates(query: SmartLoadingCandidateQue
|
||||
return {
|
||||
...raw, accountId, customerId: accountId, inboundId: optionalId(raw.inboundId),
|
||||
orderId: id(raw.orderId), warehouseId: id(raw.warehouseId), destinationWarehouseId: optionalId(raw.destinationWarehouseId),
|
||||
coLoadGroupId: optionalId(raw.coLoadGroupId),
|
||||
coLoadGroupId: optionalId(raw.coLoadGroupId), palletId: optionalId(raw.palletId), palletNo: raw.palletNo || null,
|
||||
unitType: raw.unitType, indivisible: Boolean(raw.indivisible),
|
||||
memberCount: raw.memberCount == null ? null : numberValue(raw.memberCount),
|
||||
groupTotalBoxes: raw.groupTotalBoxes == null ? null : numberValue(raw.groupTotalBoxes),
|
||||
locked: raw.locked == null ? null : Boolean(raw.locked),
|
||||
@@ -353,6 +357,14 @@ export async function assignSmartLoadingItem(planId: string, payload: { inboundI
|
||||
return plan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function assignSmartLoadingPallet(planId: string, payload: { palletId: string; expectedVersion: number }) {
|
||||
const response = await http.post<ApiEnvelope<SmartLoadingPlan>, ApiEnvelope<SmartLoadingPlan>>(
|
||||
`${smartLoadingPath}/plans/${encodeURIComponent(planId)}/pallet-assignments`,
|
||||
{ ...payload, operationId: operationId('assign-pallet') },
|
||||
)
|
||||
return plan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function assignPendingReviewSmartLoadingOrder(planId: string, payload: { customerId: string; orderId: string; inboundCode: string; packageCount: number; expectedVersion: number }) {
|
||||
const response = await http.post<ApiEnvelope<SmartLoadingPlan>, ApiEnvelope<SmartLoadingPlan>>(
|
||||
`${smartLoadingPath}/plans/${encodeURIComponent(planId)}/pending-review-assignments`,
|
||||
|
||||
Reference in New Issue
Block a user