362 lines
21 KiB
JavaScript
362 lines
21 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
|
|
const MODULE = 'inbound-measurement'
|
|
const SCENARIO = 'mojia-carton-measurement'
|
|
const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true }
|
|
|
|
function asId(value) { return value == null ? '' : String(value) }
|
|
function asNumber(value) { return Number(value) }
|
|
function closeTo(actual, expected, tolerance = 0.000001) {
|
|
return Number.isFinite(asNumber(actual)) && Math.abs(asNumber(actual) - expected) <= tolerance
|
|
}
|
|
function ceiling(value, scale) {
|
|
const factor = 10 ** scale
|
|
return Math.ceil((value - Number.EPSILON) * factor) / factor
|
|
}
|
|
function halfUp(value, scale) {
|
|
const factor = 10 ** scale
|
|
return Math.round((value + Number.EPSILON) * factor) / factor
|
|
}
|
|
function query(tenantId) { return `tenantId=${encodeURIComponent(tenantId)}` }
|
|
function validPdf(response) {
|
|
return response.http === 200 && response.contentType.toLowerCase().includes('pdf')
|
|
&& response.bytes?.length > 4 && new TextDecoder().decode(response.bytes.slice(0, 4)) === '%PDF'
|
|
}
|
|
|
|
function findIsolatedInbound(context) {
|
|
const roots = [context.createdResources, context.context]
|
|
const visited = new Set()
|
|
const candidates = []
|
|
|
|
function visit(value, inheritedMarker = '') {
|
|
if (!value || typeof value !== 'object' || visited.has(value)) return
|
|
visited.add(value)
|
|
const marker = String(value.marker || value.testMarker || inheritedMarker || '')
|
|
const inboundId = asId(value.inboundId || value.inbound?.id || value.inbound?.inboundId)
|
|
const tenantId = asId(value.tenantId || value.customerId || value.inbound?.tenantId)
|
|
if (marker === context.marker && inboundId && tenantId) {
|
|
candidates.push({ inboundId, tenantId, marker, source: value })
|
|
}
|
|
for (const [key, child] of Object.entries(value)) {
|
|
if (key === 'temporaryPassword' || key === 'token') continue
|
|
if (child && typeof child === 'object') visit(child, marker)
|
|
}
|
|
}
|
|
|
|
roots.forEach((root) => visit(root))
|
|
return candidates[0] || null
|
|
}
|
|
|
|
async function multipartRequest(context, path, data) {
|
|
const form = new FormData()
|
|
form.append('data', new Blob([JSON.stringify(data)], { type: 'application/json' }))
|
|
const image = await readFile(new URL('../../../apps/pda-uniapp/static/c7.png', import.meta.url))
|
|
form.append('image', new Blob([image], { type: 'image/png' }), 'carton-evidence.png')
|
|
try {
|
|
const response = await fetch(`${context.options.gateway}${path}`, {
|
|
method: 'POST',
|
|
headers: { Accept: 'application/json', Authorization: `Bearer ${context.token}` },
|
|
body: form,
|
|
signal: AbortSignal.timeout(60_000),
|
|
})
|
|
const content = await response.text()
|
|
let body = null
|
|
try { body = content ? JSON.parse(content) : null } catch { /* Preserve only a bounded error string. */ }
|
|
return {
|
|
method: 'POST', path, http: response.status, body,
|
|
raw: body === null ? content.slice(0, 500) : null,
|
|
requestId: body?.requestId || response.headers.get('x-request-id'),
|
|
traceId: body?.traceId || response.headers.get('x-trace-id'),
|
|
}
|
|
} catch (error) {
|
|
return { method: 'POST', path, http: 0, body: null, raw: error instanceof Error ? error.message : String(error), requestId: null, traceId: null }
|
|
}
|
|
}
|
|
|
|
async function requestWithHeaders(context, method, path, body, extraHeaders) {
|
|
try {
|
|
const response = await fetch(`${context.options.gateway}${path}`, {
|
|
method,
|
|
headers: {
|
|
Accept: 'application/json', Authorization: `Bearer ${context.token}`,
|
|
'Content-Type': 'application/json; charset=utf-8', ...extraHeaders,
|
|
},
|
|
body: JSON.stringify(body), signal: AbortSignal.timeout(30_000),
|
|
})
|
|
const content = await response.text()
|
|
let payload = null
|
|
try { payload = content ? JSON.parse(content) : null } catch { /* Preserve only a bounded error string. */ }
|
|
return {
|
|
method, path, http: response.status, body: payload,
|
|
raw: payload === null ? content.slice(0, 500) : null,
|
|
requestId: payload?.requestId || response.headers.get('x-request-id'),
|
|
traceId: payload?.traceId || response.headers.get('x-trace-id'),
|
|
}
|
|
} catch (error) {
|
|
return { method, path, http: 0, body: null, raw: error instanceof Error ? error.message : String(error), requestId: null, traceId: null }
|
|
}
|
|
}
|
|
|
|
function finishScenario(context, start) {
|
|
const steps = context.steps.slice(start)
|
|
context.scenarios.push({
|
|
name: SCENARIO, module: MODULE, success: steps.every((step) => step.passed),
|
|
deploymentBlocked: steps.some((step) => step.deploymentBlocker),
|
|
passed: steps.filter((step) => step.passed).length,
|
|
failed: steps.filter((step) => !step.passed).length,
|
|
})
|
|
}
|
|
|
|
export async function run(context) {
|
|
const start = context.steps.length
|
|
const isolated = findIsolatedInbound(context)
|
|
if (!isolated) {
|
|
context.addStep(MODULE, '取得隔离的待测量入库单', null, false,
|
|
`orders 模块未提供 marker=${context.marker} 的 inboundId/tenantId;为避免修改随机历史订单,本模块停止写入`, OPTIONS)
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const { inboundId, tenantId } = isolated
|
|
const detailPath = `/api/platform/inbounds/${inboundId}?${query(tenantId)}`
|
|
const inboundResponse = await context.request('GET', detailPath)
|
|
const inboundDetail = context.apiSuccess(MODULE, '查询隔离入库单详情', inboundResponse,
|
|
`inboundId=${inboundId}, tenantId=${tenantId}`, OPTIONS)
|
|
const summary = inboundDetail?.summary
|
|
const provenanceOk = asId(summary?.id) === inboundId && asId(summary?.tenantId) === tenantId
|
|
&& ['WAIT_MEASURE', 'MEASURING'].includes(summary?.processStatus)
|
|
context.assert(MODULE, '隔离入库单身份与可测量状态正确', inboundResponse, provenanceOk,
|
|
`identity=${asId(summary?.id) === inboundId && asId(summary?.tenantId) === tenantId}, status=${summary?.processStatus ?? 'missing'}`, OPTIONS)
|
|
if (!provenanceOk) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const inboundCode = String(summary.inboundCode || '')
|
|
if (!inboundCode) {
|
|
context.assert(MODULE, '箱码生成前置数据完整', inboundResponse, false,
|
|
'inboundCode=missing', OPTIONS)
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const labelPath = `/api/platform/inbounds/${inboundId}/carton-label-pdf?${query(tenantId)}`
|
|
const labelResponse = await context.requestBinary('POST', labelPath)
|
|
const pdfOk = validPdf(labelResponse)
|
|
context.assert(MODULE, '生成箱唛 PDF 并冻结稳定箱码', labelResponse, pdfOk,
|
|
`contentType=${labelResponse.contentType || 'missing'}, bytes=${labelResponse.bytes?.length ?? 0}`, OPTIONS)
|
|
if (!pdfOk) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const frozenInboundResponse = await context.request('GET', detailPath)
|
|
const frozenInbound = context.apiSuccess(MODULE, '首次打印后回查冻结箱数', frozenInboundResponse, '', OPTIONS)
|
|
const frozenSummary = frozenInbound?.summary
|
|
const originalPackageCount = Number(frozenSummary?.totalPackageCount)
|
|
context.assert(MODULE, '首次打印按订单货物冻结原始总箱数', frozenInboundResponse,
|
|
frozenSummary?.inboundCode === inboundCode && Number.isInteger(originalPackageCount) && originalPackageCount >= 1,
|
|
`inboundCodeStable=${frozenSummary?.inboundCode === inboundCode}, totalPackageCount=${frozenSummary?.totalPackageCount ?? 'missing'}`, OPTIONS)
|
|
if (!Number.isInteger(originalPackageCount) || originalPackageCount < 1) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const boxCode = `${inboundCode}#${originalPackageCount}-1`
|
|
const reprintResponse = await context.requestBinary('POST', labelPath)
|
|
context.assert(MODULE, '重复整单打印复用已冻结箱码映射', reprintResponse, validPdf(reprintResponse),
|
|
`boxCode=${boxCode}, contentType=${reprintResponse.contentType || 'missing'}, bytes=${reprintResponse.bytes?.length ?? 0}`, OPTIONS)
|
|
|
|
const partialPrintPath = `${labelPath}&cartonIndex=1`
|
|
const partialPrintResponse = await context.requestBinary('POST', partialPrintPath)
|
|
context.assert(MODULE, '指定第1箱补打复用已冻结箱码映射', partialPrintResponse, validPdf(partialPrintResponse),
|
|
`boxCode=${boxCode}, cartonIndex=1, contentType=${partialPrintResponse.contentType || 'missing'}, bytes=${partialPrintResponse.bytes?.length ?? 0}`, OPTIONS)
|
|
if (!validPdf(reprintResponse) || !validPdf(partialPrintResponse)) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
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, weight: 12.5 }
|
|
const firstShipmentId = `FBA-${context.marker.slice(-12)}-01`
|
|
const firstScanResponse = await multipartRequest(context, scanPath, {
|
|
boxCode, ...firstDimensions, scannedShipmentId: firstShipmentId,
|
|
machineNo: 'MJ-ADMIN-E2E-01', measuredAt: new Date().toISOString().slice(0, 19),
|
|
})
|
|
const firstScan = context.apiSuccess(MODULE, '模拟墨家首次扫描单箱', firstScanResponse,
|
|
`boxCode=${boxCode}, machineNo=MJ-ADMIN-E2E-01`, OPTIONS)
|
|
context.assert(MODULE, '首次扫描新增一条且默认代表1箱', firstScanResponse,
|
|
firstScan?.created === true && asId(firstScan?.boxCode) === boxCode
|
|
&& Number(firstScan?.confirmedPackageCount) === 1 && asId(firstScan?.itemId),
|
|
`created=${firstScan?.created}, confirmedPackageCount=${firstScan?.confirmedPackageCount ?? 'missing'}`, OPTIONS)
|
|
context.assert(MODULE, '重复打印及指定箱补打后原箱码仍可解析', firstScanResponse,
|
|
firstScan?.created === true && firstScan?.boxCode === boxCode,
|
|
`boxCodeStable=${firstScan?.boxCode === boxCode}, resolvedInboundId=${inboundId}`, OPTIONS)
|
|
if (!firstScan?.itemId || !firstScan?.measurementId) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const measurementPath = `/api/platform/v1/first-leg/inbounds/${inboundId}/measurement?${query(tenantId)}`
|
|
const firstQueryResponse = await context.request('GET', measurementPath)
|
|
const firstQuery = context.apiSuccess(MODULE, '查询箱级测量详情', firstQueryResponse,
|
|
`measurementId=${asId(firstScan.measurementId)}`, OPTIONS)
|
|
const firstItem = firstQuery?.items?.find((item) => asId(item.id) === asId(firstScan.itemId))
|
|
context.assert(MODULE, '箱级查询回显箱码、尺寸、图片与设备信息', firstQueryResponse,
|
|
firstItem?.boxCode === boxCode && asId(firstItem?.intelligentImageFileId)
|
|
&& firstItem?.machineNo === 'MJ-ADMIN-E2E-01'
|
|
&& firstItem?.scannedShipmentId === firstShipmentId
|
|
&& closeTo(firstItem?.lengthCm, firstDimensions.length)
|
|
&& closeTo(firstItem?.widthCm, firstDimensions.width)
|
|
&& closeTo(firstItem?.heightCm, firstDimensions.height),
|
|
`itemFound=${Boolean(firstItem)}, imageFileId=${asId(firstItem?.intelligentImageFileId) || 'missing'}`, OPTIONS)
|
|
|
|
const countResponse = await context.request('PUT',
|
|
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/items/${firstScan.itemId}/confirmed-package-count?${query(tenantId)}`,
|
|
{ measurementVersion: Number(firstScan.measurementVersion), confirmedPackageCount: originalPackageCount })
|
|
const countResult = context.apiSuccess(MODULE, '人工确认当前测量代表箱数', countResponse,
|
|
`confirmedPackageCount=${originalPackageCount}`, OPTIONS)
|
|
context.assert(MODULE, '人工箱数更新且测量版本递增', countResponse,
|
|
Number(countResult?.confirmedPackageCount) === originalPackageCount
|
|
&& Number(countResult?.measurementVersion) === Number(firstScan.measurementVersion) + 1,
|
|
`version=${countResult?.measurementVersion ?? 'missing'}`, OPTIONS)
|
|
|
|
const replacement = { length: 59.5, width: 41.2, height: 31.3, weight: 13.6 }
|
|
const replacementShipmentId = `FBA-${context.marker.slice(-12)}-02`
|
|
const rescanResponse = await multipartRequest(context, scanPath, {
|
|
boxCode, ...replacement, scannedShipmentId: replacementShipmentId,
|
|
machineNo: 'MJ-ADMIN-E2E-02', measuredAt: new Date().toISOString().slice(0, 19),
|
|
})
|
|
const rescan = context.apiSuccess(MODULE, '同箱号重新扫描覆盖当前记录', rescanResponse,
|
|
`boxCode=${boxCode}, machineNo=MJ-ADMIN-E2E-02`, OPTIONS)
|
|
context.assert(MODULE, '覆盖保留同一条记录和人工箱数', rescanResponse,
|
|
rescan?.created === false && asId(rescan?.itemId) === asId(firstScan.itemId)
|
|
&& Number(rescan?.confirmedPackageCount) === originalPackageCount
|
|
&& closeTo(rescan?.lengthCm, replacement.length),
|
|
`sameItem=${asId(rescan?.itemId) === asId(firstScan.itemId)}, confirmedPackageCount=${rescan?.confirmedPackageCount ?? 'missing'}`, OPTIONS)
|
|
|
|
const measuredResponse = await context.request('GET', measurementPath)
|
|
const measured = context.apiSuccess(MODULE, '回查覆盖后的尺寸与体积结果', measuredResponse, '', OPTIONS)
|
|
const 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 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(replacement.length + 2 * (replacement.width + replacement.height), 3)
|
|
context.assert(MODULE, '覆盖尺寸、箱数和服务端体积公式正确', measuredResponse,
|
|
measuredItem && closeTo(measuredItem.lengthCm, replacement.length)
|
|
&& closeTo(measuredItem.widthCm, replacement.width) && closeTo(measuredItem.heightCm, replacement.height)
|
|
&& measuredItem.scannedShipmentId === replacementShipmentId
|
|
&& Number(measuredItem.confirmedPackageCount) === originalPackageCount
|
|
&& closeTo(measuredItem.outerLengthCm, weightedOuter.length)
|
|
&& closeTo(measuredItem.outerWidthCm, weightedOuter.width)
|
|
&& closeTo(measuredItem.outerHeightCm, weightedOuter.height)
|
|
&& closeTo(measuredItem.unitVolumeCbm, unitVolume) && closeTo(measuredItem.totalVolumeCbm, totalVolume)
|
|
&& closeTo(measuredItem.volumeWeightKg, volumeWeight) && closeTo(measuredItem.girthCm, girth)
|
|
&& closeTo(measured.actualVolumeCbm, totalVolume) && closeTo(measured.volumeWeightKg, volumeWeight),
|
|
`weightedOuter=${weightedOuter.length}x${weightedOuter.width}x${weightedOuter.height}, unitVolume=${unitVolume}, totalVolume=${totalVolume}, volumeWeight=${volumeWeight}, girth=${girth}`, OPTIONS)
|
|
|
|
let inboundDetailId = measuredItem?.inboundDetailId
|
|
if (!inboundDetailId) {
|
|
const orderId = asId(frozenSummary?.orderId || context.createdResources?.orders?.orderId)
|
|
const orderDetailResponse = await context.request('GET',
|
|
`/api/platform/v1/orders/first-leg/${encodeURIComponent(orderId)}?${query(tenantId)}`)
|
|
const orderDetail = context.apiSuccess(MODULE, '测量详情缺少货物ID时回查隔离订单', orderDetailResponse,
|
|
`orderId=${orderId}`, OPTIONS)
|
|
const cargoes = Array.isArray(orderDetail?.cargoes) ? [...orderDetail.cargoes] : []
|
|
cargoes.sort((left, right) => Number(left?.itemNo ?? Number.MAX_SAFE_INTEGER)
|
|
- Number(right?.itemNo ?? Number.MAX_SAFE_INTEGER) || asId(left?.id).localeCompare(asId(right?.id)))
|
|
const firstCargo = cargoes[0]
|
|
const cargoBelongsToRun = asId(orderDetail?.order?.id) === orderId
|
|
&& String(firstCargo?.cargoRemark || '').includes(context.marker)
|
|
inboundDetailId = cargoBelongsToRun ? firstCargo?.id : null
|
|
context.assert(MODULE, '草稿绑定本轮箱码对应的隔离货物', orderDetailResponse,
|
|
cargoBelongsToRun && asId(inboundDetailId),
|
|
`cargoCount=${cargoes.length}, markerMatched=${cargoBelongsToRun}, inboundDetailId=${asId(inboundDetailId) || 'missing'}`, OPTIONS)
|
|
}
|
|
if (!inboundDetailId || !measuredItem?.intelligentImageFileId) {
|
|
context.assert(MODULE, '草稿保存前置字段完整', measuredResponse, false,
|
|
`inboundDetailId=${asId(inboundDetailId) || 'missing'}, imageFileId=${asId(measuredItem?.intelligentImageFileId) || 'missing'}`, OPTIONS)
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const draftPayload = {
|
|
measurementId: measured.measurementId, version: Number(measured.version),
|
|
originalDocumentNo: context.marker, originalGrossWeightKg: replacement.weight * originalPackageCount,
|
|
originalVolumeCbm: totalVolume, actualGoodsName: `墨家测量验收-${context.marker.slice(-12)}`,
|
|
items: [{
|
|
inboundDetailId, itemNo: measuredItem.itemNo,
|
|
boxSpecification: measuredItem.boxSpecification, packageCount: originalPackageCount,
|
|
lengthCm: replacement.length, widthCm: replacement.width, heightCm: replacement.height,
|
|
unitWeightKg: replacement.weight, intelligentImageFileId: measuredItem.intelligentImageFileId,
|
|
overLimitWarning: measuredItem.overLimitWarning ?? null,
|
|
outerLengthCm: measuredItem.outerLengthCm ?? null, outerWidthCm: measuredItem.outerWidthCm ?? null,
|
|
outerHeightCm: measuredItem.outerHeightCm ?? null, remark: context.marker,
|
|
}],
|
|
}
|
|
const draftResponse = await context.request('PUT',
|
|
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/draft?${query(tenantId)}`, draftPayload)
|
|
const draft = context.apiSuccess(MODULE, '保存同一测量批次草稿', draftResponse,
|
|
`measurementId=${asId(measured.measurementId)}, items=1`, OPTIONS)
|
|
context.assert(MODULE, '草稿沿用批次并递增版本', draftResponse,
|
|
asId(draft?.measurementId) === asId(measured.measurementId)
|
|
&& Number(draft?.version) === Number(measured.version) + 1 && draft?.status === 'DRAFT',
|
|
`status=${draft?.status ?? 'missing'}, version=${draft?.version ?? 'missing'}`, OPTIONS)
|
|
if (!draft?.measurementId) {
|
|
finishScenario(context, start)
|
|
return
|
|
}
|
|
|
|
const submitResponse = await context.request('POST',
|
|
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/submit?${query(tenantId)}`,
|
|
{ measurementId: draft.measurementId, version: Number(draft.version) })
|
|
const submitted = context.apiSuccess(MODULE, '提交测量审核', submitResponse,
|
|
`measurementId=${asId(draft.measurementId)}`, OPTIONS)
|
|
context.assert(MODULE, '提交后进入待测量审核状态', submitResponse,
|
|
submitted?.processStatus === 'MEASURE_PENDING' && submitted?.measureReviewStatus === 'PENDING',
|
|
`processStatus=${submitted?.processStatus ?? 'missing'}, reviewStatus=${submitted?.measureReviewStatus ?? 'missing'}`, OPTIONS)
|
|
|
|
const rejectedScanResponse = await multipartRequest(context, scanPath, {
|
|
boxCode, ...replacement, machineNo: 'MJ-ADMIN-E2E-03', measuredAt: new Date().toISOString().slice(0, 19),
|
|
})
|
|
const rejected = rejectedScanResponse.http === 200 && String(rejectedScanResponse.body?.code) === 'A0409'
|
|
&& String(rejectedScanResponse.body?.message || '').includes('测量已提交')
|
|
context.assert(MODULE, '测量提交后拒绝设备覆盖并返回中文提示', rejectedScanResponse, rejected,
|
|
`HTTP=${rejectedScanResponse.http}, code=${rejectedScanResponse.body?.code ?? 'missing'}, chineseMessage=${String(rejectedScanResponse.body?.message || '').includes('测量已提交')}`, OPTIONS)
|
|
|
|
const pendingInboundResponse = await context.request('GET', detailPath)
|
|
const pendingInbound = context.apiSuccess(MODULE, '审核前读取最新入库单版本', pendingInboundResponse, '', OPTIONS)
|
|
const pendingSummary = pendingInbound?.summary
|
|
context.assert(MODULE, '审核前状态和版本有效', pendingInboundResponse,
|
|
pendingSummary?.processStatus === 'MEASURE_PENDING' && pendingSummary?.measureReviewStatus === 'PENDING'
|
|
&& Number.isInteger(Number(pendingSummary?.version)),
|
|
`status=${pendingSummary?.processStatus ?? 'missing'}, version=${pendingSummary?.version ?? 'missing'}`, OPTIONS)
|
|
|
|
const approvalPath = `/api/platform/v1/inbounds/${inboundId}/measurements/approve?${query(tenantId)}`
|
|
const approvalResponse = await requestWithHeaders(context, 'POST', approvalPath, {
|
|
measurementId: draft.measurementId, expectedVersion: Number(pendingSummary?.version), reason: null,
|
|
}, { 'Idempotency-Key': `${context.marker}-mojia-approve`.slice(0, 128) })
|
|
context.apiSuccess(MODULE, 'Admin 审核通过墨家测量', approvalResponse,
|
|
`measurementId=${asId(draft.measurementId)}`, OPTIONS)
|
|
|
|
const finalResponse = await context.request('GET', measurementPath)
|
|
const finalMeasurement = context.apiSuccess(MODULE, '查询审核后的最终测量状态', finalResponse, '', OPTIONS)
|
|
context.assert(MODULE, '审核后入库与测量状态闭环且结果未变化', finalResponse,
|
|
finalMeasurement?.processStatus === 'INBOUNDED' && finalMeasurement?.measureReviewStatus === 'APPROVED'
|
|
&& finalMeasurement?.measurementStatus === 'APPROVED'
|
|
&& Number(finalMeasurement?.actualPackageCount) === originalPackageCount
|
|
&& closeTo(finalMeasurement?.actualVolumeCbm, totalVolume)
|
|
&& closeTo(finalMeasurement?.volumeWeightKg, volumeWeight),
|
|
`processStatus=${finalMeasurement?.processStatus ?? 'missing'}, measurementStatus=${finalMeasurement?.measurementStatus ?? 'missing'}, packages=${finalMeasurement?.actualPackageCount ?? 'missing'}`, OPTIONS)
|
|
|
|
context.createdResources.inboundMeasurement = {
|
|
marker: context.marker, tenantId, inboundId, measurementId: asId(draft.measurementId),
|
|
boxCode, finalStatus: finalMeasurement?.measurementStatus || null,
|
|
}
|
|
finishScenario(context, start)
|
|
}
|