import { verifyDownloadCenterExport } from './download-center.mjs' const MODULE = 'phase4-overseas-warehouse' const SCENARIO = 'phase4-overseas-warehouse' const OPTIONS = { scenario: SCENARIO } function id(value) { return value == null ? '' : String(value) } function nonBlank(value) { return typeof value === 'string' && value.trim().length > 0 } function nonNegative(value) { return Number.isFinite(Number(value)) && Number(value) >= 0 } function longId(value) { return /^\d+$/.test(id(value)) && id(value) !== '0' } function trace(response) { return `HTTP=${response?.http ?? 'missing'}, code=${response?.body?.code ?? 'missing'}, requestId=${response?.requestId || 'missing'}, traceId=${response?.traceId || 'missing'}` } function query(path, params) { const search = new URLSearchParams() for (const [key, value] of Object.entries(params)) if (value !== undefined && value !== null && value !== '') search.set(key, String(value)) return `${path}?${search}` } function records(page) { return Array.isArray(page?.records) ? page.records : [] } function pageValid(page) { return Array.isArray(page?.records) && Number.isInteger(Number(page?.page)) && Number(page.page) >= 1 && Number.isInteger(Number(page?.size)) && Number(page.size) >= 1 && nonNegative(page?.total) && nonNegative(page?.pages) } function assertEnvelope(context, name, response) { context.assert(MODULE, `${name}响应包络正确`, response, response?.http === 200 && String(response?.body?.code) === '200' && Object.hasOwn(response?.body || {}, 'data'), trace(response), OPTIONS) } async function domesticGet(context, name, path) { const response = await context.request('GET', path) assertEnvelope(context, name, response) return { response, data: context.apiSuccess(MODULE, name, response, trace(response), OPTIONS) } } async function overseasGet(context, name, path) { const response = await context.request('GET', path) assertEnvelope(context, name, response) return { response, data: context.apiSuccess(MODULE, name, response, trace(response), OPTIONS) } } async function overseasMutation(context, method, name, path, body) { const response = await context.request(method, path, body) const passed = response?.http === 200 && String(response?.body?.code) === '200' context.addStep(MODULE, name, response, passed, trace(response), OPTIONS) return { response, data: passed ? response.body?.data : null } } async function domesticMutation(context, method, name, path, body) { const response = await context.request(method, path, body) const passed = response?.http === 200 && String(response?.body?.code) === '200' context.addStep(MODULE, name, response, passed, trace(response), OPTIONS) return { response, data: passed ? response.body?.data : null } } function assertPage(context, name, result) { context.assert(MODULE, `${name}分页结构正确`, result.response, pageValid(result.data), `records=${result.data?.records?.length ?? 'missing'}, page=${result.data?.page ?? 'missing'}, size=${result.data?.size ?? 'missing'}, total=${result.data?.total ?? 'missing'}; ${trace(result.response)}`, OPTIONS) } function businessConflict(response) { return response?.http === 409 || (response?.http === 200 && String(response?.body?.code) === 'A0409') } function voidMutationSucceeded(result) { return result?.response?.http === 200 && String(result?.response?.body?.code) === '200' && result.data == null } function businessNotFound(response) { return response?.http === 404 || (response?.http === 200 && String(response?.body?.code) === 'A0404') } export async function run(context) { context.createdResources.phase4OverseasWarehouse = { marker: context.marker, customerId: null, warehouseId: null, palletId: null, terminalStatus: null } const tenantsResult = await domesticGet(context, '查询可用客户候选', '/api/platform/v1/iam/tenants?page=1&size=100&status=ACTIVE') assertPage(context, '可用客户候选', tenantsResult) const fixedCustomer = context.context?.merchantQualificationFixture const customer = context.options?.fixedCustomerId ? { id: fixedCustomer?.tenantId, status: fixedCustomer?.tenantId ? 'ACTIVE' : null } : records(tenantsResult.data).find((item) => longId(item?.id) && item?.status === 'ACTIVE') const customerId = id(customer?.id) if (!customerId) { context.assert(MODULE, '海外仓验收必须取得ACTIVE客户', tenantsResult.response, false, `IAM ACTIVE 客户列表未返回可用 customerId;${trace(tenantsResult.response)}`, OPTIONS) return } const scoped = { customerId, page: 1, size: 20 } const orders = await overseasGet(context, '查询尾程订单列表', query('/api/platform/v1/last-leg/orders', scoped)) assertPage(context, '尾程订单列表', orders) const order = records(orders.data)[0] if (order) { context.assert(MODULE, '尾程订单关键字段正确', orders.response, longId(order.id) && nonBlank(order.orderNo) && nonBlank(order.currentStatus), `id=${id(order.id) || 'missing'}, orderNo=${order.orderNo || 'missing'}, status=${order.currentStatus || 'missing'}; ${trace(orders.response)}`, OPTIONS) const detail = await overseasGet(context, '查询尾程订单详情', query(`/api/platform/v1/last-leg/orders/${encodeURIComponent(id(order.id))}`, { customerId })) context.assert(MODULE, '尾程订单详情对应列表记录', detail.response, id(detail.data?.order?.id) === id(order.id) && Array.isArray(detail.data?.cargo) && Array.isArray(detail.data?.statusHistory), `listId=${id(order.id)}, detailId=${id(detail.data?.order?.id) || 'missing'}; ${trace(detail.response)}`, OPTIONS) } const relabels = await overseasGet(context, '查询换标单列表', query('/api/platform/v1/last-leg/relabel-applications', scoped)) assertPage(context, '换标单列表', relabels) const relabel = records(relabels.data)[0] if (relabel) { context.assert(MODULE, '换标单关键字段正确', relabels.response, longId(relabel.id) && nonBlank(relabel.relabelNo) && nonBlank(relabel.status), `id=${id(relabel.id) || 'missing'}, relabelNo=${relabel.relabelNo || 'missing'}, status=${relabel.status || 'missing'}; ${trace(relabels.response)}`, OPTIONS) const detail = await overseasGet(context, '查询换标单详情', query(`/api/platform/v1/last-leg/relabel-applications/${encodeURIComponent(relabel.relabelNo)}`, { customerId })) context.assert(MODULE, '换标单详情对应列表记录', detail.response, detail.data?.application?.relabelNo === relabel.relabelNo && Array.isArray(detail.data?.items) && Array.isArray(detail.data?.history), `listNo=${relabel.relabelNo}, detailNo=${detail.data?.application?.relabelNo || 'missing'}; ${trace(detail.response)}`, OPTIONS) } const plans = await overseasGet(context, '查询入仓计划列表', query('/api/platform/v1/last-leg/inbound-plans', scoped)) assertPage(context, '入仓计划列表', plans) const plan = records(plans.data)[0] if (plan) { context.assert(MODULE, '入仓计划关键字段正确', plans.response, longId(plan.lineId) && longId(plan.planId) && longId(plan.orderId) && nonBlank(plan.planNo) && id(plan.customerId) === customerId && nonBlank(plan.status), `lineId=${id(plan.lineId) || 'missing'}, planId=${id(plan.planId) || 'missing'}, orderId=${id(plan.orderId) || 'missing'}, planNo=${plan.planNo || 'missing'}, status=${plan.status || 'missing'}; ${trace(plans.response)}`, OPTIONS) const detail = await overseasGet(context, '查询入仓计划详情', query(`/api/platform/v1/last-leg/inbound-plans/${encodeURIComponent(plan.planNo)}`, { customerId })) context.assert(MODULE, '入仓计划详情对应列表记录', detail.response, detail.data?.planNo === plan.planNo && longId(detail.data?.sourceOrderId) && Array.isArray(detail.data?.lines) && Array.isArray(detail.data?.pallets) && Array.isArray(detail.data?.allocations), `listNo=${plan.planNo}, detailNo=${detail.data?.planNo || 'missing'}, sourceOrderId=${id(detail.data?.sourceOrderId) || 'missing'}; ${trace(detail.response)}`, OPTIONS) } const summary = await overseasGet(context, '查询库存汇总(不传分页参数)', query('/api/platform/v1/last-leg/inventory/summary', { customerId })) context.assert(MODULE, '库存汇总关键数量非负', summary.response, ['inboundOrderCount', 'palletCount', 'totalPackages', 'availablePackages', 'holdPackages', 'inTransitPackages', 'receivingPackages', 'procurementPackages'] .every((field) => nonNegative(summary.data?.[field])), `total=${summary.data?.totalPackages ?? 'missing'}, available=${summary.data?.availablePackages ?? 'missing'}; ${trace(summary.response)}`, OPTIONS) const balances = await overseasGet(context, '查询SKU库存余额', query('/api/platform/v1/last-leg/inventory/balances', scoped)) assertPage(context, 'SKU库存余额', balances) const balance = records(balances.data)[0] if (balance) { context.assert(MODULE, 'SKU库存余额关键字段正确', balances.response, longId(balance.id) && id(balance.customerId) === customerId && nonBlank(balance.skuCode) && nonNegative(balance.totalPackages) && nonNegative(balance.availablePackages) && nonBlank(balance.updatedAt), `id=${id(balance.id) || 'missing'}, sku=${balance.skuCode || 'missing'}, total=${balance.totalPackages ?? 'missing'}; ${trace(balances.response)}`, OPTIONS) const positions = await overseasGet(context, '查询库存库位详情', query(`/api/platform/v1/last-leg/inventory/balances/${encodeURIComponent(id(balance.id))}/positions`, { customerId })) context.assert(MODULE, '库存库位详情结构正确', positions.response, Array.isArray(positions.data) && positions.data.every((item) => longId(item?.id) && id(item?.balanceId) === id(balance.id) && nonNegative(item?.totalPackages)), `balanceId=${id(balance.id)}, positions=${positions.data?.length ?? 'missing'}; ${trace(positions.response)}`, OPTIONS) } const ledger = await overseasGet(context, '查询进销存流水', query('/api/platform/v1/last-leg/inventory/ledger', scoped)) assertPage(context, '进销存流水', ledger) const ledgerRow = records(ledger.data)[0] if (ledgerRow) context.assert(MODULE, '进销存流水关键字段正确', ledger.response, longId(ledgerRow.id) && id(ledgerRow.customerId) === customerId && nonBlank(ledgerRow.businessType) && Number.isFinite(Number(ledgerRow.beforePackages)) && Number.isFinite(Number(ledgerRow.deltaPackages)) && Number.isFinite(Number(ledgerRow.afterPackages)) && nonBlank(ledgerRow.occurredAt), `id=${id(ledgerRow.id) || 'missing'}, type=${ledgerRow.businessType || 'missing'}, delta=${ledgerRow.deltaPackages ?? 'missing'}; ${trace(ledger.response)}`, OPTIONS) await verifyDownloadCenterExport(context, MODULE, 'OVERSEAS_INVENTORY_BALANCE', { customerId }, ['入库单号', 'SKU', '可用箱数']) await verifyDownloadCenterExport(context, MODULE, 'OVERSEAS_INVENTORY_LEDGER', { customerId }, ['发生时间', '业务单号', '变动箱数']) let warehouse = await findWarehouse(context, records(tenantsResult.data), customerId) let ownedWarehouse = false if (!warehouse) { if (context.options?.fixedCustomerId) throw new Error('固定资源模式缺少海外仓,禁止自动创建仓库') warehouse = await createWarehouseFixture(context, customerId) ownedWarehouse = Boolean(warehouse) } if (!warehouse) return const warehouseId = id(warehouse.id) context.createdResources.phase4OverseasWarehouse.customerId = customerId context.createdResources.phase4OverseasWarehouse.warehouseId = warehouseId const locations = await overseasGet(context, '查询公共库位列表', query(`/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations`, { page: 1, size: 20 })) assertPage(context, '公共库位列表', locations) const location = records(locations.data)[0] if (location) { context.assert(MODULE, '公共库位关键字段正确', locations.response, longId(location.id) && id(location.warehouseId) === warehouseId && id(location.customerId) === '0' && nonBlank(location.locationCode) && nonBlank(location.status), `id=${id(location.id) || 'missing'}, customerId=${id(location.customerId) || 'missing'}, status=${location.status || 'missing'}; ${trace(locations.response)}`, OPTIONS) const detail = await overseasGet(context, '查询公共库位详情', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations/${encodeURIComponent(id(location.id))}`) context.assert(MODULE, '公共库位详情对应列表记录', detail.response, id(detail.data?.id) === id(location.id) && id(detail.data?.warehouseId) === warehouseId && id(detail.data?.customerId) === '0', `listId=${id(location.id)}, detailId=${id(detail.data?.id) || 'missing'}; ${trace(detail.response)}`, OPTIONS) } const pallets = await overseasGet(context, '查询公共托盘列表', query(`/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets`, { page: 1, size: 20 })) assertPage(context, '公共托盘列表', pallets) const pallet = records(pallets.data)[0] if (pallet) { context.assert(MODULE, '公共托盘关键字段正确', pallets.response, longId(pallet.id) && id(pallet.warehouseId) === warehouseId && id(pallet.customerId) === '0' && nonBlank(pallet.palletCode) && ['IDLE', 'OCCUPIED', 'SCRAPPED'].includes(pallet.status), `id=${id(pallet.id) || 'missing'}, customerId=${id(pallet.customerId) || 'missing'}, status=${pallet.status || 'missing'}; ${trace(pallets.response)}`, OPTIONS) const detail = await overseasGet(context, '查询公共托盘详情', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(id(pallet.id))}`) context.assert(MODULE, '公共托盘详情对应列表记录', detail.response, id(detail.data?.id) === id(pallet.id) && id(detail.data?.warehouseId) === warehouseId && id(detail.data?.customerId) === '0', `listId=${id(pallet.id)}, detailId=${id(detail.data?.id) || 'missing'}; ${trace(detail.response)}`, OPTIONS) } if (context.options?.fixedCustomerId) { context.context.phase4OverseasWarehouse = { customerId, warehouseId, palletId: null } context.addCoverageGap(MODULE, 'resource-lifecycle', '复用海外仓资源,仅核验列表和详情', '不创建或修改公共库位及托盘;业务分配由后续实际库存流程决定', 'not-implemented', { blocking: false }) return } try { await verifyLocationLifecycle(context, warehouseId) await verifyPalletLifecycle(context, warehouseId) context.context.phase4OverseasWarehouse = { customerId, warehouseId, palletId: context.createdResources.phase4OverseasWarehouse.palletId, } } finally { if (ownedWarehouse) await retireWarehouseFixture(context, customerId, warehouseId) } } async function verifyLocationLifecycle(context, warehouseId) { const marker = String(context.marker || '').replace(/[^A-Za-z0-9_-]/g, '').slice(-40).toUpperCase() || 'PHASE4' const locationCode = `ACC-${marker}`.slice(0, 64) let locationId = '' try { const created = await overseasMutation(context, 'POST', '创建本轮公共库位', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations`, { locationCode, locationType: 'STORAGE', maxLoadKg: 1000, status: 'ACTIVE', remark: context.marker, }) locationId = id(created.data) context.createdResources.phase4OverseasWarehouse.locationId = locationId || null context.assert(MODULE, '公共库位创建返回长ID', created.response, longId(locationId), `locationId=${locationId || 'missing'}, marker=${context.marker}; ${trace(created.response)}`, OPTIONS) if (!locationId) return const detailPath = `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations/${encodeURIComponent(locationId)}` const detail = await overseasGet(context, '查询本轮公共库位详情', detailPath) context.assert(MODULE, '本轮公共库位创建事实正确', detail.response, id(detail.data?.id) === locationId && id(detail.data?.customerId) === '0' && detail.data?.locationCode === locationCode && detail.data?.remark === context.marker, `locationId=${id(detail.data?.id) || 'missing'}, code=${detail.data?.locationCode || 'missing'}; ${trace(detail.response)}`, OPTIONS) const updated = await overseasMutation(context, 'PUT', '编辑本轮公共库位', detailPath, { locationCode, locationType: 'STORAGE', maxLoadKg: 1200, status: 'ACTIVE', remark: `${context.marker}-edited`, }) context.assert(MODULE, '公共库位编辑成功', updated.response, voidMutationSucceeded(updated), `locationId=${locationId}; ${trace(updated.response)}`, OPTIONS) const after = await overseasGet(context, '回查公共库位编辑结果', detailPath) context.assert(MODULE, '公共库位编辑字段已生效', after.response, Number(after.data?.maxLoadKg) === 1200 && after.data?.remark === `${context.marker}-edited`, `maxLoadKg=${after.data?.maxLoadKg ?? 'missing'}, remark=${after.data?.remark || 'missing'}; ${trace(after.response)}`, OPTIONS) } finally { if (locationId) { const removed = await overseasMutation(context, 'DELETE', '删除本轮公共库位', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations/${encodeURIComponent(locationId)}`) const deletedDetail = await context.request('GET', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations/${encodeURIComponent(locationId)}`) const deleted = voidMutationSucceeded(removed) && businessNotFound(deletedDetail) context.createdResources.phase4OverseasWarehouse.locationTerminalStatus = deleted ? 'DELETED' : null context.assert(MODULE, '本轮公共库位已删除且回查不存在', deletedDetail, deleted, `locationId=${locationId}, terminalStatus=${context.createdResources.phase4OverseasWarehouse.locationTerminalStatus || 'missing'}; delete=${trace(removed.response)}, readback=${trace(deletedDetail)}`, OPTIONS) } } } async function createWarehouseFixture(context, customerId) { const suffix = String(context.marker || '').replace(/[^A-Za-z0-9]/g, '').slice(-20).toUpperCase() const payload = { tenantId: 0, warehouseCode: `P4W-${suffix}`.slice(0, 50), warehouseName: `${context.marker} 海外仓`, warehouseType: 'OVERSEAS', countryCode: 'US', timezone: 'America/Los_Angeles', contactName: 'Phase4 QA', contactPhone: '12135550188', contactEmail: null, status: 'ACTIVE', remark: `${context.marker}-warehouse-fixture`, address: { addressType: 'WAREHOUSE', countryCode: 'US', stateProvince: 'California', city: 'Los Angeles', district: 'Commerce', postalCode: '90040', addressLine1: '501 Phase Four Warehouse Avenue', addressLine2: null, latitude: null, longitude: null }, capabilities: [{ capabilityCode: 'STORAGE', capabilityName: 'Storage', enabled: true, cutoffTime: null, description: `${context.marker} acceptance` }], } const created = await domesticMutation(context, 'POST', '创建本轮客户海外仓', '/api/platform/v1/warehouse-admin', payload) const warehouseId = id(created.data) context.assert(MODULE, '客户海外仓创建返回长ID', created.response, longId(warehouseId), `warehouseId=${warehouseId || 'missing'}, customerId=${customerId}; ${trace(created.response)}`, OPTIONS) if (!warehouseId) return null const detail = await domesticGet(context, '查询本轮公共海外仓详情', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}`, { tenantId: 0 })) context.assert(MODULE, '本轮公共海外仓类型和状态正确', detail.response, id(detail.data?.warehouse?.id || detail.data?.id) === warehouseId && (detail.data?.warehouse?.warehouseType || detail.data?.warehouseType) === 'OVERSEAS' && (detail.data?.warehouse?.status || detail.data?.status) === 'ACTIVE', `warehouseId=${warehouseId}, warehouseType=${detail.data?.warehouse?.warehouseType || detail.data?.warehouseType || 'missing'}; ${trace(detail.response)}`, OPTIONS) return { id: warehouseId, customerId, warehouseCode: payload.warehouseCode, warehouseName: payload.warehouseName, status: 'ACTIVE' } } async function retireWarehouseFixture(context, customerId, warehouseId) { const retired = await context.request('PATCH', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/status`, { tenantId: 0, status: 'INACTIVE' })) context.addStep(MODULE, '本轮客户海外仓已停用收口', retired, retired?.http === 200 && String(retired?.body?.code) === '200', trace(retired), OPTIONS) } async function verifyPalletLifecycle(context, warehouseId) { const marker = String(context.marker || '').replace(/[^A-Za-z0-9_-]/g, '').slice(-48) || 'PHASE4' const palletCode = `ACC-${marker}`.slice(0, 128) let palletId = '' try { const created = await overseasMutation(context, 'POST', '创建本轮公共托盘', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets`, { palletCode, palletType: '木托盘', capacityCbm: 2, unitPrice: 0, status: 'IDLE', remark: context.marker, }) palletId = id(created.data) context.createdResources.phase4OverseasWarehouse.palletId = palletId || null context.assert(MODULE, '公共托盘创建返回长ID', created.response, longId(palletId), `palletId=${palletId || 'missing'}, marker=${context.marker}; ${trace(created.response)}`, OPTIONS) if (!palletId) return const detailPath = `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(palletId)}` const detail = await overseasGet(context, '查询本轮公共托盘详情', detailPath) context.assert(MODULE, '本轮公共托盘创建事实正确', detail.response, id(detail.data?.id) === palletId && id(detail.data?.warehouseId) === warehouseId && id(detail.data?.customerId) === '0' && detail.data?.palletCode === palletCode && detail.data?.status === 'IDLE', `palletId=${id(detail.data?.id) || 'missing'}, code=${detail.data?.palletCode || 'missing'}, customerId=${id(detail.data?.customerId) || 'missing'}; ${trace(detail.response)}`, OPTIONS) const updated = await overseasMutation(context, 'PUT', '编辑本轮公共托盘', detailPath, { palletCode, palletType: '木托盘', capacityCbm: 2.5, unitPrice: 1, status: 'IDLE', locationId: id(detail.data?.locationId) || undefined, remark: `${context.marker}-edited`, version: Number(detail.data?.version), }) context.assert(MODULE, '公共托盘编辑成功', updated.response, voidMutationSucceeded(updated), `palletId=${palletId}; ${trace(updated.response)}`, OPTIONS) const after = await overseasGet(context, '回查公共托盘编辑结果', detailPath) context.assert(MODULE, '公共托盘编辑字段已生效', after.response, id(after.data?.id) === palletId && Number(after.data?.capacityCbm) === 2.5 && Number(after.data?.unitPrice) === 1 && after.data?.remark === `${context.marker}-edited`, `capacity=${after.data?.capacityCbm ?? 'missing'}, unitPrice=${after.data?.unitPrice ?? 'missing'}, remark=${after.data?.remark || 'missing'}; ${trace(after.response)}`, OPTIONS) const stale = await context.request('PUT', detailPath, { palletCode, palletType: '木托盘', capacityCbm: 9, unitPrice: 9, status: 'IDLE', remark: 'STALE-DO-NOT-APPLY', version: Number(detail.data?.version), }) context.addStep(MODULE, '公共托盘旧版本编辑返回冲突', stale, stale?.http === 409 || (stale?.http === 200 && String(stale?.body?.code).endsWith('409')), trace(stale), OPTIONS) } finally { if (palletId) { const retired = await overseasMutation(context, 'PUT', '报废本轮公共托盘', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(palletId)}`, { palletCode, palletType: '木托盘', capacityCbm: 2.5, unitPrice: 1, status: 'SCRAPPED', remark: `${context.marker}-acceptance-retired`, version: Number((await overseasGet(context, '读取公共托盘报废前最新版本', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(palletId)}`)).data?.version), }) context.createdResources.phase4OverseasWarehouse.terminalStatus = voidMutationSucceeded(retired) ? 'SCRAPPED' : null context.assert(MODULE, '本轮公共托盘已报废收口', retired.response, voidMutationSucceeded(retired), `palletId=${palletId}, terminalStatus=${context.createdResources.phase4OverseasWarehouse.terminalStatus || 'missing'}; ${trace(retired.response)}`, OPTIONS) const retiredDetail = await overseasGet(context, '回查本轮公共托盘报废状态', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(palletId)}`) context.assert(MODULE, '本轮公共托盘终态为已报废', retiredDetail.response, id(retiredDetail.data?.id) === palletId && retiredDetail.data?.status === 'SCRAPPED', `palletId=${id(retiredDetail.data?.id) || 'missing'}, status=${retiredDetail.data?.status || 'missing'}; ${trace(retiredDetail.response)}`, OPTIONS) } } } async function findWarehouse(context, tenants, preferredCustomerId) { const result = await domesticGet(context, '查询可用公共海外仓', query('/api/platform/v1/warehouse-admin', { tenantId: 0, warehouseType: 'OVERSEAS', page: 1, size: 100, status: 'ACTIVE', })) assertPage(context, '可用公共海外仓', result) const warehouse = records(result.data).find((item) => longId(item?.id) && item?.status === 'ACTIVE' && item?.warehouseType === 'OVERSEAS') if (warehouse) { context.assert(MODULE, '海外仓关键字段正确', result.response, nonBlank(warehouse.warehouseCode) && nonBlank(warehouse.warehouseName), `warehouseId=${id(warehouse.id)}, code=${warehouse.warehouseCode || 'missing'}; ${trace(result.response)}`, OPTIONS) const detail = await domesticGet(context, '查询海外仓详情', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(id(warehouse.id))}`, { tenantId: 0 })) context.assert(MODULE, '海外仓详情对应列表记录', detail.response, id(detail.data?.warehouse?.id || detail.data?.id) === id(warehouse.id), `listId=${id(warehouse.id)}, detailId=${id(detail.data?.warehouse?.id || detail.data?.id) || 'missing'}; ${trace(detail.response)}`, OPTIONS) return warehouse } return null }