修正海外PDA实装箱唛验收夹具

This commit is contained in:
2026-09-02 15:47:13 +08:00
parent ecf4eb1211
commit 5f688d6131
2 changed files with 23 additions and 7 deletions
@@ -47,17 +47,19 @@ async function ensureMatsonSupplier(context, records) {
return supplierId ? { id: supplierId, name: MATSON_SUPPLIER_NAME, status: 'ACTIVE' } : null
}
export function stableLoadedCartonMarks(items, loadingPlanId) {
export function stableLoadedCartonMarks(items) {
const used = new Set()
return [...(Array.isArray(items) ? items : [])]
.sort((left, right) => id(left?.loadingPlanItemId).localeCompare(id(right?.loadingPlanItemId)))
.flatMap((item) => {
const count = Math.max(0, Number(item?.pickedPackageCount) || 0)
const seed = `${loadingPlanId}:${id(item?.loadingPlanItemId)}`
const labels = [...new Set((item?.boxCodes || [])
.map((value) => String(value || '').trim()).filter((value) => value && !used.has(value)))]
.sort((left, right) => createHash('sha256').update(`${seed}:${left}`).digest('hex')
.localeCompare(createHash('sha256').update(`${seed}:${right}`).digest('hex')))
// Partial loading keeps the lowest carton indexes on the loaded source inbound and
// transfers the highest indexes to the remainder child. Mirror that settlement rule
// instead of sampling labels, otherwise downstream PDA fixtures may reference cartons
// that were deliberately moved out of this container.
.sort((left, right) => left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }))
.slice(0, count)
labels.forEach((value) => used.add(value))
return labels
@@ -1477,7 +1479,7 @@ export async function run(context) {
context.createdResources.phase3Loading = {
tenantId, orderId: id(source?.orderId), inboundId, preloadPlanId: planId,
cartonMark: source?.boxCode || source?.cartonMark || null,
cartonMarks: stableLoadedCartonMarks(stagedTask?.items, loadingPlanId),
cartonMarks: stableLoadedCartonMarks(stagedTask?.items),
fulfillmentType: source?.fulfillmentType || 'STANDARD',
skuCodes: [...new Set([...(Array.isArray(source?.skuCodes) ? source.skuCodes : []),
...(Array.isArray(incrementalFixture?.skuCodes) ? incrementalFixture.skuCodes : [])])],
@@ -4,6 +4,7 @@ import test from 'node:test'
import { selectCustomsFlow } from './phase3-customs.mjs'
import { buildFinalizeResource } from './phase3-loading-finalize.mjs'
import { stableLoadedCartonMarks } from './phase3-loading.mjs'
import { selectContainerLoadingFixture, selectPlatformChannelIsolationPair } from './phase3-container-loading.mjs'
import { diffPushPreloadStability, projectPushPreloadStability } from './push-preload-stability.mjs'
import { AcceptanceRun, normalizeModuleOrder, parseArguments } from '../admin-test-env-acceptance.mjs'
@@ -440,11 +441,24 @@ test('preload and loading versions use immediate operation baselines and exact p
assert.match(loading, /shippingCompanyId: id\(shippingCarrier\.id\), shippingCompanyName: shippingCarrier\.name/)
assert.match(loading, /bookingAgentId: id\(supplier\.id\), bookingAgentName: supplier\.name/)
assert.doesNotMatch(loading, /shippingCompanyId: id\(supplier\.id\), shippingCompanyName: supplier\.name/)
assert.match(loading, /cartonMarks: stableLoadedCartonMarks\(stagedTask\?\.items, loadingPlanId\)/)
assert.match(loading, /createHash\('sha256'\)\.update\(`\$\{seed\}:\$\{left\}`\)/)
assert.match(loading, /cartonMarks: stableLoadedCartonMarks\(stagedTask\?\.items\)/)
assert.match(source('./phase3-loading-finalize.mjs'), /cartonMarks: Array\.isArray\(fixture\.cartonMarks\)/)
})
test('loaded carton fixture mirrors partial settlement and keeps the lowest carton indexes', () => {
const marks = stableLoadedCartonMarks([
{ loadingPlanItemId: '20', pickedPackageCount: 3,
boxCodes: ['NYEXX2609020358#8-5', 'NYEXX2609020358#8-2', 'NYEXX2609020358#8-1', 'NYEXX2609020358#8-3'] },
{ loadingPlanItemId: '10', pickedPackageCount: 1, boxCodes: ['NYEXX2609020357#4-4', 'NYEXX2609020357#4-1'] },
])
assert.deepEqual(marks, [
'NYEXX2609020357#4-1',
'NYEXX2609020358#8-1',
'NYEXX2609020358#8-2',
'NYEXX2609020358#8-3',
])
})
test('PDA completion acceptance verifies immutable BATCH and COMPLETE_LOADING audit with operator metadata', () => {
const loading = source('./phase3-loading.mjs')
const container = source('./phase3-container-loading.mjs')