优化
This commit is contained in:
@@ -450,9 +450,9 @@ function hasFields(value, fields) { return fields.every((field) => own(value, fi
|
|||||||
function numberEquals(actual, expected) { return Math.abs(Number(actual) - Number(expected)) < 0.0005 }
|
function numberEquals(actual, expected) { return Math.abs(Number(actual) - Number(expected)) < 0.0005 }
|
||||||
function halfUp3(value) { return Math.round((Number(value) + Number.EPSILON) * 1000) / 1000 }
|
function halfUp3(value) { return Math.round((Number(value) + Number.EPSILON) * 1000) / 1000 }
|
||||||
function derived(length, width, height, edge) {
|
function derived(length, width, height, edge) {
|
||||||
const outerLength = Number(length) + Number(edge) * 2
|
const outerLength = Number(length) + Number(edge)
|
||||||
const outerWidth = Number(width) + Number(edge) * 2
|
const outerWidth = Number(width) + Number(edge)
|
||||||
const outerHeight = Number(height) + Number(edge) * 2
|
const outerHeight = Number(height) + Number(edge)
|
||||||
return { outerLength, outerWidth, outerHeight, volume: halfUp3(outerLength * outerWidth * outerHeight / 1_000_000) }
|
return { outerLength, outerWidth, outerHeight, volume: halfUp3(outerLength * outerWidth * outerHeight / 1_000_000) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,10 +779,10 @@ export class AcceptanceRun {
|
|||||||
|
|
||||||
waitSplitVariant(sequence) {
|
waitSplitVariant(sequence) {
|
||||||
const dimensionProfiles = [
|
const dimensionProfiles = [
|
||||||
{ code: 'SMALL', first: { length: 29.1, width: 19.1, height: 18.1 }, replacement: { length: 29.5, width: 19.5, height: 18.5 }, outer: { length: 34, width: 24, height: 24 } },
|
{ code: 'SMALL', first: { length: 29.1, width: 19.1, height: 18.1 }, replacement: { length: 29.5, width: 19.5, height: 18.5 } },
|
||||||
{ code: 'MEDIUM', first: { length: 39.1, width: 29.1, height: 28.1 }, replacement: { length: 39.5, width: 29.5, height: 28.5 }, outer: { length: 44, width: 34, height: 34 } },
|
{ code: 'MEDIUM', first: { length: 39.1, width: 29.1, height: 28.1 }, replacement: { length: 39.5, width: 29.5, height: 28.5 } },
|
||||||
{ code: 'LARGE', first: { length: 49.1, width: 39.1, height: 38.1 }, replacement: { length: 49.5, width: 39.5, height: 38.5 }, outer: { length: 54, width: 44, height: 44 } },
|
{ code: 'LARGE', first: { length: 49.1, width: 39.1, height: 38.1 }, replacement: { length: 49.5, width: 39.5, height: 38.5 } },
|
||||||
{ code: 'EXTRA_LARGE', first: { length: 59.1, width: 49.1, height: 48.1 }, replacement: { length: 59.5, width: 49.5, height: 48.5 }, outer: { length: 64, width: 54, height: 54 } },
|
{ code: 'EXTRA_LARGE', first: { length: 59.1, width: 49.1, height: 48.1 }, replacement: { length: 59.5, width: 49.5, height: 48.5 } },
|
||||||
]
|
]
|
||||||
const randomValue = this.options.waitSplitRandomValue || sequence
|
const randomValue = this.options.waitSplitRandomValue || sequence
|
||||||
const requestedTotal = Number(this.options.waitSplitPackageCount || 0)
|
const requestedTotal = Number(this.options.waitSplitPackageCount || 0)
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ async function createRouteVariantFixture(context, base, variant) {
|
|||||||
sequence: 90 + attempt, skuCount: 4, packageCountPerSku: 1, packageCounts: [1, 1, 1, 1],
|
sequence: 90 + attempt, skuCount: 4, packageCountPerSku: 1, packageCounts: [1, 1, 1, 1],
|
||||||
totalPackageCount: 4, palletCount: 1, grossWeightPerPalletKg: 45,
|
totalPackageCount: 4, palletCount: 1, grossWeightPerPalletKg: 45,
|
||||||
measurementProfile: { code: 'SMALL', first: { length: 29.1, width: 19.1, height: 18.1 },
|
measurementProfile: { code: 'SMALL', first: { length: 29.1, width: 19.1, height: 18.1 },
|
||||||
replacement: { length: 29.5, width: 19.5, height: 18.5 }, outer: { length: 34, width: 24, height: 24 } },
|
replacement: { length: 29.5, width: 19.5, height: 18.5 } },
|
||||||
}
|
}
|
||||||
context.context.phase3LoadingIncrementalDetailCount = 4
|
context.context.phase3LoadingIncrementalDetailCount = 4
|
||||||
context.context.phase3LoadingIncrementalPackageCount = 1
|
context.context.phase3LoadingIncrementalPackageCount = 1
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ test('volume ceiling removes binary floating point noise before scaling', () =>
|
|||||||
assert.match(source, /Number\.EPSILON \* Math\.max\(1, Math\.abs\(value\)\) \* 4/)
|
assert.match(source, /Number\.EPSILON \* Math\.max\(1, Math\.abs\(value\)\) \* 4/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('measurement draft lets the server apply each dimension addition exactly once', () => {
|
||||||
|
assert.match(source, /length: replacement\.length \+ dimensionAddition/)
|
||||||
|
assert.match(source, /coefficientMode: 'ADD'/)
|
||||||
|
assert.match(source, /measuredItem\.outerLengthCm == null/)
|
||||||
|
assert.doesNotMatch(source, /outerLengthCm: item\.outerLengthCm/)
|
||||||
|
assert.doesNotMatch(source, /outerLengthCm: measuredItem\.outerLengthCm/)
|
||||||
|
assert.match(cartonTests, /getOuterLengthCm\(\)\)\.isEqualByComparingTo\("62\.000"\)/)
|
||||||
|
})
|
||||||
|
|
||||||
test('demo package freezes repeated label printing and Mojia dimension replacement', () => {
|
test('demo package freezes repeated label printing and Mojia dimension replacement', () => {
|
||||||
assert.match(labelTests, /repeatedAndPartialPrintsReadFrozenMappingWithoutReallocation/)
|
assert.match(labelTests, /repeatedAndPartialPrintsReadFrozenMappingWithoutReallocation/)
|
||||||
assert.match(labelTests, /changedCargoKeepsTheFirstFrozenBoxCodes/)
|
assert.match(labelTests, /changedCargoKeepsTheFirstFrozenBoxCodes/)
|
||||||
|
|||||||
@@ -510,24 +510,43 @@ export async function run(context) {
|
|||||||
let measuredResponse = await context.request('GET', measurementPath)
|
let measuredResponse = await context.request('GET', measurementPath)
|
||||||
let measured = context.apiSuccess(MODULE, '回查覆盖后的尺寸与体积结果', measuredResponse, '', OPTIONS)
|
let measured = context.apiSuccess(MODULE, '回查覆盖后的尺寸与体积结果', measuredResponse, '', OPTIONS)
|
||||||
let measuredItem = measured?.items?.find((item) => asId(item.id) === asId(firstScan.itemId))
|
let measuredItem = measured?.items?.find((item) => asId(item.id) === asId(firstScan.itemId))
|
||||||
const weightedOuter = measurementProfile?.outer || { length: 64, width: 54, height: 54 }
|
const deviceOuterByType = {
|
||||||
const unitVolume = halfUp(
|
SMALL: { length: 32, width: 22, height: 22 },
|
||||||
weightedOuter.length * weightedOuter.width * weightedOuter.height / 1_000_000, 3)
|
MEDIUM: { length: 42, width: 32, height: 32 },
|
||||||
const totalVolume = unitVolume * originalPackageCount
|
LARGE: { length: 52, width: 42, height: 42 },
|
||||||
const volumeWeight = ceiling(totalVolume * 167, 3)
|
EXTRA_LARGE: { length: 62, width: 52, height: 52 },
|
||||||
const girth = ceiling(replacement.length + 2 * (replacement.width + replacement.height), 3)
|
}
|
||||||
|
const deviceOuter = deviceOuterByType[measurementProfile?.code] || deviceOuterByType.EXTRA_LARGE
|
||||||
|
const deviceUnitVolume = halfUp(
|
||||||
|
deviceOuter.length * deviceOuter.width * deviceOuter.height / 1_000_000, 3)
|
||||||
|
const deviceTotalVolume = deviceUnitVolume * originalPackageCount
|
||||||
|
const deviceVolumeWeight = ceiling(deviceTotalVolume * 167, 3)
|
||||||
|
const deviceGirth = ceiling(replacement.length + 2 * (replacement.width + replacement.height), 3)
|
||||||
context.assert(MODULE, '覆盖尺寸、箱数和服务端体积公式正确', measuredResponse,
|
context.assert(MODULE, '覆盖尺寸、箱数和服务端体积公式正确', measuredResponse,
|
||||||
measuredItem && closeTo(measuredItem.lengthCm, replacement.length)
|
measuredItem && closeTo(measuredItem.lengthCm, replacement.length)
|
||||||
&& closeTo(measuredItem.widthCm, replacement.width) && closeTo(measuredItem.heightCm, replacement.height)
|
&& closeTo(measuredItem.widthCm, replacement.width) && closeTo(measuredItem.heightCm, replacement.height)
|
||||||
&& measuredItem.scannedShipmentId === replacementShipmentId
|
&& measuredItem.scannedShipmentId === replacementShipmentId
|
||||||
&& Number(measuredItem.confirmedPackageCount) === originalPackageCount
|
&& Number(measuredItem.confirmedPackageCount) === originalPackageCount
|
||||||
&& closeTo(measuredItem.outerLengthCm, weightedOuter.length)
|
&& measuredItem.outerLengthCm == null && measuredItem.outerWidthCm == null
|
||||||
&& closeTo(measuredItem.outerWidthCm, weightedOuter.width)
|
&& measuredItem.outerHeightCm == null
|
||||||
&& closeTo(measuredItem.outerHeightCm, weightedOuter.height)
|
&& closeTo(measuredItem.unitVolumeCbm, deviceUnitVolume)
|
||||||
&& closeTo(measuredItem.unitVolumeCbm, unitVolume) && closeTo(measuredItem.totalVolumeCbm, totalVolume)
|
&& closeTo(measuredItem.totalVolumeCbm, deviceTotalVolume)
|
||||||
&& closeTo(measuredItem.volumeWeightKg, volumeWeight) && closeTo(measuredItem.girthCm, girth)
|
&& closeTo(measuredItem.volumeWeightKg, deviceVolumeWeight) && closeTo(measuredItem.girthCm, deviceGirth)
|
||||||
&& closeTo(measured.actualVolumeCbm, totalVolume) && closeTo(measured.volumeWeightKg, volumeWeight),
|
&& closeTo(measured.actualVolumeCbm, deviceTotalVolume)
|
||||||
`weightedOuter=${weightedOuter.length}x${weightedOuter.width}x${weightedOuter.height}, unitVolume=${unitVolume}, totalVolume=${totalVolume}, volumeWeight=${volumeWeight}, girth=${girth}`, OPTIONS)
|
&& closeTo(measured.volumeWeightKg, deviceVolumeWeight),
|
||||||
|
`expectedOuter=${deviceOuter.length}x${deviceOuter.width}x${deviceOuter.height}, actualOuter=${measuredItem?.outerLengthCm ?? 'missing'}x${measuredItem?.outerWidthCm ?? 'missing'}x${measuredItem?.outerHeightCm ?? 'missing'}, expectedUnitVolume=${deviceUnitVolume}, actualUnitVolume=${measuredItem?.unitVolumeCbm ?? 'missing'}, expectedTotalVolume=${deviceTotalVolume}, actualTotalVolume=${measuredItem?.totalVolumeCbm ?? 'missing'}, expectedVolumeWeight=${deviceVolumeWeight}, actualVolumeWeight=${measuredItem?.volumeWeightKg ?? 'missing'}, expectedGirth=${deviceGirth}, actualGirth=${measuredItem?.girthCm ?? 'missing'}`, OPTIONS)
|
||||||
|
|
||||||
|
const dimensionAddition = 2
|
||||||
|
const weightedOuter = {
|
||||||
|
length: replacement.length + dimensionAddition,
|
||||||
|
width: replacement.width + dimensionAddition,
|
||||||
|
height: replacement.height + dimensionAddition,
|
||||||
|
}
|
||||||
|
const unitVolume = halfUp(
|
||||||
|
weightedOuter.length * weightedOuter.width * weightedOuter.height / 1_000_000, 3)
|
||||||
|
const totalVolume = unitVolume * originalPackageCount
|
||||||
|
const volumeWeight = ceiling(totalVolume * 167, 3)
|
||||||
|
const girth = ceiling(weightedOuter.length + 2 * (weightedOuter.width + weightedOuter.height), 3)
|
||||||
|
|
||||||
const incrementalDetailCount = Number(context.context?.phase3LoadingIncrementalDetailCount || 1)
|
const incrementalDetailCount = Number(context.context?.phase3LoadingIncrementalDetailCount || 1)
|
||||||
if (incrementalDetailCount > 1) {
|
if (incrementalDetailCount > 1) {
|
||||||
@@ -625,18 +644,14 @@ export async function run(context) {
|
|||||||
boxSpecification: item.boxSpecification, packageCount: Number(item.confirmedPackageCount),
|
boxSpecification: item.boxSpecification, packageCount: Number(item.confirmedPackageCount),
|
||||||
lengthCm: Number(item.lengthCm), widthCm: Number(item.widthCm), heightCm: Number(item.heightCm),
|
lengthCm: Number(item.lengthCm), widthCm: Number(item.widthCm), heightCm: Number(item.heightCm),
|
||||||
intelligentImageFileId: item.intelligentImageFileId,
|
intelligentImageFileId: item.intelligentImageFileId,
|
||||||
overLimitWarning: item.overLimitWarning ?? null,
|
overLimitWarning: item.overLimitWarning ?? null, coefficientMode: 'ADD', remark: context.marker,
|
||||||
outerLengthCm: item.outerLengthCm ?? null, outerWidthCm: item.outerWidthCm ?? null,
|
|
||||||
outerHeightCm: item.outerHeightCm ?? null, remark: context.marker,
|
|
||||||
}))
|
}))
|
||||||
: [{
|
: [{
|
||||||
inboundDetailId, itemNo: measuredItem.itemNo,
|
inboundDetailId, itemNo: measuredItem.itemNo,
|
||||||
boxSpecification: measuredItem.boxSpecification, packageCount: originalPackageCount,
|
boxSpecification: measuredItem.boxSpecification, packageCount: originalPackageCount,
|
||||||
lengthCm: replacement.length, widthCm: replacement.width, heightCm: replacement.height,
|
lengthCm: replacement.length, widthCm: replacement.width, heightCm: replacement.height,
|
||||||
intelligentImageFileId: measuredItem.intelligentImageFileId,
|
intelligentImageFileId: measuredItem.intelligentImageFileId,
|
||||||
overLimitWarning: measuredItem.overLimitWarning ?? null,
|
overLimitWarning: measuredItem.overLimitWarning ?? null, coefficientMode: 'ADD', remark: context.marker,
|
||||||
outerLengthCm: measuredItem.outerLengthCm ?? null, outerWidthCm: measuredItem.outerWidthCm ?? null,
|
|
||||||
outerHeightCm: measuredItem.outerHeightCm ?? null, remark: context.marker,
|
|
||||||
}]
|
}]
|
||||||
const draftPayload = {
|
const draftPayload = {
|
||||||
measurementId: measured.measurementId, version: Number(measured.version),
|
measurementId: measured.measurementId, version: Number(measured.version),
|
||||||
|
|||||||
@@ -47,9 +47,10 @@ test('approval convergence is a hard WAIT_MEASURE prerequisite without coverage
|
|||||||
assert.doesNotMatch(source, /审核后入库单与履约任务异步收敛[\s\S]{0,300}addCoverageGap/)
|
assert.doesNotMatch(source, /审核后入库单与履约任务异步收敛[\s\S]{0,300}addCoverageGap/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('post-approval declaration update precedes asynchronous fulfilment convergence', () => {
|
test('post-approval declaration update waits for authoritative fulfilment convergence', () => {
|
||||||
const approval = source.indexOf("Admin 审核通过本轮订单")
|
const approval = source.indexOf("Admin 审核通过本轮订单")
|
||||||
const declaration = source.indexOf("Admin 修改审核后客户申报资料")
|
const declaration = source.indexOf("Admin 修改审核后客户申报资料")
|
||||||
const convergence = source.indexOf('const inboundResult = await waitForInbound')
|
const convergence = source.indexOf('const inboundResult = await waitForInbound')
|
||||||
assert.ok(approval >= 0 && declaration > approval && convergence > declaration)
|
assert.ok(approval >= 0 && convergence > approval && declaration > convergence)
|
||||||
|
assert.match(source, /APPROVED declaration edits fail closed against the authoritative Firstleg projection/)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ async function assertDimensionConfig(context, tenantId) {
|
|||||||
&& Number(rule.innerWidthCm) === box.widthCm && Number(rule.innerHeightCm) === box.heightCm
|
&& Number(rule.innerWidthCm) === box.widthCm && Number(rule.innerHeightCm) === box.heightCm
|
||||||
&& Number(rule.edgeAdditionCm) === 2
|
&& Number(rule.edgeAdditionCm) === 2
|
||||||
})
|
})
|
||||||
context.assert(MODULE, '固定四箱型顺序、内径及每边加2cm正确', response,
|
context.assert(MODULE, '固定四箱型顺序、内径及各尺寸增加2cm正确', response,
|
||||||
rules.length === BOXES.length && exact,
|
rules.length === BOXES.length && exact,
|
||||||
`types=${rules.map((rule) => rule.boxType).join(',') || 'missing'}`, CONTRACT_OPTIONS)
|
`types=${rules.map((rule) => rule.boxType).join(',') || 'missing'}`, CONTRACT_OPTIONS)
|
||||||
}
|
}
|
||||||
@@ -684,8 +684,14 @@ export async function run(context) {
|
|||||||
`/api/platform/v1/orders/first-leg/${encodeURIComponent(orderId)}/approve?tenantId=${encodeURIComponent(tenantId)}`)
|
`/api/platform/v1/orders/first-leg/${encodeURIComponent(orderId)}/approve?tenantId=${encodeURIComponent(tenantId)}`)
|
||||||
context.apiSuccess(MODULE, 'Admin 审核通过本轮订单', approveResponse, `orderId=${orderId}`)
|
context.apiSuccess(MODULE, 'Admin 审核通过本轮订单', approveResponse, `orderId=${orderId}`)
|
||||||
|
|
||||||
// The declaration API deliberately accepts APPROVED only. Do this before waiting for
|
// APPROVED declaration edits fail closed against the authoritative Firstleg projection.
|
||||||
// asynchronous inbound/task convergence, which may move the order into fulfilment.
|
// Wait for the approval outbox to create that projection before exercising the edit API.
|
||||||
|
const inboundResult = await waitForInbound(context, tenantId, orderId)
|
||||||
|
const inboundId = id(inboundResult.detail?.summary?.inboundId)
|
||||||
|
if (!inboundId || !inboundResult.task) {
|
||||||
|
throw new Error('订单审核后的入库单与履约任务未在30秒内收敛,无法修改申报并向 OCR/墨家模块交接')
|
||||||
|
}
|
||||||
|
|
||||||
const editableDetailResponse = await context.request('GET', firstLegDetailPath)
|
const editableDetailResponse = await context.request('GET', firstLegDetailPath)
|
||||||
const editableDetail = context.apiSuccess(MODULE, 'Admin 读取审核后客户申报资料', editableDetailResponse)
|
const editableDetail = context.apiSuccess(MODULE, 'Admin 读取审核后客户申报资料', editableDetailResponse)
|
||||||
const declarationDetail = editableDetail?.order?.order ? editableDetail.order : editableDetail
|
const declarationDetail = editableDetail?.order?.order ? editableDetail.order : editableDetail
|
||||||
@@ -719,12 +725,6 @@ export async function run(context) {
|
|||||||
staleDeclarationResponse.http === 200 && String(staleDeclarationResponse.body?.code) === 'A0409',
|
staleDeclarationResponse.http === 200 && String(staleDeclarationResponse.body?.code) === 'A0409',
|
||||||
`code=${staleDeclarationResponse.body?.code ?? 'missing'}`, CONTRACT_OPTIONS)
|
`code=${staleDeclarationResponse.body?.code ?? 'missing'}`, CONTRACT_OPTIONS)
|
||||||
|
|
||||||
const inboundResult = await waitForInbound(context, tenantId, orderId)
|
|
||||||
const inboundId = id(inboundResult.detail?.summary?.inboundId)
|
|
||||||
if (!inboundId || !inboundResult.task) {
|
|
||||||
throw new Error('订单审核后的入库单与履约任务未在30秒内收敛,无法向 OCR/墨家模块交接')
|
|
||||||
}
|
|
||||||
|
|
||||||
const inboundPath = `/api/platform/inbounds/${encodeURIComponent(inboundId)}?tenantId=${encodeURIComponent(tenantId)}`
|
const inboundPath = `/api/platform/inbounds/${encodeURIComponent(inboundId)}?tenantId=${encodeURIComponent(tenantId)}`
|
||||||
const inboundResponse = await context.request('GET', inboundPath)
|
const inboundResponse = await context.request('GET', inboundPath)
|
||||||
const inbound = context.apiSuccess(MODULE, 'Admin 查询本轮入库单详情', inboundResponse,
|
const inbound = context.apiSuccess(MODULE, 'Admin 查询本轮入库单详情', inboundResponse,
|
||||||
|
|||||||
Reference in New Issue
Block a user