diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index 8f6211677..10fd5475b 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -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 : [])])], diff --git a/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs b/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs index d5b1bbbda..14226516e 100644 --- a/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs @@ -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')