优化
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.common.event;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FirstLegLoadingPlanCompletedV2Test {
|
||||
@Test
|
||||
void carriesTheThreeItemRemarks() throws Exception {
|
||||
var item = new FirstLegLoadingPlanCompletedV2.Item("1", "SKU", "中文", "EN", 1,
|
||||
2, null, null, null, null, null, null, null, null, null, null, null,
|
||||
"客户备注", "内部备注", "仓库备注");
|
||||
var order = new FirstLegLoadingPlanCompletedV2.Order("2", "2", "3", "ORD", "3", "RK",
|
||||
null, null, null, 1, 2, null, null, null, null, List.of(item));
|
||||
var payload = new FirstLegLoadingPlanCompletedV2("4", "P", "5", "CONT", "CAB", "6", "WH", List.of(order));
|
||||
String json = new ObjectMapper().writeValueAsString(payload);
|
||||
assertThat(json).contains("customerRemark", "internalRemark", "warehouseRemark");
|
||||
assertThat(new ObjectMapper().readTree(json).at("/orders/0/items/0/warehouseRemark").asText())
|
||||
.isEqualTo("仓库备注");
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ class PlatformSmartLoadingControllerContractTest {
|
||||
authority("assign","platform:firstleg:smart-loading:assign",AuthenticatedUser.class,long.class,AssignRequest.class);
|
||||
authority("recommend","platform:firstleg:smart-loading:assign",AuthenticatedUser.class,long.class,RecommendRequest.class);
|
||||
authority("adjust","platform:firstleg:smart-loading:assign",AuthenticatedUser.class,long.class,long.class,AdjustRequest.class);
|
||||
authority("warehouseRemark","platform:firstleg:smart-loading:assign",AuthenticatedUser.class,long.class,long.class,WarehouseRemarkRequest.class);
|
||||
authority("warehouseRemark","platform:firstleg:preload:edit",AuthenticatedUser.class,long.class,long.class,WarehouseRemarkRequest.class);
|
||||
authority("delete","platform:firstleg:smart-loading:delete",AuthenticatedUser.class,long.class,long.class,DeleteRequest.class);
|
||||
authority("push","platform:firstleg:smart-loading:push",AuthenticatedUser.class,long.class,String.class,PushRequest.class);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ export function oceanBookingFieldGaps(record) {
|
||||
return gaps(record, {
|
||||
id: isPositiveId,
|
||||
bookingNo: isNonBlank,
|
||||
status: (value) => ['CONFIRMED', 'CHANGE_PENDING', 'RELEASED', 'CANCELLED'].includes(value),
|
||||
status: (value) => ['DRAFT', 'RELEASED'].includes(value),
|
||||
vesselName: isNonBlank,
|
||||
voyageNo: isNonBlank,
|
||||
estimatedSailingAt: isIsoDateTime,
|
||||
|
||||
@@ -2,7 +2,7 @@ const MODULE = 'manual-state-ocean-trucking'
|
||||
const OPTIONS = { scenario: 'manual-ocean-trucking-state-matrix', deploymentBlocker: true }
|
||||
const OCEAN_BASE = '/api/platform/v1/ocean-bookings'
|
||||
const TRUCK_BASE = '/api/platform/v1/truck-appointments'
|
||||
const OCEAN_LIST_STATUSES = ['CONFIRMED', 'CHANGE_PENDING', 'RELEASED', 'CANCELLED']
|
||||
const OCEAN_LIST_STATUSES = ['DRAFT', 'RELEASED']
|
||||
const OCEAN_STATUSES = [...OCEAN_LIST_STATUSES]
|
||||
const TRUCK_STATUSES = ['DRAFT', 'PENDING_DISPATCH', 'DISPATCHED', 'PICKUP_IN_PROGRESS', 'ARRIVED_WAREHOUSE',
|
||||
'LOADING', 'DEPARTED', 'COMPLETED', 'RESCHEDULED', 'EXCEPTION', 'CANCELLED']
|
||||
@@ -36,20 +36,20 @@ function oceanPayload(context, template, index) {
|
||||
remark: `${context.marker} 人工列表状态 ${index}`,
|
||||
parties: template.parties.map(({ partyType, partyName, address, contactName, contactPhone }) =>
|
||||
({ partyType, partyName, address, contactName, contactPhone })),
|
||||
containers: template.containers.map(({ containerType, containerKind, capacityCbm, maxWeightKg }, containerIndex) =>
|
||||
({ containerNo: `MAN-${suffix(context, index)}-${String(containerIndex + 1).padStart(2, '0')}`.slice(0, 64),
|
||||
containerType, containerKind, capacityCbm, maxWeightKg })),
|
||||
containerRequirements: template.containerRequirements.map(({ containerType, containerKind, quantity, capacityCbm, maxWeightKg }) =>
|
||||
({ containerType, containerKind, quantity, capacityCbm, maxWeightKg })),
|
||||
} }
|
||||
}
|
||||
|
||||
async function createOcean(context, template, index) {
|
||||
const response = await post(context, OCEAN_BASE, oceanPayload(context, template, index), `ocean-create-${index}`)
|
||||
const value = context.apiSuccess(MODULE, `创建订舱状态样本 ${index}`, response, '', OPTIONS)
|
||||
context.assert(MODULE, `订舱样本 ${index} 一次创建直接返回 CONFIRMED`, response,
|
||||
Boolean(value?.oceanBooking?.id) && value?.oceanBooking?.status === 'CONFIRMED',
|
||||
context.assert(MODULE, `订舱样本 ${index} 创建为 DRAFT 且无真实柜`, response,
|
||||
Boolean(value?.oceanBooking?.id) && value?.oceanBooking?.status === 'DRAFT'
|
||||
&& (!value?.containers || value.containers.length === 0),
|
||||
`id=${id(value?.oceanBooking?.id) || 'missing'}, status=${value?.oceanBooking?.status || 'missing'}`, OPTIONS)
|
||||
if (!value?.oceanBooking?.id || value?.oceanBooking?.status !== 'CONFIRMED') {
|
||||
throw new Error(`创建订舱样本 ${index} 未直接进入 CONFIRMED`)
|
||||
if (!value?.oceanBooking?.id || value?.oceanBooking?.status !== 'DRAFT') {
|
||||
throw new Error(`创建订舱样本 ${index} 未进入 DRAFT`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -64,15 +64,8 @@ async function oceanAction(context, value, endpoint, body = {}) {
|
||||
|
||||
async function buildOcean(context, template, status, index) {
|
||||
let value = await createOcean(context, template, index)
|
||||
if (status === 'CONFIRMED') return value
|
||||
if (status === 'CANCELLED') return oceanAction(context, value, 'cancel')
|
||||
if (status === 'CHANGE_PENDING') return oceanAction(context, value, 'change-request', {
|
||||
vesselName: `变更轮-${suffix(context, index)}`, voyageNo: `CV${suffix(context, index)}`,
|
||||
estimatedSailingAt: '2026-09-16T08:00:00', estimatedArrivalAt: '2026-10-16T08:00:00' })
|
||||
if (status === 'RELEASED') return oceanAction(context, value, 'release', {
|
||||
billOfLadingNo: value.oceanBooking.billOfLadingNo, vesselName: value.oceanBooking.vesselName,
|
||||
voyageNo: value.oceanBooking.voyageNo, estimatedSailingAt: value.oceanBooking.estimatedSailingAt,
|
||||
estimatedArrivalAt: value.oceanBooking.estimatedArrivalAt })
|
||||
if (status === 'DRAFT') return value
|
||||
if (status === 'RELEASED') return oceanAction(context, value, 'release')
|
||||
throw new Error(`unsupported ocean status ${status}`)
|
||||
}
|
||||
|
||||
@@ -139,8 +132,8 @@ export async function run(context, matrix = context.createdResources.manualState
|
||||
if (!sourceOceanId) throw new Error('缺少现有订舱生命周期模块生成的模板资源')
|
||||
const templateResponse = await context.request('GET', `${OCEAN_BASE}/${sourceOceanId}`)
|
||||
const template = context.apiSuccess(MODULE, '读取订舱状态矩阵模板', templateResponse, '', OPTIONS)
|
||||
if (!template?.oceanBooking || !template?.parties?.length || !template?.containers?.length) {
|
||||
throw new Error('订舱模板缺少渠道、仓库、参与方或真实柜明细')
|
||||
if (!template?.oceanBooking || !template?.parties?.length || !template?.containerRequirements?.length) {
|
||||
throw new Error('订舱模板缺少渠道、仓库、参与方或柜型需求')
|
||||
}
|
||||
matrix.pages.oceanBookings = { requiredStatuses: OCEAN_LIST_STATUSES, fixtures: {} }
|
||||
for (const [index, status] of OCEAN_STATUSES.entries()) {
|
||||
|
||||
@@ -161,9 +161,10 @@ export async function run(context) {
|
||||
parties: [{ partyType: 'SHIPPER', partyName: `发货方-${suffix}` },
|
||||
{ partyType: 'CONSIGNEE', partyName: `收货方-${suffix}` },
|
||||
{ partyType: 'NOTIFY_PARTY', partyName: `通知方-${suffix}` }],
|
||||
containers: [
|
||||
{ containerNo: realContainerNo, ...primaryRequirement },
|
||||
{ containerNo: wrongContainerNo, containerType: '20GP', containerKind: 'FCL', capacityCbm: 33, maxWeightKg: 28000 },
|
||||
containerRequirements: [
|
||||
{ containerType: primaryRequirement.containerType, containerKind: primaryRequirement.containerKind, quantity: 1,
|
||||
capacityCbm: primaryRequirement.capacityCbm, maxWeightKg: primaryRequirement.maxWeightKg },
|
||||
{ containerType: '20GP', containerKind: 'FCL', quantity: 1, capacityCbm: 33, maxWeightKg: 28000 },
|
||||
],
|
||||
}
|
||||
context.assert(MODULE, '订舱渠道与仓库快照完整', inboundResponse, Boolean(channelCode && channelName && warehouseName),
|
||||
@@ -373,8 +374,12 @@ export async function run(context) {
|
||||
|
||||
const bookingCreate = await request(context, 'POST', '/api/platform/v1/ocean-bookings',
|
||||
{ expectedVersion: 0, data: bookingDraft }, { 'Idempotency-Key': `${context.marker}-loading-booking-create` })
|
||||
const bookingCreated = context.apiSuccess(MODULE, '登记含渠道仓库和运输快照的已确认真实订舱', bookingCreate, '', OPTIONS)
|
||||
const bookingId = id(bookingCreated?.oceanBooking?.id)
|
||||
const bookingCreatedDraft = context.apiSuccess(MODULE, '登记含渠道仓库和运输快照的 DRAFT 订舱', bookingCreate, '', OPTIONS)
|
||||
const bookingId = id(bookingCreatedDraft?.oceanBooking?.id)
|
||||
const bookingRelease = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${bookingId}/release`, {
|
||||
expectedVersion: Number(bookingCreatedDraft?.oceanBooking?.version), reason: context.marker,
|
||||
}, { 'Idempotency-Key': `${context.marker}-loading-booking-release`.slice(0, 128) })
|
||||
const bookingCreated = context.apiSuccess(MODULE, '放舱订舱并冻结运输事实', bookingRelease, '', OPTIONS)
|
||||
const bookingNo = id(bookingCreated?.oceanBooking?.bookingNo)
|
||||
if (!bookingId) return
|
||||
const confirmedBooking = bookingCreated
|
||||
@@ -383,35 +388,31 @@ export async function run(context) {
|
||||
const frozenTransportMatches = transportFields.every((field) => frozenTransport[field] != null
|
||||
&& String(frozenTransport[field]).trim() !== ''
|
||||
&& String(frozenTransport[field]) === String(bookingDraft[field]))
|
||||
context.assert(MODULE, '一次登记直接返回 CONFIRMED 且尚未占用模拟柜', bookingCreate,
|
||||
confirmedBooking?.oceanBooking?.status === 'CONFIRMED'
|
||||
&& Array.isArray(confirmedBooking?.containers) && confirmedBooking.containers.length === 2,
|
||||
`status=${confirmedBooking?.oceanBooking?.status || 'missing'}, containers=${confirmedBooking?.containers?.length ?? 'n/a'}`, OPTIONS)
|
||||
context.assert(MODULE, '已确认订舱回显完整且一致的冻结运输快照', bookingCreate,
|
||||
context.assert(MODULE, '订舱放舱为 RELEASED 且不预生成真实货柜', bookingRelease,
|
||||
confirmedBooking?.oceanBooking?.status === 'RELEASED'
|
||||
&& Array.isArray(confirmedBooking?.containerRequirements) && confirmedBooking.containerRequirements.length === 2
|
||||
&& (!confirmedBooking?.containers || confirmedBooking.containers.length === 0),
|
||||
`status=${confirmedBooking?.oceanBooking?.status || 'missing'}, requirements=${confirmedBooking?.containerRequirements?.length ?? 'n/a'}, containers=${confirmedBooking?.containers?.length ?? 0}`, OPTIONS)
|
||||
context.assert(MODULE, 'RELEASED 订舱回显完整且一致的冻结运输快照', bookingRelease,
|
||||
frozenTransportMatches,
|
||||
transportFields.map((field) => `${field}=${frozenTransport[field] || 'missing'}`).join(', '), OPTIONS)
|
||||
if (!frozenTransportMatches) return
|
||||
const confirmedListResponse = await context.request('GET',
|
||||
`/api/platform/v1/ocean-bookings?page=1&size=20&keyword=${encodeURIComponent(bookingNo)}`)
|
||||
const confirmedList = context.apiSuccess(MODULE, '按真实订舱号查询正式列表', confirmedListResponse, '', OPTIONS)
|
||||
context.assert(MODULE, 'CONFIRMED 后真实订舱进入正式列表', confirmedListResponse,
|
||||
records(confirmedList).some((item) => id(item.id) === bookingId && item.status === 'CONFIRMED'),
|
||||
context.assert(MODULE, 'RELEASED 订舱进入正式列表', confirmedListResponse,
|
||||
records(confirmedList).some((item) => id(item.id) === bookingId && item.status === 'RELEASED'),
|
||||
`bookingId=${bookingId}, records=${records(confirmedList).length}`, OPTIONS)
|
||||
|
||||
const requirement = confirmedBooking?.containers?.find?.((item) => item.containerNo === realContainerNo)
|
||||
const wrongTypeRequirement = confirmedBooking?.containers?.find?.((item) => item.containerNo === wrongContainerNo)
|
||||
if (!requirement?.containerBookingId || !wrongTypeRequirement?.containerBookingId) return
|
||||
const wrongTypeLinkResponse = await request(context, 'POST', `${CONTAINERS}/${containerId}/ocean-booking-link`, {
|
||||
realContainerBookingId: id(wrongTypeRequirement.containerBookingId), expectedVersion: Number(simulationBeforeLink.version),
|
||||
})
|
||||
context.addStep(MODULE, '箱型不匹配拒绝关联真实订舱柜', wrongTypeLinkResponse,
|
||||
failure(wrongTypeLinkResponse), `HTTP=${wrongTypeLinkResponse.http}, code=${codeOf(wrongTypeLinkResponse)}`, OPTIONS)
|
||||
const requirement = confirmedBooking?.containerRequirements?.find?.((item) => item.containerType === primaryRequirement.containerType)
|
||||
if (!requirement?.id) return
|
||||
|
||||
const beforeLinkItems = JSON.stringify(assigned.items?.map((item) => ({ id: id(item.id), inboundId: id(item.inboundId), packageCount: Number(item.packageCount) })) || [])
|
||||
const linkResponse = await request(context, 'POST', `${CONTAINERS}/${containerId}/ocean-booking-link`, {
|
||||
realContainerBookingId: id(requirement.containerBookingId), expectedVersion: Number(simulationBeforeLink.version),
|
||||
oceanBookingId: bookingId, requirementId: id(requirement.id), containerNo: realContainerNo,
|
||||
expectedVersion: Number(simulationBeforeLink.version),
|
||||
})
|
||||
const linkedContainer = context.apiSuccess(MODULE, '首次关联真实已确认订舱柜', linkResponse,
|
||||
const linkedContainer = context.apiSuccess(MODULE, '首次关联 RELEASED 订舱需求并补真实柜号', linkResponse,
|
||||
responseEvidence(linkResponse), OPTIONS)
|
||||
if (!linkedContainer) return
|
||||
const afterLinkPlanResponse = await context.request('GET', `${SMART}/plans/${planId}`)
|
||||
@@ -426,24 +427,23 @@ export async function run(context) {
|
||||
|
||||
const activeContainersResponse = await context.request('GET',
|
||||
`${CONTAINERS}?page=1&size=50&keyword=${encodeURIComponent(realContainerNo)}`)
|
||||
const activeContainers = context.apiSuccess(MODULE, '合并后按真实柜号回查正常货柜列表', activeContainersResponse, '', OPTIONS)
|
||||
const activeContainers = context.apiSuccess(MODULE, '关联后按真实柜号回查正常货柜列表', activeContainersResponse, '', OPTIONS)
|
||||
const activeRealRows = records(activeContainers).filter((item) => item.containerNo === realContainerNo)
|
||||
context.assert(MODULE, '合并后列表仅保留模拟柜原地转成的一条 ACTIVE 操作身份', activeContainersResponse,
|
||||
activeRealRows.length === 1 && id(activeRealRows[0]?.id) === containerId
|
||||
&& !records(activeContainers).some((item) => id(item?.id) === id(requirement.containerBookingId)),
|
||||
`activeIds=${activeRealRows.map((item) => id(item?.id)).join('|') || 'none'}, mergedSourceId=${id(requirement.containerBookingId)}`, OPTIONS)
|
||||
context.assert(MODULE, '关联后列表仅保留模拟柜原地补全的一条 ACTIVE 操作身份', activeContainersResponse,
|
||||
activeRealRows.length === 1 && id(activeRealRows[0]?.id) === containerId,
|
||||
`activeIds=${activeRealRows.map((item) => id(item?.id)).join('|') || 'none'}`, OPTIONS)
|
||||
const containerGaps = containerFieldGaps(activeRealRows[0], { requireContainerNo: true })
|
||||
context.assert(MODULE, '货柜列表关键展示字段来自正式柜次快照且格式完整', activeContainersResponse,
|
||||
activeRealRows.length === 1 && containerGaps.length === 0,
|
||||
`missing=${containerGaps.join(',') || 'none'}`, OPTIONS)
|
||||
|
||||
const mergedBookingResponse = await context.request('GET', `/api/platform/v1/ocean-bookings/${bookingId}`)
|
||||
const mergedBooking = context.apiSuccess(MODULE, '合并后回查订舱真实柜当前操作身份', mergedBookingResponse, '', OPTIONS)
|
||||
const mergedBooking = context.apiSuccess(MODULE, '关联后回查订舱 allocation 当前操作身份', mergedBookingResponse, '', OPTIONS)
|
||||
const mergedContainer = mergedBooking?.containers?.find?.((item) => item.containerNo === realContainerNo)
|
||||
context.assert(MODULE, '订舱真实柜 allocation 已原子改指稳定模拟柜身份', mergedBookingResponse,
|
||||
context.assert(MODULE, '订舱 allocation 原子创建并指向稳定模拟柜身份', mergedBookingResponse,
|
||||
id(mergedContainer?.containerBookingId) === containerId
|
||||
&& id(mergedContainer?.requirementId) === id(requirement.requirementId)
|
||||
&& id(mergedContainer?.allocationId) === id(requirement.allocationId),
|
||||
&& id(mergedContainer?.requirementId) === id(requirement.id)
|
||||
&& Boolean(mergedContainer?.allocationId),
|
||||
`containerBookingId=${id(mergedContainer?.containerBookingId) || 'missing'}, requirementId=${id(mergedContainer?.requirementId) || 'missing'}, allocationId=${id(mergedContainer?.allocationId) || 'missing'}`, OPTIONS)
|
||||
|
||||
const duplicateSimulationResponse = await request(context, 'POST', CONTAINERS, {
|
||||
@@ -452,9 +452,10 @@ export async function run(context) {
|
||||
})
|
||||
const duplicateSimulation = context.apiSuccess(MODULE, '新增重复容量占用负向模拟柜', duplicateSimulationResponse, '', OPTIONS)
|
||||
const duplicateLinkResponse = await request(context, 'POST', `${CONTAINERS}/${id(duplicateSimulation?.id)}/ocean-booking-link`, {
|
||||
realContainerBookingId: id(requirement.containerBookingId), expectedVersion: Number(duplicateSimulation?.version),
|
||||
oceanBookingId: bookingId, requirementId: id(requirement.id), containerNo: realContainerNo,
|
||||
expectedVersion: Number(duplicateSimulation?.version),
|
||||
})
|
||||
context.addStep(MODULE, '已消费 MERGED 真实柜拒绝再次关联', duplicateLinkResponse,
|
||||
context.addStep(MODULE, '已消费订舱需求拒绝再次关联', duplicateLinkResponse,
|
||||
failure(duplicateLinkResponse), `HTTP=${duplicateLinkResponse.http}, code=${codeOf(duplicateLinkResponse)}`, OPTIONS)
|
||||
|
||||
const unlinkAfterLoadingResponse = await request(context, 'DELETE', `${CONTAINERS}/${containerId}/ocean-booking-link`, {
|
||||
|
||||
@@ -136,7 +136,7 @@ export async function run(context) {
|
||||
parties: [{ partyType: 'SHIPPER', partyName: `P3-SHIPPER-${suffix}` },
|
||||
{ partyType: 'CONSIGNEE', partyName: `P3-CONSIGNEE-${suffix}` },
|
||||
{ partyType: 'NOTIFY_PARTY', partyName: `P3-NOTIFY-${suffix}` }],
|
||||
containers: [{ containerNo, containerType: '40HC', containerKind: 'FCL', capacityCbm: 76,
|
||||
containerRequirements: [{ containerType: '40HC', containerKind: 'FCL', quantity: 1, capacityCbm: 76,
|
||||
maxWeightKg: 28000 }],
|
||||
}
|
||||
const simulationResponse = await request(context, 'POST', '/api/platform/v1/containers', {
|
||||
@@ -287,49 +287,41 @@ export async function run(context) {
|
||||
const bookingCreateResponse = await request(context, 'POST', '/api/platform/v1/ocean-bookings',
|
||||
{ expectedVersion: 0, data: bookingDraft },
|
||||
{ 'Idempotency-Key': `${context.marker}-p3-booking-create`.slice(0, 128) })
|
||||
const bookingConfirmed = context.apiSuccess(MODULE, '创建带渠道仓库快照的三期 CONFIRMED 订舱', bookingCreateResponse, '', OPTIONS)
|
||||
const bookingId = id(bookingConfirmed?.oceanBooking?.id)
|
||||
const bookingDraftResponse = context.apiSuccess(MODULE, '创建三期 DRAFT 订舱(不生成真实柜)', bookingCreateResponse, '', OPTIONS)
|
||||
const bookingId = id(bookingDraftResponse?.oceanBooking?.id)
|
||||
const draftVersion = Number(bookingDraftResponse?.oceanBooking?.version)
|
||||
const releaseResponse = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${bookingId}/release`,
|
||||
{ expectedVersion: draftVersion, reason: context.marker },
|
||||
{ 'Idempotency-Key': `${context.marker}-p3-booking-release`.slice(0, 128) })
|
||||
const bookingConfirmed = context.apiSuccess(MODULE, '三期订舱 RELEASED 冻结运输快照', releaseResponse, '', OPTIONS)
|
||||
const transportFields = ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']
|
||||
const frozenTransport = Object.fromEntries(transportFields.map((field) => [field, bookingConfirmed?.oceanBooking?.[field]]))
|
||||
const frozenTransportValid = transportFields.every((field) => frozenTransport[field] != null && String(frozenTransport[field]).trim() !== '')
|
||||
const frozenTransportMatches = frozenTransportValid
|
||||
&& transportFields.every((field) => String(frozenTransport[field]) === String(bookingDraft[field]))
|
||||
const requirement = bookingConfirmed?.containers?.find?.((item) => item.containerNo === containerNo)
|
||||
context.assert(MODULE, 'CONFIRMED 订舱回显完整且冻结五项运输快照', bookingCreateResponse,
|
||||
Boolean(bookingId) && frozenTransportMatches && Boolean(requirement?.containerBookingId)
|
||||
&& bookingConfirmed?.containers?.length === 1,
|
||||
const requirement = bookingConfirmed?.containerRequirements?.find?.((item) => item.containerType === '40HC')
|
||||
context.assert(MODULE, 'RELEASED 订舱回显完整冻结运输快照且无真实柜号', releaseResponse,
|
||||
Boolean(bookingId) && bookingConfirmed?.oceanBooking?.status === 'RELEASED' && frozenTransportMatches
|
||||
&& Boolean(requirement?.id) && (!bookingConfirmed?.containers || bookingConfirmed.containers.length === 0),
|
||||
`bookingId=${bookingId || 'missing'}, status=${bookingConfirmed?.oceanBooking?.status || 'missing'}`, OPTIONS)
|
||||
if (!bookingId || !requirement?.containerBookingId || !frozenTransportMatches) return
|
||||
if (!bookingId || !requirement?.id || !frozenTransportMatches) return
|
||||
const latestContainerResponse = await context.request('GET', `/api/platform/v1/containers/${containerBookingId}`)
|
||||
const latestContainer = context.apiSuccess(MODULE, '合并前读取模拟柜最新版本', latestContainerResponse, '', OPTIONS)
|
||||
const linkResponse = await request(context, 'POST', `/api/platform/v1/containers/${containerBookingId}/ocean-booking-link`, {
|
||||
realContainerBookingId: id(requirement.containerBookingId), expectedVersion: Number(latestContainer?.version),
|
||||
oceanBookingId: bookingId, requirementId: id(requirement.id), containerNo,
|
||||
expectedVersion: Number(latestContainer?.version),
|
||||
})
|
||||
const linked = context.apiSuccess(MODULE, '模拟柜原地合并三期 CONFIRMED 真实订舱柜', linkResponse, '', OPTIONS)
|
||||
const linked = context.apiSuccess(MODULE, '模拟柜原地关联三期 RELEASED 订舱需求并补真实柜号', linkResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '订舱关联保持柜次计划与真实柜号身份', linkResponse,
|
||||
id(linked?.id) === containerBookingId && linked?.cabinetNo === simulation.cabinetNo
|
||||
&& linked?.containerNo === containerNo && id(linked?.oceanBookingId) === bookingId,
|
||||
`containerBookingId=${containerBookingId}, loadingPlanId=${loadingPlanId}`, OPTIONS)
|
||||
if (id(linked?.id) !== containerBookingId) return
|
||||
|
||||
const confirmedBookingTaskResponse = await context.request('GET',
|
||||
`${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
context.addStep(MODULE, '订舱已关联但未 RELEASED 仍无 PDA 任务', confirmedBookingTaskResponse,
|
||||
confirmedBookingTaskResponse.http === 200 && codeOf(confirmedBookingTaskResponse) === 'A0404',
|
||||
`HTTP=${confirmedBookingTaskResponse.http}, code=${codeOf(confirmedBookingTaskResponse)}`, OPTIONS)
|
||||
|
||||
const releaseResponse = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${bookingId}/release`, {
|
||||
expectedVersion: Number(bookingConfirmed?.oceanBooking?.version),
|
||||
...frozenTransport, reason: context.marker,
|
||||
}, { 'Idempotency-Key': `${context.marker}-p3-booking-release`.slice(0, 128) })
|
||||
const bookingReleased = context.apiSuccess(MODULE, '三期订舱携带已冻结运输快照并放舱', releaseResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '三期订舱 RELEASED 后保持模拟柜关联身份', releaseResponse,
|
||||
bookingReleased?.oceanBooking?.status === 'RELEASED'
|
||||
&& bookingReleased?.containers?.some?.((item) => id(item.containerBookingId) === containerBookingId
|
||||
&& item.containerNo === containerNo)
|
||||
&& transportFields.every((field) => String(bookingReleased?.oceanBooking?.[field]) === String(frozenTransport[field])),
|
||||
`status=${bookingReleased?.oceanBooking?.status || 'missing'}, containerBookingId=${containerBookingId}`, OPTIONS)
|
||||
if (bookingReleased?.oceanBooking?.status !== 'RELEASED') return
|
||||
context.assert(MODULE, '三期订舱 RELEASED 后保持模拟柜关联身份和运输快照', releaseResponse,
|
||||
bookingConfirmed?.oceanBooking?.status === 'RELEASED'
|
||||
&& id(linked?.oceanBookingId) === bookingId && linked?.containerNo === containerNo,
|
||||
`status=${bookingConfirmed?.oceanBooking?.status || 'missing'}, containerBookingId=${containerBookingId}`, OPTIONS)
|
||||
|
||||
const unconfirmedTaskResponse = await context.request('GET',
|
||||
`${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
|
||||
Reference in New Issue
Block a user