优化
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
const MODULE = 'orders'
|
||||
const CONTRACT_OPTIONS = { scenario: 'isolated-order-chain', deploymentBlocker: true }
|
||||
const MERCHANT_USERNAME = process.env.MERCHANT_TEST_USERNAME || 'merchant_admin'
|
||||
const MERCHANT_PASSWORD = process.env.MERCHANT_TEST_PASSWORD || 'ChangeMe123!'
|
||||
const MERCHANT_USERNAME = process.env.MERCHANT_TEST_USERNAME || ''
|
||||
const MERCHANT_PASSWORD = process.env.MERCHANT_TEST_PASSWORD || ''
|
||||
const BOXES = [
|
||||
{ boxType: 'SMALL', lengthCm: 30, widthCm: 20, heightCm: 20 },
|
||||
{ boxType: 'MEDIUM', lengthCm: 40, widthCm: 30, heightCm: 30 },
|
||||
@@ -21,8 +21,8 @@ function success(response) { return Boolean(response) && response.http === 200 &
|
||||
function sleep(milliseconds) { return new Promise((resolve) => setTimeout(resolve, milliseconds)) }
|
||||
function key(context, operation) { return `${context.marker}-${operation}`.slice(0, 128) }
|
||||
export function requestedOrderDestinationType(context) {
|
||||
return String(context?.context?.orderDestinationType || 'FBA').trim().toUpperCase() === 'OVERSEAS'
|
||||
? 'OVERSEAS' : 'FBA'
|
||||
const value = String(context?.context?.orderDestinationType || 'FBA').trim().toUpperCase()
|
||||
return ['FBA', 'OVERSEAS', 'PRIVATE'].includes(value) ? value : 'FBA'
|
||||
}
|
||||
export function approvedInboundReady(orderDetail, taskResponse, tenantId, orderId) {
|
||||
const summary = orderDetail?.summary
|
||||
@@ -202,35 +202,88 @@ async function assertProductImageDownload(context, merchantToken, fileId) {
|
||||
|
||||
async function loadMasterData(context, merchantToken) {
|
||||
const destinationType = requestedOrderDestinationType(context)
|
||||
const fixedRoute = context.context?.waitSplitRoute || {}
|
||||
const domesticResponse = await request(context, 'GET',
|
||||
'/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=DOMESTIC', undefined, merchantToken)
|
||||
const domesticPage = context.apiSuccess(MODULE, 'Client 查询国内收货仓', domesticResponse)
|
||||
context.assert(MODULE, '存在可用国内收货仓', domesticResponse, records(domesticPage).length > 0,
|
||||
`count=${records(domesticPage).length}`)
|
||||
|
||||
const destinationResponse = await request(context, 'GET',
|
||||
`/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=${encodeURIComponent(destinationType)}`,
|
||||
undefined, merchantToken)
|
||||
const destinationPage = context.apiSuccess(MODULE, `Client 查询 ${destinationType} 目的仓`, destinationResponse)
|
||||
context.assert(MODULE, `存在可用 ${destinationType} 目的仓`, destinationResponse,
|
||||
records(destinationPage).length > 0, `count=${records(destinationPage).length}`)
|
||||
let destinationResponse = null
|
||||
let destinationPage = null
|
||||
if (destinationType !== 'PRIVATE') {
|
||||
destinationResponse = await request(context, 'GET',
|
||||
`/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=${encodeURIComponent(destinationType)}`,
|
||||
undefined, merchantToken)
|
||||
destinationPage = context.apiSuccess(MODULE, `Client 查询 ${destinationType} 目的仓`, destinationResponse)
|
||||
context.assert(MODULE, `存在可用 ${destinationType} 目的仓`, destinationResponse,
|
||||
records(destinationPage).length > 0, `count=${records(destinationPage).length}`)
|
||||
}
|
||||
const receivings = records(domesticPage).filter((warehouse) => warehouse?.status === 'ACTIVE')
|
||||
const destinations = records(destinationPage).filter((warehouse) => warehouse?.status === 'ACTIVE')
|
||||
if (receivings.length === 0 || destinations.length === 0) throw new Error('缺少创建测试订单所需的收货仓或目的仓')
|
||||
let destinations = records(destinationPage).filter((warehouse) => warehouse?.status === 'ACTIVE')
|
||||
if (Number(destinationPage?.total) > destinations.length) {
|
||||
const nextResponse = await request(context, 'GET',
|
||||
`/api/merchant/v1/warehouses?page=2&size=200&status=ACTIVE&warehouseType=${encodeURIComponent(destinationType)}`,
|
||||
undefined, merchantToken)
|
||||
const nextPage = context.apiSuccess(MODULE, `Client 查询 ${destinationType} 目的仓第二页`, nextResponse)
|
||||
destinations = [...destinations, ...records(nextPage).filter((warehouse) => warehouse?.status === 'ACTIVE')]
|
||||
}
|
||||
if (receivings.length === 0 || (destinationType !== 'PRIVATE' && destinations.length === 0)) {
|
||||
throw new Error('缺少创建测试订单所需的收货仓或目的仓')
|
||||
}
|
||||
|
||||
const pricingResult = await findPricedOrderRouteAcrossOrigins({
|
||||
receivings, destinations,
|
||||
loadChannels: (receiving) => request(context, 'GET',
|
||||
let pricingResult
|
||||
if (fixedRoute.originWarehouseName && (fixedRoute.channelCode || fixedRoute.channelName)) {
|
||||
const receiving = receivings.find((warehouse) => warehouse.warehouseName === fixedRoute.originWarehouseName)
|
||||
if (!receiving) throw new Error(`未找到固定起运仓: ${fixedRoute.originWarehouseName}`)
|
||||
const channelsResponse = await request(context, 'GET',
|
||||
`/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=${encodeURIComponent(receiving.id)}`,
|
||||
undefined, merchantToken),
|
||||
estimate: (body) => request(context, 'POST', '/api/merchant/v1/first-leg/pricing/estimate', body, merchantToken),
|
||||
destinationType,
|
||||
})
|
||||
undefined, merchantToken)
|
||||
const channels = context.apiSuccess(MODULE, 'Client 查询固定起运仓物流渠道', channelsResponse)
|
||||
const channel = (Array.isArray(channels) ? channels : []).find((item) =>
|
||||
(!fixedRoute.channelCode || item.channelCode === fixedRoute.channelCode)
|
||||
&& (!fixedRoute.channelName || item.channelName === fixedRoute.channelName))
|
||||
if (!channel) throw new Error(`未找到固定物流渠道: ${fixedRoute.channelCode || fixedRoute.channelName}`)
|
||||
const modes = Array.isArray(channel.pricingTaxModes) ? [...channel.pricingTaxModes] : []
|
||||
if (modes.length === 0) throw new Error('固定物流渠道没有可用报价税制')
|
||||
const shift = modes.length ? Number(fixedRoute.taxModeIndex || 0) % modes.length : 0
|
||||
const selectedChannel = { ...channel, pricingTaxModes: [...modes.slice(shift), ...modes.slice(0, shift)] }
|
||||
if (destinationType === 'PRIVATE') {
|
||||
pricingResult = { receiving, priced: { channel: selectedChannel,
|
||||
pricingTaxMode: selectedChannel.pricingTaxModes[0], destination: null },
|
||||
response: channelsResponse, attempts: 0, failures: [], originFailures: [] }
|
||||
} else {
|
||||
const detailResponse = await context.request('GET',
|
||||
`/api/platform/v1/fba-pricing/carrier-rates/price-books/${encodeURIComponent(channel.priceBookVersionId)}`)
|
||||
const detail = context.apiSuccess(MODULE, 'Admin 查询固定渠道当前价格明细', detailResponse)
|
||||
const pricedCodes = new Set((detail?.rates || [])
|
||||
.filter((rate) => rate.channelCode === channel.channelCode && rate.destinationType === destinationType)
|
||||
.map((rate) => String(rate.destinationCode || '').trim()).filter(Boolean))
|
||||
const eligible = destinations.filter((destination) => pricedCodes.has(String(destination.warehouseCode)))
|
||||
if (eligible.length < 1) throw new Error(`固定渠道没有可计价目的仓: ${destinationType}`)
|
||||
const destination = eligible[Number(fixedRoute.destinationIndex || 0) % eligible.length]
|
||||
const result = await findPricedOrderRoute({
|
||||
receiving, destinations: [destination], channels: [selectedChannel],
|
||||
estimate: (body) => request(context, 'POST', '/api/merchant/v1/first-leg/pricing/estimate', body, merchantToken),
|
||||
destinationType,
|
||||
})
|
||||
pricingResult = { receiving, ...result, originFailures: [] }
|
||||
}
|
||||
} else {
|
||||
pricingResult = await findPricedOrderRouteAcrossOrigins({
|
||||
receivings, destinations,
|
||||
loadChannels: (receiving) => request(context, 'GET',
|
||||
`/api/merchant/v1/first-leg/pricing/channels?originWarehouseId=${encodeURIComponent(receiving.id)}`,
|
||||
undefined, merchantToken),
|
||||
estimate: (body) => request(context, 'POST', '/api/merchant/v1/first-leg/pricing/estimate', body, merchantToken),
|
||||
destinationType,
|
||||
})
|
||||
}
|
||||
const priced = pricingResult.priced
|
||||
const receiving = pricingResult.receiving
|
||||
context.addStep(MODULE, 'Client 从 ACTIVE 国内仓选择具有真实价格覆盖的渠道和目的仓', pricingResult.response, Boolean(priced),
|
||||
priced
|
||||
? `origin=${receiving.id}, priceBookVersionId=${priced.channel.priceBookVersionId}, channel=${priced.channel.channelCode}, pricingTaxMode=${priced.pricingTaxMode}, destination=${priced.destination.warehouseCode}, attempts=${pricingResult.attempts}`
|
||||
? `origin=${receiving.id}, priceBookVersionId=${priced.channel.priceBookVersionId}, channel=${priced.channel.channelCode}, pricingTaxMode=${priced.pricingTaxMode}, destination=${priced.destination?.warehouseCode || 'PRIVATE_ADDRESS'}, attempts=${pricingResult.attempts}`
|
||||
: originPricingFailureEvidence(pricingResult),
|
||||
CONTRACT_OPTIONS)
|
||||
if (!priced) throw new Error(`当前 ACTIVE 国内收货仓、渠道、报价税制和 ${destinationType} 目的仓没有可计价组合`)
|
||||
@@ -249,22 +302,39 @@ function buildPayload(context, profile, master, productImageFileId) {
|
||||
const shipmentId = `SHIP-${context.marker}`.slice(0, 64)
|
||||
const referenceId = `REF-${context.marker}`.slice(0, 64)
|
||||
const companyName = profile.tenantName || `订单验收客户-${context.marker}`
|
||||
const requestedSkuCount = Number(context.context?.waitSplitVariant?.skuCount || BOXES.length)
|
||||
const skuCount = Math.max(1, Math.min(BOXES.length, requestedSkuCount))
|
||||
const packageCountPerSku = Math.max(1,
|
||||
Number(context.context?.waitSplitVariant?.packageCountPerSku
|
||||
|| context.context?.phase3LoadingIncrementalPackageCount || 1))
|
||||
const selectedBoxes = BOXES.slice(0, skuCount)
|
||||
const orderProfile = context.context?.orderProfile || {}
|
||||
const isPrivate = master.destinationType === 'PRIVATE'
|
||||
const pickupMethod = orderProfile.pickupMethod || 'PICKUP'
|
||||
const receiver = isPrivate ? {
|
||||
companyName: `${companyName} 私人收件人`, contactName: `收件人-${context.marker}`.slice(0, 64),
|
||||
phone: '12025550188', email: 'private-receiver@example.com', countryCode: 'US',
|
||||
province: 'New York', city: 'New York', district: 'Queens',
|
||||
addressLine: `${context.marker} 88 Randomized Test Ave`, postalCode: '11354',
|
||||
} : null
|
||||
return {
|
||||
fulfillmentType: String(context.context?.orderFulfillmentType || 'STANDARD').trim().toUpperCase(),
|
||||
customerId: master.partner?.id ?? null,
|
||||
customerName: master.partner?.partnerName ?? companyName,
|
||||
customerId: profile.id,
|
||||
customerName: companyName,
|
||||
transportMode: 'SEA', tradeTerm: 'FOB',
|
||||
destinationCountryCode: master.destination.countryCode || 'US',
|
||||
destinationWarehouseId: master.destination.id,
|
||||
destinationWarehouseName: master.destination.warehouseName,
|
||||
destinationCountryCode: master.destination?.countryCode || receiver?.countryCode || 'US',
|
||||
destinationWarehouseId: master.destination?.id || null,
|
||||
destinationWarehouseName: master.destination?.warehouseName || (isPrivate ? '私人地址' : null),
|
||||
expectedShipDate: tomorrow(), customerRemark: `${context.marker} Client 创建隔离订单`,
|
||||
pickupMethod: 'PICKUP', receivingWarehouseId: master.receiving.id,
|
||||
pickupMethod, requestedPickupAt: pickupMethod === 'PICKUP'
|
||||
? new Date(Date.now() + 7 * 86_400_000).toISOString().slice(0, 19) : null,
|
||||
receivingWarehouseId: master.receiving.id,
|
||||
receivingWarehouseName: master.receiving.warehouseName,
|
||||
channelCode: master.channel.channelCode, channelName: master.channel.channelName,
|
||||
carrierId: null, carrierName: null, taxMode: 'BUY_DECLARATION',
|
||||
pricingTaxMode: master.pricingTaxMode, containerMode: 'LCL',
|
||||
customsInspection: false, destinationType: master.destinationType,
|
||||
amazonWarehouseCode: master.destinationType === 'FBA' ? master.destination.warehouseCode : null,
|
||||
carrierId: null, carrierName: null, taxMode: orderProfile.taxMode || 'BUY_DECLARATION',
|
||||
pricingTaxMode: master.pricingTaxMode, containerMode: orderProfile.containerMode || 'LCL',
|
||||
customsInspection: orderProfile.customsInspection ?? false, destinationType: master.destinationType,
|
||||
amazonWarehouseCode: master.destinationType === 'FBA' ? master.destination?.warehouseCode : null,
|
||||
shipmentId: master.destinationType === 'FBA' ? shipmentId : null,
|
||||
referenceId: master.destinationType === 'FBA' ? referenceId : null,
|
||||
sender: {
|
||||
@@ -272,11 +342,11 @@ function buildPayload(context, profile, master, productImageFileId) {
|
||||
email: 'order-acceptance@example.com', countryCode: 'CN', province: '广东省',
|
||||
city: '深圳市', district: '南山区', addressLine: `${context.marker} 测试地址`, postalCode: '518000',
|
||||
},
|
||||
receiver: null,
|
||||
cargoes: BOXES.map((box, index) => {
|
||||
receiver,
|
||||
cargoes: selectedBoxes.map((box, index) => {
|
||||
const volumeCbm = box.lengthCm * box.widthCm * box.heightCm / 1_000_000
|
||||
const unitsPerPackage = 10 + index
|
||||
const packageCount = Number(context.context?.phase3LoadingIncrementalPackageCount) === 2 ? 2 : 1
|
||||
const packageCount = packageCountPerSku
|
||||
return {
|
||||
skuNo: `SKU-${box.boxType}-${context.marker}`.slice(0, 64),
|
||||
cargoName: `${box.boxType} 测试产品`, cargoNameEn: `${box.boxType} test product`,
|
||||
@@ -300,11 +370,12 @@ function buildPayload(context, profile, master, productImageFileId) {
|
||||
}
|
||||
|
||||
function assertCargoContract(context, response, cargoes, payload) {
|
||||
const complete = Array.isArray(cargoes) && cargoes.length === BOXES.length
|
||||
&& BOXES.every((box, index) => {
|
||||
const actual = cargoes[index]; const expected = payload.cargoes[index]
|
||||
return Number(actual?.lengthCm) === box.lengthCm && Number(actual?.widthCm) === box.widthCm
|
||||
&& Number(actual?.heightCm) === box.heightCm && id(actual?.productImageFileId) === id(expected.productImageFileId)
|
||||
const expectedCargoes = payload.cargoes || []
|
||||
const complete = Array.isArray(cargoes) && cargoes.length === expectedCargoes.length
|
||||
&& expectedCargoes.every((expected, index) => {
|
||||
const actual = cargoes[index]
|
||||
return Number(actual?.lengthCm) === expected.lengthCm && Number(actual?.widthCm) === expected.widthCm
|
||||
&& Number(actual?.heightCm) === expected.heightCm && id(actual?.productImageFileId) === id(expected.productImageFileId)
|
||||
&& actual?.skuNo === expected.skuNo && actual?.cargoName === expected.cargoName
|
||||
&& actual?.cargoNameEn === expected.cargoNameEn && actual?.customsHsCode === expected.customsHsCode
|
||||
&& Number(actual?.unitsPerPackage) === expected.unitsPerPackage
|
||||
@@ -320,7 +391,7 @@ function assertCargoContract(context, response, cargoes, payload) {
|
||||
&& (actual?.referenceId ?? null) === (expected.referenceId ?? null)
|
||||
&& actual?.upcNo === expected.upcNo && actual?.cargoRemark.includes(context.marker)
|
||||
})
|
||||
context.assert(MODULE, '四箱型产品字段及图片契约完整回显', response, complete,
|
||||
context.assert(MODULE, '动态SKU产品字段及图片契约完整回显', response, complete,
|
||||
`cargoes=${Array.isArray(cargoes) ? cargoes.length : 'n/a'}, imageFileId=${payload.cargoes[0].productImageFileId}`,
|
||||
CONTRACT_OPTIONS)
|
||||
}
|
||||
@@ -405,9 +476,18 @@ export async function run(context) {
|
||||
})),
|
||||
boxTypes: BOXES.map((box) => box.boxType), inboundId: null,
|
||||
receivingWarehouseId: id(master.receiving.id),
|
||||
receivingWarehouseName: master.receiving.warehouseName,
|
||||
destinationType: master.destinationType,
|
||||
destinationWarehouseId: id(master.destination.id),
|
||||
destinationWarehouseCode: master.destination.warehouseCode,
|
||||
destinationWarehouseId: id(master.destination?.id),
|
||||
destinationWarehouseCode: master.destination?.warehouseCode || null,
|
||||
destinationWarehouseName: master.destination?.warehouseName || (master.destinationType === 'PRIVATE' ? '私人地址' : null),
|
||||
channelCode: master.channel.channelCode,
|
||||
channelName: master.channel.channelName,
|
||||
pricingTaxMode: master.pricingTaxMode,
|
||||
pickupMethod: payload.pickupMethod,
|
||||
taxMode: payload.taxMode,
|
||||
containerMode: payload.containerMode,
|
||||
customsInspection: payload.customsInspection,
|
||||
}
|
||||
|
||||
const replayResponse = await request(context, 'POST', createPath, payload, merchantToken,
|
||||
|
||||
Reference in New Issue
Block a user