fix: align phase4 receiving and carrier fixtures
This commit is contained in:
-9
@@ -449,15 +449,6 @@ public class ReceivingContainerService {
|
|||||||
if (pallet.fbaWarehouseCode() != null && !pallet.fbaWarehouseCode().isBlank()) {
|
if (pallet.fbaWarehouseCode() != null && !pallet.fbaWarehouseCode().isBlank()) {
|
||||||
throw conflict("FBA-designated pallet cannot receive an OVERSEAS destination carton");
|
throw conflict("FBA-designated pallet cannot receive an OVERSEAS destination carton");
|
||||||
}
|
}
|
||||||
if (ref.line().getDestinationWarehouseId() != null && !ref.line().getDestinationWarehouseId().isBlank()) {
|
|
||||||
try {
|
|
||||||
if (Long.parseLong(ref.line().getDestinationWarehouseId()) != pallet.warehouseId()) {
|
|
||||||
throw conflict("Pallet destination warehouse does not match the inbound code");
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
// Destination codes that are not resource IDs are still protected by the actual overseas warehouse check.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
@@ -224,6 +224,22 @@ class ReceivingContainerServiceBehaviorTest {
|
|||||||
verify(workflow, never()).assignResolvedPallet(any(), any(), any(), any());
|
verify(workflow, never()).assignResolvedPallet(any(), any(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void overseasPalletUsesActualReceivingWarehouseInsteadOfBusinessDestinationId() {
|
||||||
|
stubCurrent("OVERSEAS", "CONT-A");
|
||||||
|
InboundPlanLine overseasLine = line("OVERSEAS");
|
||||||
|
overseasLine.setDestinationWarehouseId("900");
|
||||||
|
when(lines.selectList(any())).thenReturn(List.of(overseasLine));
|
||||||
|
when(warehouse.resolvePallet("PAL-1")).thenReturn(resource(100L, null));
|
||||||
|
|
||||||
|
var resolved = service.resolvePallet("7",
|
||||||
|
new ReceivingContainerRequests.PalletResolve("PAL-1", "IN-1"));
|
||||||
|
|
||||||
|
assertThat(resolved.compatible()).isTrue();
|
||||||
|
assertThat(resolved.warehouseId()).isEqualTo(100L);
|
||||||
|
assertThat(resolved.destinationType()).isEqualTo("OVERSEAS");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void containerCompletionRequiresEveryInboundCodeToBalanceExactly() {
|
void containerCompletionRequiresEveryInboundCodeToBalanceExactly() {
|
||||||
stubCurrent("FBA", "CONT-A");
|
stubCurrent("FBA", "CONT-A");
|
||||||
|
|||||||
@@ -47,4 +47,6 @@ test('outbound lifecycle checks replay, hash conflict, stale version and final r
|
|||||||
'负向与幂等验证后回查出仓聚合',
|
'负向与幂等验证后回查出仓聚合',
|
||||||
'出仓聚合保持 READY_FOR_DISPATCH、LOADED 和确认撤销历史',
|
'出仓聚合保持 READY_FOR_DISPATCH、LOADED 和确认撤销历史',
|
||||||
]) assert.match(source, new RegExp(step))
|
]) assert.match(source, new RegExp(step))
|
||||||
|
assert.match(source, /if \(!confirmed\?\.aggregate\) return/)
|
||||||
|
assert.match(source, /hashConflict\.http === 200 && codeOf\(hashConflict\) === 'A0409'/)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -138,12 +138,22 @@ export async function run(context) {
|
|||||||
context.assert(MODULE, '存在可用于发布订舱的 ACTIVE 订舱代理', supplierResponse,
|
context.assert(MODULE, '存在可用于发布订舱的 ACTIVE 订舱代理', supplierResponse,
|
||||||
Boolean(supplier?.id && supplier?.name), `supplierId=${id(supplier?.id) || 'missing'}`, OPTIONS)
|
Boolean(supplier?.id && supplier?.name), `supplierId=${id(supplier?.id) || 'missing'}`, OPTIONS)
|
||||||
if (!supplier?.id) return
|
if (!supplier?.id) return
|
||||||
|
const carrierResponse = await context.request('GET',
|
||||||
|
'/api/platform/v1/carrier-schedule-queries/carriers')
|
||||||
|
const carriers = context.apiSuccess(MODULE, '读取船节点canonical船司目录', carrierResponse, '', OPTIONS)
|
||||||
|
const shippingCarrier = Array.isArray(carriers)
|
||||||
|
? carriers.find((value) => value?.carrierCode === 'MATSON' && value?.carrierName)
|
||||||
|
: null
|
||||||
|
context.assert(MODULE, '订舱船司与订舱代理使用各自真实事实源', carrierResponse,
|
||||||
|
Boolean(shippingCarrier?.carrierName),
|
||||||
|
`carrierCode=${shippingCarrier?.carrierCode || 'missing'}, bookingAgentId=${id(supplier.id)}`, OPTIONS)
|
||||||
|
if (!shippingCarrier?.carrierName) return
|
||||||
const bookingDraft = {
|
const bookingDraft = {
|
||||||
customerId: tenantId, customerName: initial?.summary?.customerName,
|
customerId: tenantId, customerName: initial?.summary?.customerName,
|
||||||
warehouseId,
|
warehouseId,
|
||||||
channelCode: initial?.summary?.channelCode,
|
channelCode: initial?.summary?.channelCode,
|
||||||
billOfLadingNo: `P3BL-${suffix}`.slice(0, 128),
|
billOfLadingNo: `P3BL-${suffix}`.slice(0, 128),
|
||||||
shippingCompanyId: id(supplier.id), shippingCompanyName: supplier.name,
|
shippingCompanyId: null, shippingCompanyName: shippingCarrier.carrierName,
|
||||||
bookingAgentId: id(supplier.id), bookingAgentName: supplier.name,
|
bookingAgentId: id(supplier.id), bookingAgentName: supplier.name,
|
||||||
vesselName: `P3VESSEL-${suffix}`.slice(0, 128), voyageNo: `P3V-${suffix}`.slice(0, 64),
|
vesselName: `P3VESSEL-${suffix}`.slice(0, 128), voyageNo: `P3V-${suffix}`.slice(0, 64),
|
||||||
departurePortCode: 'CNSZX', departurePortName: '深圳', destinationPortCode: 'USLAX',
|
departurePortCode: 'CNSZX', departurePortName: '深圳', destinationPortCode: 'USLAX',
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export async function run(context) {
|
|||||||
&& confirmed?.aggregate?.inboundStatus === 'LOADED'
|
&& confirmed?.aggregate?.inboundStatus === 'LOADED'
|
||||||
&& Number(confirmed?.aggregate?.version) === expectedVersion + 1,
|
&& Number(confirmed?.aggregate?.version) === expectedVersion + 1,
|
||||||
`dispatch=${confirmed?.aggregate?.dispatchStatus || 'missing'}, inbound=${confirmed?.aggregate?.inboundStatus || 'missing'}, version=${confirmed?.aggregate?.version ?? 'missing'}`, OPTIONS)
|
`dispatch=${confirmed?.aggregate?.dispatchStatus || 'missing'}, inbound=${confirmed?.aggregate?.inboundStatus || 'missing'}, version=${confirmed?.aggregate?.version ?? 'missing'}`, OPTIONS)
|
||||||
|
if (!confirmed?.aggregate) return
|
||||||
|
|
||||||
const replayResponse = await request(context, 'POST', `${BASE}/confirm-dispatch`, actionBody,
|
const replayResponse = await request(context, 'POST', `${BASE}/confirm-dispatch`, actionBody,
|
||||||
{ 'Idempotency-Key': confirmKey })
|
{ 'Idempotency-Key': confirmKey })
|
||||||
@@ -118,7 +119,8 @@ export async function run(context) {
|
|||||||
|
|
||||||
const hashConflict = await request(context, 'POST', `${BASE}/confirm-dispatch`, { ...actionBody, remark: 'forged' },
|
const hashConflict = await request(context, 'POST', `${BASE}/confirm-dispatch`, { ...actionBody, remark: 'forged' },
|
||||||
{ 'Idempotency-Key': confirmKey })
|
{ 'Idempotency-Key': confirmKey })
|
||||||
context.addStep(MODULE, '相同 Idempotency-Key 不同请求拒绝', hashConflict, rejected(hashConflict),
|
context.addStep(MODULE, '相同 Idempotency-Key 不同请求拒绝', hashConflict,
|
||||||
|
hashConflict.http === 200 && codeOf(hashConflict) === 'A0409',
|
||||||
`HTTP=${hashConflict.http}, code=${codeOf(hashConflict)}`, OPTIONS)
|
`HTTP=${hashConflict.http}, code=${codeOf(hashConflict)}`, OPTIONS)
|
||||||
|
|
||||||
const reverseKey = `${context.marker}-outbound-reverse`.slice(0, 128)
|
const reverseKey = `${context.marker}-outbound-reverse`.slice(0, 128)
|
||||||
|
|||||||
@@ -395,6 +395,11 @@ test('preload and loading versions use immediate operation baselines and exact p
|
|||||||
assert.equal((loading.match(/request\(context, 'POST', pushRequest\.path, pushRequest\.body, pushRequest\.headers\)/g) || []).length, 2)
|
assert.equal((loading.match(/request\(context, 'POST', pushRequest\.path, pushRequest\.body, pushRequest\.headers\)/g) || []).length, 2)
|
||||||
assert.match(loading, /push exact replay 对计划版本历史关联和任务零漂移/)
|
assert.match(loading, /push exact replay 对计划版本历史关联和任务零漂移/)
|
||||||
assert.match(loading, /Number\(claimedPlan\?\.version\) > beforeClaimPlanVersion/)
|
assert.match(loading, /Number\(claimedPlan\?\.version\) > beforeClaimPlanVersion/)
|
||||||
|
assert.match(loading, /carrier-schedule-queries\/carriers/)
|
||||||
|
assert.match(loading, /carrierCode === 'MATSON'/)
|
||||||
|
assert.match(loading, /shippingCompanyId: null, shippingCompanyName: shippingCarrier\.carrierName/)
|
||||||
|
assert.match(loading, /bookingAgentId: id\(supplier\.id\), bookingAgentName: supplier\.name/)
|
||||||
|
assert.doesNotMatch(loading, /shippingCompanyId: id\(supplier\.id\), shippingCompanyName: supplier\.name/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('PDA completion acceptance verifies immutable BATCH and COMPLETE_LOADING audit with operator metadata', () => {
|
test('PDA completion acceptance verifies immutable BATCH and COMPLETE_LOADING audit with operator metadata', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user