291 lines
21 KiB
JavaScript
291 lines
21 KiB
JavaScript
const MODULE = 'phase4-overseas-warehouse'
|
|
const SCENARIO = 'phase4-overseas-warehouse'
|
|
const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true }
|
|
|
|
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.requestOverseas('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.requestOverseas(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 overseasCsv(context, name, path) {
|
|
const response = await context.requestOverseasBinary('GET', path)
|
|
context.addStep(MODULE, name, response,
|
|
response?.http === 200 && Number(response?.bytes?.length) > 0 && String(response?.contentType || '').toLowerCase().includes('csv'),
|
|
`bytes=${response?.bytes?.length ?? 0}, contentType=${response?.contentType || 'missing'}, requestId=${response?.requestId || 'missing'}, traceId=${response?.traceId || 'missing'}`, OPTIONS)
|
|
return response
|
|
}
|
|
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 coverageGap(context, name, reason, category = 'prerequisite') {
|
|
context.addCoverageGap(MODULE, SCENARIO, name, reason, category)
|
|
}
|
|
|
|
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 customer = records(tenantsResult.data).find((item) => longId(item?.id) && item?.status === 'ACTIVE')
|
|
const customerId = id(customer?.id)
|
|
if (!customerId) {
|
|
coverageGap(context, '海外只读接口显式客户范围', `IAM ACTIVE 客户列表未返回可用 customerId;${trace(tenantsResult.response)}`)
|
|
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.id) && longId(plan.sourceOrderId) && nonBlank(plan.planNo) && id(plan.customerId) === customerId && nonBlank(plan.status),
|
|
`id=${id(plan.id) || 'missing'}, sourceOrderId=${id(plan.sourceOrderId) || '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, page: 1, size: 200 }))
|
|
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 overseasCsv(context, '下载SKU库存余额CSV', query('/api/platform/v1/last-leg/inventory/balances/export', { customerId, page: 1, size: 200 }))
|
|
await overseasCsv(context, '下载进销存流水CSV', query('/api/platform/v1/last-leg/inventory/ledger/export', { customerId, page: 1, size: 200 }))
|
|
|
|
coverageGap(context, '换标创建与状态生命周期',
|
|
'当前 Admin 换标新增/审核仍使用不可提交的契约提示弹框,前端未真实接入写 API;验收模块不越过页面范围直接创建换标单', 'frontend-scope')
|
|
|
|
let warehouse = await findWarehouse(context, records(tenantsResult.data), customerId)
|
|
let ownedWarehouse = false
|
|
if (!warehouse) {
|
|
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 pallets = await overseasGet(context, '查询公共托盘列表', query(`/api/platform/v1/warehouse-admin/${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/warehouse-admin/${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)
|
|
}
|
|
|
|
try {
|
|
await verifyPalletLifecycle(context, warehouseId)
|
|
context.context.phase4OverseasWarehouse = { customerId, warehouseId, palletId: context.createdResources.phase4OverseasWarehouse.palletId }
|
|
} finally {
|
|
if (ownedWarehouse) await retireWarehouseFixture(context, customerId, warehouseId)
|
|
}
|
|
}
|
|
|
|
async function createWarehouseFixture(context, customerId) {
|
|
const suffix = String(context.marker || '').replace(/[^A-Za-z0-9]/g, '').slice(-20).toUpperCase()
|
|
const payload = {
|
|
customerId, warehouseCode: `P4W-${suffix}`.slice(0, 50), warehouseName: `${context.marker} 海外仓`,
|
|
warehouseType: 'OVERSEAS', countryCode: 'US', timezone: 'America/Los_Angeles',
|
|
contactName: 'Phase4 QA', contactPhone: '+1-213-555-0188', 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 overseasMutation(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 overseasGet(context, '查询本轮客户海外仓详情', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}`, { customerId }))
|
|
context.assert(MODULE, '本轮客户海外仓归属和状态正确', detail.response,
|
|
id(detail.data?.warehouse?.id || detail.data?.id) === warehouseId
|
|
&& id(detail.data?.warehouse?.customerId || detail.data?.customerId) === customerId
|
|
&& (detail.data?.warehouse?.status || detail.data?.status) === 'ACTIVE',
|
|
`warehouseId=${warehouseId}, customerId=${id(detail.data?.warehouse?.customerId || detail.data?.customerId) || '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.requestOverseas('PATCH', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/status`, { customerId, 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/warehouse-admin/${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/warehouse-admin/${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, updated.data === null,
|
|
`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.requestOverseas('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/warehouse-admin/${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/warehouse-admin/${encodeURIComponent(warehouseId)}/pallets/${encodeURIComponent(palletId)}`)).data?.version),
|
|
})
|
|
context.createdResources.phase4OverseasWarehouse.terminalStatus = retired.data === null ? 'SCRAPPED' : null
|
|
context.assert(MODULE, '本轮公共托盘已报废收口', retired.response, retired.data === null,
|
|
`palletId=${palletId}, terminalStatus=${context.createdResources.phase4OverseasWarehouse.terminalStatus || 'missing'}; ${trace(retired.response)}`, OPTIONS)
|
|
const retiredDetail = await overseasGet(context, '回查本轮公共托盘报废状态',
|
|
`/api/platform/v1/warehouse-admin/${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 candidates = [...new Set([preferredCustomerId, ...tenants.map((item) => id(item?.id)).filter(longId)])]
|
|
let lastResponse = null
|
|
for (const customerId of candidates) {
|
|
const result = await overseasGet(context, `查询客户${customerId}可用海外仓`, query('/api/platform/v1/warehouse-admin', { customerId, page: 1, size: 100, status: 'ACTIVE' }))
|
|
assertPage(context, `客户${customerId}可用海外仓`, result)
|
|
lastResponse = result.response
|
|
const warehouse = records(result.data).find((item) => longId(item?.id) && item?.status === 'ACTIVE')
|
|
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 overseasGet(context, '查询海外仓详情', query(`/api/platform/v1/warehouse-admin/${encodeURIComponent(id(warehouse.id))}`, { customerId }))
|
|
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
|
|
}
|