import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import test from 'node:test' import { fileURLToPath } from 'node:url' import { requestedOrderDestinationType } from './orders.mjs' import { collectPhase4InboundProducerFixture } from './phase3-loading-finalize.mjs' import { AcceptanceRun } from '../admin-test-env-acceptance.mjs' const here = dirname(fileURLToPath(import.meta.url)) const read = (name) => readFileSync(resolve(here, name), 'utf8') test('转运异仓资源不足在造单前阻断,并恢复外层批次和路线', async () => { let created = false const originalRoute = { randomIndex: 3 } const run = { marker: 'MATRIX', phase4ProducerAttempts: new Map(), options: { fixedCustomerId: '101', fixedPdaActors: [{ warehouseId: '201' }], modules: ['phase4-overseas-client-services'] }, context: { waitSplitRoute: originalRoute }, createdResources: { phase3Loading: { marker: 'OUTER' } }, request: async method => { assert.equal(method, 'GET'); return { http: 200, body: { code: '200', data: { total: '0', records: [] } } } }, apiSuccess: (_module, _name, response) => response.body.data, preparePhase3LoadingFixture: async () => { created = true; throw Error('unexpected order creation') }, } await assert.rejects(AcceptanceRun.prototype.runPhase4ProducerVariant.call(run, 'DROPSHIP-PRIMARY', 'FBA', 'ONE_CLICK_DROPSHIP', true), /未创建来源订单/) assert.equal(created, false) assert.equal(run.marker, 'MATRIX') assert.equal(run.context.waitSplitRoute, originalRoute) assert.deepEqual(run.createdResources.phase3Loading, { marker: 'OUTER' }) }) test('order fixture defaults to FBA and preserves explicit OVERSEAS or PRIVATE routes', () => { assert.equal(requestedOrderDestinationType({ context: {} }), 'FBA') assert.equal(requestedOrderDestinationType({ context: { orderDestinationType: 'overseas' } }), 'OVERSEAS') assert.equal(requestedOrderDestinationType({ context: { orderDestinationType: 'PRIVATE' } }), 'PRIVATE') assert.equal(requestedOrderDestinationType({ context: { orderDestinationType: 'invalid' } }), 'FBA') const orders = read('orders.mjs') assert.match(orders, /warehouseType=\$\{encodeURIComponent\(destinationType\)\}/) assert.match(orders, /destinationType: master\.destinationType/) assert.match(orders, /fulfillmentType: dropship \? 'ONE_CLICK_DROPSHIP' : '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 =/) }) test('loading pick operation schemas accept whole-pallet dropship audits', () => { const moduleSql = read('../../firstleg/src/main/resources/db/phase3-pda-convergence-schema.sql') const unifiedSql = read('../../deploy/sql/头程数据库统一增量-MySQL8.sql') for (const sql of [moduleSql, unifiedSql]) { assert.match(sql, /chk_loading_pick_operation_action[\s\S]*?(?:_utf8mb4)?'PICK_PALLET'/) } assert.equal((unifiedSql.match(/CHECK \(\(?action_type in \([^)]*(?:_utf8mb4)?'PICK_PALLET'[^)]*\)\)?\)/gi) || []).length, 1) }) test('producer collector requires dropship FBA, standard FBA, OVERSEAS and a fourth distinct container', () => { const common = { marker: 'RUN-1', eventType: 'FIRST_LEG_LOADING_PLAN_COMPLETED', sourceStatus: 'LOADED' } const dropship = { ...common, eventKey: 'DROP-EVENT', destinationType: 'FBA', fulfillmentType: 'ONE_CLICK_DROPSHIP', containerNo: 'DROP-001' } const fba = { ...common, eventKey: 'FBA-EVENT', destinationType: 'FBA', fulfillmentType: 'STANDARD', containerNo: 'FBA-001' } const overseasPrimary = { ...common, eventKey: 'OS-EVENT-1', destinationType: 'OVERSEAS', containerNo: 'OS-001' } const secondary = { ...common, eventKey: 'FBA-EVENT-2', destinationType: 'FBA', containerNo: 'FBA-002' } const one = collectPhase4InboundProducerFixture(null, dropship) const two = collectPhase4InboundProducerFixture(one, fba) const three = collectPhase4InboundProducerFixture(two, overseasPrimary) const ready = collectPhase4InboundProducerFixture(three, secondary) assert.equal(one.readiness.readyForPhase4Pda, false) assert.equal(two.readiness.readyForPhase4Pda, false) assert.equal(three.readiness.readyForPhase4Pda, false) assert.equal(ready.readiness.readyForPhase4Pda, true) assert.equal(ready.readiness.fbaContainerCount, 3) assert.equal(ready.readiness.overseasContainerCount, 1) assert.equal(ready.readiness.dropshipContainerCount, 1) assert.equal(ready.readiness.distinctContainerCount, 4) assert.equal(ready.byDestinationType.FBA[2].eventKey, 'FBA-EVENT-2') }) test('same event key is idempotently replaced and another marker starts a new matrix', () => { const first = collectPhase4InboundProducerFixture(null, { marker: 'RUN-1', eventKey: 'EVENT-1', destinationType: 'FBA', containerNo: 'FBA-001' }) const replay = collectPhase4InboundProducerFixture(first, { marker: 'RUN-1', eventKey: 'EVENT-1', destinationType: 'FBA', containerNo: 'FBA-001' }) assert.equal(replay.containers.length, 1) const nextRun = collectPhase4InboundProducerFixture(replay, { marker: 'RUN-2', eventKey: 'EVENT-2', destinationType: 'OVERSEAS', containerNo: 'OS-002' }) assert.equal(nextRun.containers.length, 1) assert.equal(nextRun.marker, 'RUN-2') assert.deepEqual(nextRun.requiredEntryOrchestration, [ 'DROPSHIP-FBA: public order fulfillmentType -> inbound-measurement -> phase3-loading -> phase3-loading-finalize -> overseas PDA', 'FBA: orders -> inbound-measurement -> phase3-loading -> phase3-customs -> phase3-loading-finalize', 'OVERSEAS-PRIMARY: orders -> inbound-measurement -> phase3-loading -> phase3-customs -> phase3-loading-finalize', 'SECONDARY(FBA|OVERSEAS): orders -> inbound-measurement -> phase3-loading -> phase3-customs -> phase3-loading-finalize', ]) }) test('measurement and loading pass destination identity into the LOADED producer key', () => { const measurement = read('inbound-measurement.mjs') const loading = read('phase3-loading.mjs') const finalize = read('phase3-loading-finalize.mjs') assert.match(measurement, /destinationType: context\.createdResources\?\.orders\?\.destinationType/) assert.match(loading, /destinationType: initial\?\.summary\?\.destinationType \|\| source\?\.destinationType/) assert.match(loading, /cabinetNo: simulation\.cabinetNo, containerNo/) 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, /cabinetNo: fixture\.cabinetNo \|\| null/) assert.match(finalize, /`\$\{fixture\.cabinetNo\}-\$\{label\.destinationWarehouseInternalCode\}-/) 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', true\)/) assert.match(entry, /packageCounts: \[5\], palletCount: 2/) assert.match(entry, /sealedSourcePalletNos: Array\.isArray\(fixture\.sealedSourcePalletNos\)/) assert.match(entry, /orderFulfillmentType/) assert.match(entry, /fulfillmentType: fixtureOrder\.fulfillmentType \|\| 'STANDARD'/) assert.match(entry, /inboundFees\.ensureInitialCalculated\(this,/) assert.match(entry, /this\.createdResources\.orchestrationFixtures\?\.phase3Loading/) assert.doesNotMatch(entry, /inbound-fees-phase4-|createdResources\.inboundMeasurement = this\.createdResources\.orchestrationFixtures/) 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(loading, /pallet-assignments/) assert.match(loading, /一件代发分配首托后其余托盘仍可独立分配/) assert.match(loading, /已封托托盘拒绝按单条装柜明细删除/) assert.match(loading, /智能推荐按整托重新加入一件代发多托/) assert.match(loading, /智能推荐保留同票全部封托托盘且不按箱拆分/) assert.match(loading, /customerId:\s*tenantId/) assert.match(loading, /inboundCode:\s*initial\?\.summary\?\.inboundCode\s*\|\|\s*source\?\.inboundCode/) assert.match(loading, /canonicalPickItems\.map/) assert.match(loading, /pickedPackageCount: Number\(item\.plannedPackageCount\)/) assert.match(loading, /const dropshipWholePallet = source\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP'/) assert.match(loading, /const canonicalPickPath = batchPath/) assert.match(loading, /action: 'PICK_PALLET', palletNo: item\.palletNo/) assert.match(loading, /const canonicalPickAction = 'BATCH'/) assert.match(loading, /一件代发整托生产者不执行可拆分动态增量场景/) assert.match(loading, /!dropshipWholePallet && Array\.isArray\(incrementalFixture\?\.skuCodes\)/) assert.match(pda, /find\(\(item\) => item\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP'\)/) assert.match(pda, /resolveCartonFixtures\(context, token, current\.data\.inbounds, producerCartonMarks, preferredProducer\)/) assert.match(pda, /FBA与OVERSEAS箱码resolve均为纯查询且零状态写入/) }) test('all receiving and appointment sources use native outbound; waiting source stays below pickup readiness', () => { const entry = read('../admin-test-env-acceptance.mjs') assert.match(entry, /runPhase4ProducerVariant\('OVERSEAS-PRIMARY', 'OVERSEAS', 'ONE_CLICK_DROPSHIP', true\)/) assert.match(entry, /runPhase4ProducerVariant\('FBA-STANDARD', 'FBA', 'STANDARD', true\)/) assert.match(entry, /runPhase4ProducerVariant\('FBA-SECOND', 'FBA', 'STANDARD', true\)/) assert.match(entry, /runPhase4ProducerVariant\('FBA-OPERATIONS', 'FBA', 'STANDARD', true\)/) assert.match(entry, /runPhase4ProducerVariant\('FBA-ROUTE', 'FBA', 'STANDARD', true\)/) assert.match(entry, /routeFbaReady\?\.shippingReady === true/) assert.match(entry, /secondStandardFbaReady\?\.shippingReady === true/) assert.match(entry, /runPhase4ProducerVariant\('SECONDARY', 'FBA', 'STANDARD', false\)/) assert.match(entry, /const hasSecondaryProducer =/) assert.match(entry, /if \(!hasSecondaryProducer\(\)\)/) assert.doesNotMatch(entry, /if \(Number\(readiness\(\)\.distinctContainerCount \|\| 0\) < 4\)/) assert.match(entry, /const shippingRequired = pickupRequired \|\| label === 'SECONDARY'/) assert.match(entry, /if \(!pickupRequired\)/) assert.match(entry, /writeShippingEvidence\(false, ready \? String\(source.queryId\) : null\)/) const start = entry.indexOf('if (shippingRequired && producerKey)') const end = entry.indexOf('} finally {', start) const pickupChain = entry.slice(start, end) const ordered = [ 'phase3-customs.mjs', 'service-center-flow.mjs', 'phase3-port-schedules.mjs', 'phase3-outbound-dispatch.mjs', 'phase3-carrier-schedule-queries.mjs', 'phase4-carrier-nodes.mjs', 'runUntilPickupReady(this)', ] let cursor = -1 for (const token of ordered) { const next = pickupChain.indexOf(token) assert.ok(next > cursor, `${token} must follow the real prerequisite chain`) cursor = next } for (const key of ['phase3Customs', 'serviceCenterFlow', 'portSchedule', 'phase3OutboundDispatch', 'phase3CarrierScheduleQuery', 'phase4CarrierPickupReady']) assert.match(entry, new RegExp(`'${key}'`)) assert.match(entry, /for \(const key of createdKeys\) delete this\.createdResources\[key\]/) assert.match(pickupChain, /shippingReady/) assert.match(entry, /carrierScheduleQueryId/) assert.match(entry, /carrierLatestActualAt/) assert.match(pickupChain, /pickup\.latestActualAt/) assert.match(pickupChain, /pickup\?\.marker === variantMarker/) assert.doesNotMatch(pickupChain, /internal\/event-bridge|ll_container_shipping_gate|request/) assert.match(entry, /dropshipReady\?\.shippingReady === true/) assert.match(entry, /overseasReady\?\.shippingReady === true/) assert.match(entry, /awaitingVariants\.length >= 1/) assert.doesNotMatch(entry, /awaitingVariants\.length === 2/) assert.match(entry, /standardFbaReady\?\.shippingReady === true/) assert.match(entry, /awaitingVariants\.every\(\(item\) => item\.shippingReady === false/) }) test('full phase3 chain validates sealed pallet query metadata and atomic batch protection', () => { const loading = read('phase3-loading.mjs') assert.match(loading, /canonicalPickItems\.every\(\(item\) => item\.unitType === 'SEALED_PALLET'/) assert.match(loading, /unit\.unitType === 'SEALED_PALLET'/) for (const field of ['pickItemId', 'loadingPlanItemId', 'inboundId', 'palletId', 'palletNo', 'indivisible']) { assert.ok(loading.includes(`unit.${field}`), `inventory units must verify ${field}`) } assert.match(loading, /if \(!taskUnitsValid \|\| !inventoryUnitsValid\) return/) assert.match(loading, /items: \[\.\.\.batchPayload\.items, \{/) assert.match(loading, /taskItemsSignature\(unchangedAfterInvalid\) === taskItemsSignature\(beforeInvalidTask\)/) assert.match(loading, /operationsSignature\(afterInvalidOperations\) === operationsSignature\(beforeInvalidOperations\)/) assert.match(loading, /for \(const action of \['PICK', 'PARTIAL', 'SKIP'\]\)/) assert.match(loading, /operationsSignature\(afterBypassOperations\) === operationsSignature\(beforeInvalidOperations\)/) assert.match(loading, /整托batch同键改托盘码必须冲突/) assert.match(loading, /item\.operationId === batchKey[\s\S]*?\.length === 1/) assert.doesNotMatch(loading, /tasks\/\$\{canonicalTaskId\}\/pallets\/pick/) })