diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs index 87c2addb0..6b0e61040 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs @@ -24,6 +24,9 @@ test('PDA module covers the container-number receiving contract and excludes out for (const token of ['/candidates', '/prepare', '/current/unload-photos/files', '/current/sign', '/current/skus/resolve', '/current/cartons/resolve', '/current/pallets/resolve', '/current/receipts/bulk', '/current/receipts/carton', '/current/complete']) assert.ok(source.includes(token), token) + for (const token of ['/inventory/resolve', '/locations/resolve', '/pallets/resolve', '/operations?page=1&size=50']) { + assert.ok(source.includes(token), token) + } for (const role of ['LICENSE_PLATE', 'CONTAINER_SEAL', 'DOOR_OPEN']) assert.ok(source.includes(role), role) for (const forbidden of ['/outbound', '/dispatch', '/delivery', '/internal/']) assert.equal(source.includes(forbidden), false, forbidden) assert.match(source, /同键同载荷重放/) @@ -50,6 +53,8 @@ test('PDA module covers the container-number receiving contract and excludes out assert.match(source, /last-leg\/orders\?page=1&size=200&containerNo=/) assert.match(source, /phase4OverseasPda\.orderId = id\(order\.id\)/) assert.match(source, /phase4OverseasPda\.customerId = id\(order\.customerId\)/) + assert.match(source, /现场库存查询只返回Token绑定仓且包含库存位置/) + assert.match(source, /作业历史包含本轮柜号收货动作且分页稳定/) }) test('missing WAREHOUSE_PICKER overseas authority is a deployment blocker', async () => { diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs index 9a57ec520..b9edf6c8f 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs @@ -117,8 +117,9 @@ async function createPallet(context, inbound, index) { const warehouseId = id(inbound?.overseasWarehouseId) if (!longId(warehouseId)) return null const suffix = markerSuffix(context) + const locationCode = `PDA-${suffix}-${index + 1}`.slice(0, 64) const location = await context.requestOverseas('POST', `/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/locations`, { - zoneCode: `PDA-${index + 1}`, locationCode: `PDA-${suffix}-${index + 1}`.slice(0, 64), + zoneCode: `PDA-${index + 1}`, locationCode, locationType: 'STORAGE', maxLoadKg: 5000, status: 'IDLE', remark: `${context.marker}-receiving`, }) context.addStep(MODULE, `创建当前柜实收库位${index + 1}`, location, success(location), trace(location), OPTIONS) @@ -133,7 +134,44 @@ async function createPallet(context, inbound, index) { }) context.addStep(MODULE, `创建当前柜实收托盘${index + 1}`, pallet, success(pallet), trace(pallet), OPTIONS) const palletId = id(pallet?.body?.data) - return longId(palletId) ? { warehouseId, locationId, palletId, palletCode, retained: true } : null + return longId(palletId) ? { warehouseId, locationId, locationCode, palletId, palletCode, retained: true } : null +} + +async function verifyWarehouseQueries(context, token, inboundCode, pallet) { + const inventory = await pda(context, token, 'POST', '完成入库后按入仓码现场查询库存', + '/api/pda/v1/last-leg/inventory/resolve', { code: inboundCode }) + context.assert(MODULE, '现场库存查询只返回Token绑定仓且包含库存位置', inventory.response, + success(inventory.response) && inventory.data?.queryCode === inboundCode + && Array.isArray(inventory.data?.items) && inventory.data.items.length > 0 + && inventory.data.items.every((item) => id(item?.palletId) === pallet.palletId + && id(item?.locationId) === pallet.locationId && Number(item?.availablePackages) > 0), + `items=${inventory.data?.items?.length ?? 'missing'}; ${trace(inventory.response)}`, OPTIONS) + + const location = await pda(context, token, 'POST', '完成入库后扫码查询库位', + '/api/pda/v1/last-leg/locations/resolve', { code: pallet.locationCode }) + context.assert(MODULE, '库位查询返回当前仓真实占用汇总', location.response, + success(location.response) && id(location.data?.locationId) === pallet.locationId + && location.data?.locationCode === pallet.locationCode + && Number(location.data?.occupiedPackages) > 0 && Number(location.data?.inventoryPositionCount) > 0, + `locationId=${id(location.data?.locationId) || 'missing'}; ${trace(location.response)}`, OPTIONS) + + const resolvedPallet = await pda(context, token, 'POST', '完成入库后扫码查询托盘内容', + '/api/pda/v1/last-leg/pallets/resolve', { code: pallet.palletCode }) + context.assert(MODULE, '托盘查询返回当前位置与库存明细', resolvedPallet.response, + success(resolvedPallet.response) && id(resolvedPallet.data?.palletId) === pallet.palletId + && id(resolvedPallet.data?.locationId) === pallet.locationId + && Array.isArray(resolvedPallet.data?.inventory) && resolvedPallet.data.inventory.length > 0 + && Number(resolvedPallet.data?.occupiedPackages) > 0, + `palletId=${id(resolvedPallet.data?.palletId) || 'missing'}; ${trace(resolvedPallet.response)}`, OPTIONS) + + const history = await pda(context, token, 'GET', '查询当前仓管PDA作业历史', + '/api/pda/v1/last-leg/operations?page=1&size=50') + const historyRows = records(history.data) + context.assert(MODULE, '作业历史包含本轮柜号收货动作且分页稳定', history.response, + success(history.response) && historyRows.length > 0 + && historyRows.some((item) => String(item?.operationType || '').startsWith('RECEIVING_')) + && Number(history.data?.page) === 1 && Number(history.data?.size) === 50, + `records=${historyRows.length}; ${trace(history.response)}`, OPTIONS) } async function uploadPhotos(context, token) { @@ -395,6 +433,10 @@ async function runReceiving(context, token, secondaryToken, merchantToken, prefe context.createdResources.phase4OverseasPda.finalStatus = completed.data?.status || null if (context.createdResources.phase4OverseasPda.finalStatus === 'COMPLETED') { await captureCompletedOrder(context, containerNo, preferredProducer?.tenantId) + const firstInbound = current.data.inbounds.find((item) => pallets.has(item.inboundCode)) + if (firstInbound) { + await verifyWarehouseQueries(context, token, firstInbound.inboundCode, pallets.get(firstInbound.inboundCode)) + } } }