diff --git a/scripts/admin-test-env-acceptance.mjs b/scripts/admin-test-env-acceptance.mjs index 7425a5ad7..753f2b0db 100644 --- a/scripts/admin-test-env-acceptance.mjs +++ b/scripts/admin-test-env-acceptance.mjs @@ -881,6 +881,8 @@ export class AcceptanceRun { inboundId: fixture.inboundId, boxCode: fixture.boxCode, fulfillmentType: fixtureOrder.fulfillmentType || 'STANDARD', + skuCodes: Array.isArray(fixtureOrder.skuCodes) ? fixtureOrder.skuCodes : [], + skuLines: Array.isArray(fixtureOrder.skuLines) ? fixtureOrder.skuLines : [], customerRemark: fixtureOrder.customerRemark, internalRemark: fixtureOrder.internalRemark, cargoRemark: fixtureOrder.cargoRemark, @@ -901,6 +903,10 @@ export class AcceptanceRun { inboundId: incrementalSource.inboundId, boxCode: incrementalSource.boxCode, fulfillmentType: this.createdResources.orders?.fulfillmentType || 'STANDARD', + skuCodes: Array.isArray(this.createdResources.orders?.skuCodes) + ? this.createdResources.orders.skuCodes : [], + skuLines: Array.isArray(this.createdResources.orders?.skuLines) + ? this.createdResources.orders.skuLines : [], } : { marker: originalMarker, failed: true } this.createdResources.orchestrationFixtures = { ...(this.createdResources.orchestrationFixtures || {}), diff --git a/scripts/admin-test-env-modules/orders.mjs b/scripts/admin-test-env-modules/orders.mjs index 186aae83b..3e7b83c41 100644 --- a/scripts/admin-test-env-modules/orders.mjs +++ b/scripts/admin-test-env-modules/orders.mjs @@ -399,6 +399,10 @@ export async function run(context) { context.createdResources.orders = { marker: context.marker, tenantId, orderId, productImageFileId, fulfillmentType: payload.fulfillmentType, + skuCodes: payload.cargoes.map((cargo) => cargo.skuNo), + skuLines: payload.cargoes.map((cargo) => ({ + skuCode: cargo.skuNo, packageCount: cargo.packageCount, + })), boxTypes: BOXES.map((box) => box.boxType), inboundId: null, receivingWarehouseId: id(master.receiving.id), destinationType: master.destinationType, diff --git a/scripts/admin-test-env-modules/phase3-loading-finalize.mjs b/scripts/admin-test-env-modules/phase3-loading-finalize.mjs index 345454daa..c77b313ae 100644 --- a/scripts/admin-test-env-modules/phase3-loading-finalize.mjs +++ b/scripts/admin-test-env-modules/phase3-loading-finalize.mjs @@ -371,6 +371,8 @@ export async function run(context) { sourceInboundId: inboundId, cartonMark: fixture.cartonMark || null, cartonMarks: Array.isArray(fixture.cartonMarks) ? fixture.cartonMarks : [], + skuCodes: Array.isArray(fixture.skuCodes) ? fixture.skuCodes : [], + skuLines: Array.isArray(fixture.skuLines) ? fixture.skuLines : [], fulfillmentType: fixture.fulfillmentType || 'STANDARD', destinationType: fixture.destinationType || null, destinationWarehouseId: id(fixture.destinationWarehouseId), diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index f1cf90df1..bd977c3e0 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -1216,6 +1216,10 @@ export async function run(context) { cartonMarks: [...new Set((stagedTask?.items || []).flatMap((item) => item?.boxCodes || []) .map((value) => String(value || '').trim()).filter(Boolean))], fulfillmentType: source?.fulfillmentType || 'STANDARD', + skuCodes: [...new Set([...(Array.isArray(source?.skuCodes) ? source.skuCodes : []), + ...(Array.isArray(incrementalFixture?.skuCodes) ? incrementalFixture.skuCodes : [])])], + skuLines: [...(Array.isArray(source?.skuLines) ? source.skuLines : []), + ...(Array.isArray(incrementalFixture?.skuLines) ? incrementalFixture.skuLines : [])], destinationType: initial?.summary?.destinationType || source?.destinationType || null, destinationWarehouseId: id(initial?.summary?.destinationWarehouseId || source?.destinationWarehouseId), destinationWarehouseCode: initial?.summary?.destinationCode || source?.destinationWarehouseCode || null, diff --git a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs index 5c07ad16e..6574c4e2c 100644 --- a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs @@ -18,6 +18,8 @@ test('order fixture defaults to FBA and accepts only the explicit OVERSEAS varia assert.match(orders, /warehouseType=\$\{encodeURIComponent\(destinationType\)\}/) assert.match(orders, /destinationType: master\.destinationType/) assert.match(orders, /fulfillmentType: String\(context\.context\?\.orderFulfillmentType \|\| 'STANDARD'\)/) + assert.match(orders, /skuCodes: payload\.cargoes\.map\(\(cargo\) => cargo\.skuNo\)/) + assert.match(orders, /skuLines: payload\.cargoes\.map\(\(cargo\) => \(\{/) assert.match(orders, /destinationType,\s*\n\s*\}\)/) assert.doesNotMatch(orders, /const fbaResponse =/) }) @@ -70,14 +72,21 @@ test('measurement and loading pass destination identity into the LOADED producer assert.match(measurement, /destinationType: context\.createdResources\?\.orders\?\.destinationType/) assert.match(loading, /destinationType: initial\?\.summary\?\.destinationType \|\| source\?\.destinationType/) assert.match(loading, /fulfillmentType: source\?\.fulfillmentType \|\| 'STANDARD'/) + assert.match(loading, /Array\.isArray\(incrementalFixture\?\.skuCodes\)/) + assert.match(loading, /Array\.isArray\(incrementalFixture\?\.skuLines\)/) assert.match(finalize, /destinationType: fixture\.destinationType \|\| null/) assert.match(finalize, /fulfillmentType: fixture\.fulfillmentType \|\| 'STANDARD'/) + assert.match(finalize, /skuCodes: Array\.isArray\(fixture\.skuCodes\) \? fixture\.skuCodes : \[\]/) + assert.match(finalize, /skuLines: Array\.isArray\(fixture\.skuLines\) \? fixture\.skuLines : \[\]/) assert.match(finalize, /resourceKey: `\$\{context\.marker\}:\$\{fixture\.destinationType \|\| 'UNKNOWN'\}:\$\{fixture\.containerNo \|\| containerBookingId\}`/) assert.match(finalize, /phase4InboundProducerFixtures = collectPhase4InboundProducerFixture/) const entry = read('../admin-test-env-acceptance.mjs') assert.match(entry, /runPhase4ProducerVariant\('DROPSHIP-PRIMARY', 'FBA', 'ONE_CLICK_DROPSHIP'\)/) assert.match(entry, /orderFulfillmentType/) assert.match(entry, /fulfillmentType: fixtureOrder\.fulfillmentType \|\| 'STANDARD'/) + assert.match(entry, /skuCodes: Array\.isArray\(fixtureOrder\.skuCodes\) \? fixtureOrder\.skuCodes : \[\]/) + assert.match(entry, /skuLines: Array\.isArray\(fixtureOrder\.skuLines\) \? fixtureOrder\.skuLines : \[\]/) const pda = read('phase4-overseas-pda.mjs') assert.match(pda, /find\(\(item\) => item\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP'\)/) + assert.match(pda, /skuFixtures\(context, producer\)/) }) diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs index 6aae9e42f..011a96b26 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs @@ -175,31 +175,42 @@ async function awaitProducerCandidates(context, token, producers) { return ready } -function skuFixtures(context) { +function skuFixtures(context, producer) { + const frozenLines = Array.isArray(producer?.skuLines) + ? producer.skuLines + .map((line) => ({ skuCode: id(line?.skuCode), packageCount: Number(line?.packageCount) })) + .filter((line) => nonBlank(line.skuCode) && Number.isInteger(line.packageCount) && line.packageCount > 0) + : [] + if (frozenLines.length) return frozenLines + const frozen = Array.isArray(producer?.skuCodes) + ? producer.skuCodes.map(id).filter(nonBlank) + : [] + if (frozen.length) return [...new Set(frozen)].map((skuCode) => ({ skuCode, packageCount: 1 })) const boxes = context.createdResources?.orders?.boxTypes const orchestration = context.createdResources?.orchestrationFixtures || {} const markers = [context.marker, orchestration.phase3Loading?.sourceMarker, orchestration.phase3LoadingIncremental?.sourceMarker].filter(nonBlank) return Array.isArray(boxes) ? [...new Set(markers.flatMap((marker) => boxes.map((boxType) => `SKU-${boxType}-${marker}`.slice(0, 64))))] + .map((skuCode) => ({ skuCode, packageCount: 1 })) : [] } -async function receiveFba(context, token, inbound, pallet) { - const skus = skuFixtures(context) +async function receiveFba(context, token, inbound, pallet, producer) { + const skus = skuFixtures(context, producer) if (!skus.length) { blockerGap(context, `FBA入仓码${inbound.inboundCode} SKU`, '本轮订单 fixture 未交接冻结 SKU,无法安全猜测实收 SKU') return false } let accepted = 0 - for (const [index, skuCode] of skus.entries()) { + for (const [index, sku] of skus.entries()) { + const { skuCode, packageCount } = sku const resolvedResponse = await context.requestOverseas('POST', '/api/pda/v1/last-leg/receiving-containers/current/skus/resolve', { skuCode }, {}, token) const resolvable = success(resolvedResponse) || notFound(resolvedResponse) context.addStep(MODULE, `解析FBA SKU候选 ${index + 1}`, resolvedResponse, resolvable, trace(resolvedResponse), OPTIONS) const resolved = { response: resolvedResponse, data: success(resolvedResponse) ? resolvedResponse.body?.data : null } if (!success(resolved.response) || resolved.data?.inboundCode !== inbound.inboundCode || resolved.data?.destinationType !== 'FBA') continue - const packageCount = skuCode.includes('-LOAD-INC') ? 2 : 1 const body = { inboundCode: inbound.inboundCode, skuCode, palletCode: pallet.palletCode, packageCount, idempotencyKey: key(context, 'fba-bulk', skuCode) } const first = await pda(context, token, 'POST', `FBA SKU ${skuCode}按箱数实收`, '/api/pda/v1/last-leg/receiving-containers/current/receipts/bulk', body) @@ -350,7 +361,9 @@ async function runReceiving(context, token, secondaryToken, merchantToken, prefe cartonMark: context.createdResources?.inboundMeasurement?.boxCode || 'MODE-REJECT-FBA', palletCode: pallets.get(firstFba.inboundCode).palletCode, idempotencyKey: key(context, 'fba-carton-forbidden'), }, (response) => conflictMessage(response, /FBA.*bulk|bulk.*FBA/i)) - for (const inbound of fbaInbounds) await receiveFba(context, token, inbound, pallets.get(inbound.inboundCode)) + for (const inbound of fbaInbounds) { + await receiveFba(context, token, inbound, pallets.get(inbound.inboundCode), preferredProducer) + } } else if (preferredProducer?.destinationType !== 'OVERSEAS') { blockerGap(context, 'FBA SKU按箱数链', '当前FBA柜没有带真实托盘的 FBA 入仓码') }