优化
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
const MODULE = 'inbound-measurement'
|
||||
const SCENARIO = 'mojia-carton-measurement'
|
||||
const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true }
|
||||
const WEIGHING_IMAGE_COUNT = 1
|
||||
const FIXTURE_PALLET_CAPACITY_CBM = 1.2
|
||||
const WEIGHT_IMAGE = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82,
|
||||
0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84,
|
||||
@@ -44,16 +43,17 @@ async function provisionWarehouseResources(context, { inboundId, warehouseId, pa
|
||||
if (!warehouseId || !Number.isInteger(palletCount) || palletCount < 1) return null
|
||||
|
||||
const suffix = `${String(context.marker).replace(/\D/g, '').slice(-8)}${String(inboundId).slice(-6)}`
|
||||
const palletResponse = await context.request('POST', '/api/platform/v1/pallets?tenantId=0', {
|
||||
warehouseId, palletType: 'STANDARD', capacityCbm: FIXTURE_PALLET_CAPACITY_CBM,
|
||||
weightKg: 8, color: '蓝色', remark: `${context.marker}-INBOUND-${inboundId}`,
|
||||
})
|
||||
const palletId = asId(context.apiSuccess(MODULE, '创建称重托盘主数据', palletResponse,
|
||||
`warehouseId=${warehouseId}`, OPTIONS))
|
||||
if (!palletId) return null
|
||||
|
||||
const resources = []
|
||||
const palletIds = []
|
||||
for (let index = 1; index <= palletCount; index += 1) {
|
||||
const palletResponse = await context.request('POST', '/api/platform/v1/pallets?tenantId=0', {
|
||||
warehouseId, palletType: 'STANDARD', capacityCbm: FIXTURE_PALLET_CAPACITY_CBM,
|
||||
weightKg: 8, color: '蓝色', remark: `${context.marker}-INBOUND-${inboundId}-PALLET-${index}`,
|
||||
})
|
||||
const palletId = asId(context.apiSuccess(MODULE, `创建称重托盘主数据 ${index}/${palletCount}`,
|
||||
palletResponse, `warehouseId=${warehouseId}`, OPTIONS))
|
||||
if (!palletId) return null
|
||||
palletIds.push(palletId)
|
||||
const resourceSuffix = `${suffix}${String(index).padStart(2, '0')}`
|
||||
let locationId = null
|
||||
if (context.context?.phase3LoadingRequireSharedOverflow !== true) {
|
||||
@@ -75,54 +75,65 @@ async function provisionWarehouseResources(context, { inboundId, warehouseId, pa
|
||||
marker: context.marker, tenantId: '0', warehouseId: asId(warehouseId), palletCount,
|
||||
palletCapacityCbm: FIXTURE_PALLET_CAPACITY_CBM, resources,
|
||||
}
|
||||
return { palletId, palletCount, resources }
|
||||
return { palletId: palletIds[0], palletIds, palletCount, resources }
|
||||
}
|
||||
|
||||
async function createConfirmedWeightEvidence(context, { inboundId, tenantId, palletId }) {
|
||||
const uploadResponse = await context.requestMultipart('POST',
|
||||
`/api/platform/v1/files/measurements/${tenantId}/${inboundId}`, {
|
||||
content: WEIGHT_IMAGE, type: 'image/png', name: `weight-${inboundId}.png`,
|
||||
})
|
||||
const upload = context.apiSuccess(MODULE, '上传称重图片并执行 OCR', uploadResponse,
|
||||
`inboundId=${inboundId}`, OPTIONS)
|
||||
const fileId = asId(upload?.fileId)
|
||||
if (!fileId) return null
|
||||
async function createConfirmedWeightEvidence(context, {
|
||||
inboundId, tenantId, palletIds, grossWeightPerPalletKg,
|
||||
}) {
|
||||
if (!Array.isArray(palletIds) || palletIds.length < 1) return null
|
||||
const confirmedResults = []
|
||||
for (let index = 0; index < palletIds.length; index += 1) {
|
||||
const palletId = palletIds[index]
|
||||
const uploadResponse = await context.requestMultipart('POST',
|
||||
`/api/platform/v1/files/measurements/${tenantId}/${inboundId}`, {
|
||||
content: WEIGHT_IMAGE, type: 'image/png', name: `weight-${inboundId}-${index + 1}.png`,
|
||||
})
|
||||
const upload = context.apiSuccess(MODULE, `上传称重图片并执行 OCR ${index + 1}/${palletIds.length}`,
|
||||
uploadResponse, `inboundId=${inboundId}`, OPTIONS)
|
||||
const fileId = asId(upload?.fileId)
|
||||
if (!fileId) return null
|
||||
|
||||
const createResponse = await context.request('POST',
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results?${query(tenantId)}`, {
|
||||
fileId, palletId, ocrProvider: 'ADMIN_ACCEPTANCE', ocrRawText: null,
|
||||
ocrWeightValue: null, ocrWeightUnit: null, confidence: null,
|
||||
})
|
||||
const created = context.apiSuccess(MODULE, '登记称重图片与托盘快照', createResponse,
|
||||
`fileId=${fileId}, palletId=${palletId}`, OPTIONS)
|
||||
if (!created?.id) return null
|
||||
const createResponse = await context.request('POST',
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results?${query(tenantId)}`, {
|
||||
fileId, palletId, ocrProvider: 'ADMIN_ACCEPTANCE', ocrRawText: null,
|
||||
ocrWeightValue: null, ocrWeightUnit: null, confidence: null,
|
||||
})
|
||||
const created = context.apiSuccess(MODULE, `登记称重图片与托盘快照 ${index + 1}/${palletIds.length}`,
|
||||
createResponse, `fileId=${fileId}, palletId=${palletId}`, OPTIONS)
|
||||
if (!created?.id) return null
|
||||
|
||||
const confirmResponse = await context.request('POST',
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results/${created.id}/confirm?${query(tenantId)}`, {
|
||||
version: Number(created.version), adjustedWeightValue: 100,
|
||||
adjustedWeightUnit: 'KG', adjustmentReason: `${context.marker} 自动化人工确认称重`,
|
||||
})
|
||||
const confirmed = context.apiSuccess(MODULE, '人工确认称重图片净重', confirmResponse,
|
||||
`resultId=${created.id}, palletId=${palletId}`, OPTIONS)
|
||||
const netWeightKg = Number(confirmed?.netWeightKg)
|
||||
const grossWeightKg = Number(confirmed?.grossWeightKg)
|
||||
if (confirmed?.recognitionStatus !== 'CONFIRMED' || !Number.isFinite(netWeightKg) || netWeightKg <= 0) {
|
||||
return null
|
||||
const grossWeight = Number(grossWeightPerPalletKg) + index * 5
|
||||
const confirmResponse = await context.request('POST',
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results/${created.id}/confirm?${query(tenantId)}`, {
|
||||
version: Number(created.version), adjustedWeightValue: grossWeight,
|
||||
adjustedWeightUnit: 'KG', adjustmentReason: `${context.marker} 自动化人工确认称重`,
|
||||
})
|
||||
const confirmed = context.apiSuccess(MODULE, `人工确认称重图片净重 ${index + 1}/${palletIds.length}`,
|
||||
confirmResponse, `resultId=${created.id}, palletId=${palletId}`, OPTIONS)
|
||||
const netWeightKg = Number(confirmed?.netWeightKg)
|
||||
const grossWeightKg = Number(confirmed?.grossWeightKg)
|
||||
if (confirmed?.recognitionStatus !== 'CONFIRMED' || !Number.isFinite(netWeightKg) || netWeightKg <= 0) {
|
||||
return null
|
||||
}
|
||||
confirmedResults.push({ id: asId(created.id), palletId, netWeightKg, grossWeightKg })
|
||||
}
|
||||
|
||||
const workspaceResponse = await context.request('GET',
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement?${query(tenantId)}`)
|
||||
const workspace = context.apiSuccess(MODULE, '回查已确认称重图片和货物总重量', workspaceResponse,
|
||||
`resultId=${created.id}`, OPTIONS)
|
||||
`resultIds=${confirmedResults.map((result) => result.id).join(',')}`, OPTIONS)
|
||||
const confirmedRows = (workspace?.weightResults || [])
|
||||
.filter((result) => result?.recognitionStatus === 'CONFIRMED')
|
||||
const persisted = confirmedRows.length === WEIGHING_IMAGE_COUNT
|
||||
&& asId(confirmedRows[0]?.id) === asId(created.id)
|
||||
&& asId(confirmedRows[0]?.palletId) === palletId
|
||||
const grossWeightKg = confirmedResults.reduce((sum, result) => sum + result.grossWeightKg, 0)
|
||||
const netWeightKg = confirmedResults.reduce((sum, result) => sum + result.netWeightKg, 0)
|
||||
const persisted = confirmedRows.length === palletIds.length
|
||||
&& confirmedResults.every((expected) => confirmedRows.some((row) =>
|
||||
asId(row?.id) === expected.id && asId(row?.palletId) === expected.palletId))
|
||||
&& closeTo(workspace?.actualGrossWeightKg, grossWeightKg, 0.001)
|
||||
&& closeTo(workspace?.actualNetWeightKg, netWeightKg, 0.001)
|
||||
context.assert(MODULE, '图片张数决定托数且净重汇总已落地', workspaceResponse, persisted,
|
||||
`confirmedImages=${confirmedRows.length}, expectedPallets=${WEIGHING_IMAGE_COUNT}, grossWeight=${grossWeightKg}, netWeight=${netWeightKg}, actualGross=${workspace?.actualGrossWeightKg ?? 'missing'}, actualNet=${workspace?.actualNetWeightKg ?? 'missing'}`,
|
||||
`confirmedImages=${confirmedRows.length}, expectedPallets=${palletIds.length}, grossWeight=${grossWeightKg}, netWeight=${netWeightKg}, actualGross=${workspace?.actualGrossWeightKg ?? 'missing'}, actualNet=${workspace?.actualNetWeightKg ?? 'missing'}`,
|
||||
OPTIONS)
|
||||
return persisted ? { count: confirmedRows.length, grossWeightKg, netWeightKg } : null
|
||||
}
|
||||
@@ -280,8 +291,8 @@ export async function run(context) {
|
||||
}
|
||||
|
||||
const scanPath = `/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/carton-scans?${query(tenantId)}`
|
||||
// Keep the isolated sample inside the customer's largest configured inner box (60x50x50cm).
|
||||
const firstDimensions = { length: 59.1, width: 40.2, height: 30.3 }
|
||||
const measurementProfile = context.context?.waitSplitVariant?.measurementProfile
|
||||
const firstDimensions = measurementProfile?.first || { length: 59.1, width: 40.2, height: 30.3 }
|
||||
const firstShipmentId = `FBA-${context.marker.slice(-12)}-01`
|
||||
const firstScanResponse = await multipartRequest(context, scanPath, {
|
||||
boxCode, ...firstDimensions, scannedShipmentId: firstShipmentId,
|
||||
@@ -325,7 +336,7 @@ export async function run(context) {
|
||||
&& Number(countResult?.measurementVersion) === Number(firstScan.measurementVersion) + 1,
|
||||
`version=${countResult?.measurementVersion ?? 'missing'}`, OPTIONS)
|
||||
|
||||
const replacement = { length: 59.5, width: 41.2, height: 31.3 }
|
||||
const replacement = measurementProfile?.replacement || { length: 59.5, width: 41.2, height: 31.3 }
|
||||
const replacementShipmentId = `FBA-${context.marker.slice(-12)}-02`
|
||||
const rescanResponse = await multipartRequest(context, scanPath, {
|
||||
boxCode, ...replacement, scannedShipmentId: replacementShipmentId,
|
||||
@@ -342,8 +353,7 @@ export async function run(context) {
|
||||
let measuredResponse = await context.request('GET', measurementPath)
|
||||
let measured = context.apiSuccess(MODULE, '回查覆盖后的尺寸与体积结果', measuredResponse, '', OPTIONS)
|
||||
let measuredItem = measured?.items?.find((item) => asId(item.id) === asId(firstScan.itemId))
|
||||
// The verified customer rule maps this scan to EXTRA_LARGE 60x50x50cm and adds 2cm per edge.
|
||||
const weightedOuter = { length: 64, width: 54, height: 54 }
|
||||
const weightedOuter = measurementProfile?.outer || { length: 64, width: 54, height: 54 }
|
||||
const unitVolume = halfUp(
|
||||
weightedOuter.length * weightedOuter.width * weightedOuter.height / 1_000_000, 3)
|
||||
const totalVolume = unitVolume * originalPackageCount
|
||||
@@ -374,7 +384,7 @@ export async function run(context) {
|
||||
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/items/${firstScan.itemId}/confirmed-package-count?${query(tenantId)}`,
|
||||
{ measurementVersion: Number(measured?.version), confirmedPackageCount: packagesPerDetail })
|
||||
const resetFirstCount = context.apiSuccess(MODULE,
|
||||
'增量 fixture 将首条货物确认箱数恢复为自身两箱', resetFirstCountResponse,
|
||||
'增量 fixture 将首条货物确认箱数恢复为自身箱数', resetFirstCountResponse,
|
||||
`itemId=${asId(firstScan.itemId)}, confirmedPackageCount=${packagesPerDetail}`, OPTIONS)
|
||||
context.assert(MODULE, '增量首条货物箱数和版本更新正确', resetFirstCountResponse,
|
||||
Number(resetFirstCount?.confirmedPackageCount) === packagesPerDetail
|
||||
@@ -409,10 +419,10 @@ export async function run(context) {
|
||||
`measurementVersion=${countDetail?.measurementVersion ?? 'missing'}`, OPTIONS)
|
||||
}
|
||||
measuredResponse = await context.request('GET', measurementPath)
|
||||
measured = context.apiSuccess(MODULE, '回查增量 fixture 四条测量明细', measuredResponse, '', OPTIONS)
|
||||
measured = context.apiSuccess(MODULE, '回查增量 fixture 动态测量明细', measuredResponse, '', OPTIONS)
|
||||
measuredItem = measured?.items?.find((item) => asId(item.id) === asId(firstScan.itemId))
|
||||
const detailIds = new Set((measured?.items || []).map((item) => asId(item?.inboundDetailId)).filter(Boolean))
|
||||
context.assert(MODULE, '增量 fixture 形成四条真实独立货物测量明细', measuredResponse,
|
||||
context.assert(MODULE, '增量 fixture 形成动态真实独立货物测量明细', measuredResponse,
|
||||
detailIds.size >= incrementalDetailCount
|
||||
&& measured.items.filter((item) => detailIds.has(asId(item?.inboundDetailId))).length >= incrementalDetailCount,
|
||||
`measurementItems=${measured?.items?.length ?? 'missing'}, distinctInboundDetails=${detailIds.size}`, OPTIONS)
|
||||
@@ -482,12 +492,13 @@ export async function run(context) {
|
||||
return
|
||||
}
|
||||
|
||||
const palletCount = Math.max(1, Number(context.context?.waitSplitVariant?.palletCount || 1))
|
||||
const warehouseResourcesReady = await provisionWarehouseResources(context, {
|
||||
inboundId, warehouseId: asId(summary?.warehouseId), palletCount: WEIGHING_IMAGE_COUNT,
|
||||
inboundId, warehouseId: asId(summary?.warehouseId), palletCount,
|
||||
})
|
||||
context.assert(MODULE, '测量提交前按称重图片数准备真实库位', draftResponse,
|
||||
Boolean(warehouseResourcesReady),
|
||||
`warehouseId=${asId(summary?.warehouseId) || 'missing'}, palletCount=${WEIGHING_IMAGE_COUNT}`,
|
||||
`warehouseId=${asId(summary?.warehouseId) || 'missing'}, palletCount=${palletCount}`,
|
||||
OPTIONS)
|
||||
if (!warehouseResourcesReady) {
|
||||
finishScenario(context, start)
|
||||
@@ -495,11 +506,12 @@ export async function run(context) {
|
||||
}
|
||||
|
||||
const weightEvidence = await createConfirmedWeightEvidence(context, {
|
||||
inboundId, tenantId, palletId: warehouseResourcesReady.palletId,
|
||||
inboundId, tenantId, palletIds: warehouseResourcesReady.palletIds,
|
||||
grossWeightPerPalletKg: Number(context.context?.waitSplitVariant?.grossWeightPerPalletKg || 100),
|
||||
})
|
||||
context.assert(MODULE, '测量提交前已通过正式接口确认称重图片', draftResponse,
|
||||
Boolean(weightEvidence),
|
||||
`confirmedImages=${weightEvidence?.count ?? 0}, expected=${WEIGHING_IMAGE_COUNT}`,
|
||||
`confirmedImages=${weightEvidence?.count ?? 0}, expected=${palletCount}`,
|
||||
OPTIONS)
|
||||
if (!weightEvidence) {
|
||||
finishScenario(context, start)
|
||||
|
||||
Reference in New Issue
Block a user