1
This commit is contained in:
@@ -20,6 +20,10 @@ function pageOk(value) { return Array.isArray(value?.records) && Number.isFinite
|
||||
function success(response) { return Boolean(response) && response.http === 200 && String(response.body?.code) === '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'
|
||||
}
|
||||
export function approvedInboundReady(orderDetail, taskResponse, tenantId, orderId) {
|
||||
const summary = orderDetail?.summary
|
||||
const task = success(taskResponse) ? taskResponse.body?.data : null
|
||||
@@ -33,12 +37,12 @@ function tomorrow(days = 30) {
|
||||
return date.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
function pricingEstimateRequest(receiving, channel, pricingTaxMode, destination) {
|
||||
function pricingEstimateRequest(receiving, channel, pricingTaxMode, destination, destinationType = 'FBA') {
|
||||
return {
|
||||
originWarehouseId: receiving.id,
|
||||
channelCode: channel.channelCode,
|
||||
destinationCode: destination.warehouseCode,
|
||||
destinationType: 'FBA',
|
||||
destinationType,
|
||||
destinationWarehouseId: destination.id,
|
||||
destinationName: destination.warehouseName,
|
||||
pricingTaxMode,
|
||||
@@ -56,7 +60,7 @@ function estimateMatches(response, candidate) {
|
||||
}
|
||||
|
||||
export async function findPricedOrderRoute({ receiving, destinations, channels, estimate,
|
||||
concurrency = PRICING_PROBE_CONCURRENCY }) {
|
||||
concurrency = PRICING_PROBE_CONCURRENCY, destinationType = 'FBA' }) {
|
||||
const candidates = []
|
||||
for (const channel of channels) {
|
||||
if (!channel?.priceBookVersionId || !channel?.channelCode || !Array.isArray(channel.pricingTaxModes)) continue
|
||||
@@ -65,7 +69,7 @@ export async function findPricedOrderRoute({ receiving, destinations, channels,
|
||||
for (const destination of destinations) {
|
||||
if (!destination?.id || !destination?.warehouseCode) continue
|
||||
candidates.push({ channel, pricingTaxMode, destination,
|
||||
request: pricingEstimateRequest(receiving, channel, pricingTaxMode, destination) })
|
||||
request: pricingEstimateRequest(receiving, channel, pricingTaxMode, destination, destinationType) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +102,7 @@ export async function findPricedOrderRoute({ receiving, destinations, channels,
|
||||
}
|
||||
|
||||
export async function findPricedOrderRouteAcrossOrigins({ receivings, destinations, loadChannels, estimate,
|
||||
concurrency = PRICING_PROBE_CONCURRENCY }) {
|
||||
concurrency = PRICING_PROBE_CONCURRENCY, destinationType = 'FBA' }) {
|
||||
const originFailures = []
|
||||
let attempts = 0
|
||||
let response = null
|
||||
@@ -115,7 +119,8 @@ export async function findPricedOrderRouteAcrossOrigins({ receivings, destinatio
|
||||
traceId: channelResponse?.traceId ?? 'missing', count: channels.length })
|
||||
continue
|
||||
}
|
||||
const result = await findPricedOrderRoute({ receiving, destinations, channels: priceableChannels, estimate, concurrency })
|
||||
const result = await findPricedOrderRoute({ receiving, destinations, channels: priceableChannels,
|
||||
estimate, concurrency, destinationType })
|
||||
attempts += result.attempts
|
||||
response = result.response || response
|
||||
if (result.priced) return { receiving, ...result, originFailures }
|
||||
@@ -196,19 +201,21 @@ async function assertProductImageDownload(context, merchantToken, fileId) {
|
||||
}
|
||||
|
||||
async function loadMasterData(context, merchantToken) {
|
||||
const destinationType = requestedOrderDestinationType(context)
|
||||
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 fbaResponse = await request(context, 'GET',
|
||||
'/api/merchant/v1/warehouses?page=1&size=200&status=ACTIVE&warehouseType=FBA', undefined, merchantToken)
|
||||
const fbaPage = context.apiSuccess(MODULE, 'Client 查询 FBA 目的仓', fbaResponse)
|
||||
context.assert(MODULE, '存在可用 FBA 目的仓', fbaResponse, records(fbaPage).length > 0,
|
||||
`count=${records(fbaPage).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}`)
|
||||
const receivings = records(domesticPage).filter((warehouse) => warehouse?.status === 'ACTIVE')
|
||||
const destinations = records(fbaPage)
|
||||
const destinations = records(destinationPage).filter((warehouse) => warehouse?.status === 'ACTIVE')
|
||||
if (receivings.length === 0 || destinations.length === 0) throw new Error('缺少创建测试订单所需的收货仓或目的仓')
|
||||
|
||||
const pricingResult = await findPricedOrderRouteAcrossOrigins({
|
||||
@@ -217,6 +224,7 @@ async function loadMasterData(context, merchantToken) {
|
||||
`/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
|
||||
@@ -225,7 +233,7 @@ async function loadMasterData(context, merchantToken) {
|
||||
? `origin=${receiving.id}, priceBookVersionId=${priced.channel.priceBookVersionId}, channel=${priced.channel.channelCode}, pricingTaxMode=${priced.pricingTaxMode}, destination=${priced.destination.warehouseCode}, attempts=${pricingResult.attempts}`
|
||||
: originPricingFailureEvidence(pricingResult),
|
||||
CONTRACT_OPTIONS)
|
||||
if (!priced) throw new Error('当前 ACTIVE 国内收货仓、渠道、报价税制和FBA目的仓没有可计价组合')
|
||||
if (!priced) throw new Error(`当前 ACTIVE 国内收货仓、渠道、报价税制和 ${destinationType} 目的仓没有可计价组合`)
|
||||
|
||||
const partnerResponse = await request(context, 'GET',
|
||||
'/api/merchant/v1/partners/candidates?type=SHIPPER', undefined, merchantToken)
|
||||
@@ -234,7 +242,7 @@ async function loadMasterData(context, merchantToken) {
|
||||
? partners.find((item) => item.status === 'ACTIVE' && item.types?.includes?.('SHIPPER'))
|
||||
: null
|
||||
return { receiving, destination: priced.destination, channel: priced.channel,
|
||||
pricingTaxMode: priced.pricingTaxMode, partner }
|
||||
pricingTaxMode: priced.pricingTaxMode, destinationType, partner }
|
||||
}
|
||||
|
||||
function buildPayload(context, profile, master, productImageFileId) {
|
||||
@@ -254,9 +262,10 @@ function buildPayload(context, profile, master, productImageFileId) {
|
||||
channelCode: master.channel.channelCode, channelName: master.channel.channelName,
|
||||
carrierId: null, carrierName: null, taxMode: 'BUY_DECLARATION',
|
||||
pricingTaxMode: master.pricingTaxMode, containerMode: 'LCL',
|
||||
customsInspection: false, destinationType: 'FBA',
|
||||
amazonWarehouseCode: master.destination.warehouseCode,
|
||||
shipmentId, referenceId,
|
||||
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: {
|
||||
companyName, contactName: '订单验收联系人', phone: '13800138000',
|
||||
email: 'order-acceptance@example.com', countryCode: 'CN', province: '广东省',
|
||||
@@ -279,7 +288,9 @@ function buildPayload(context, profile, master, productImageFileId) {
|
||||
material: '塑料 / Plastic', usage: '接口验收 / API acceptance',
|
||||
model: `MODEL-${box.boxType}`, brand: 'TEST-NO-LOGO',
|
||||
productLengthCm: 12 + index, productWidthCm: 8 + index, productHeightCm: 6 + index,
|
||||
shipmentId, referenceId, upcNo: `${Date.now()}${index}`.slice(-12),
|
||||
shipmentId: master.destinationType === 'FBA' ? shipmentId : null,
|
||||
referenceId: master.destinationType === 'FBA' ? referenceId : null,
|
||||
upcNo: `${Date.now()}${index}`.slice(-12),
|
||||
productImageFileId, dangerousGoods: false,
|
||||
cargoRemark: `${context.marker} boxType=${box.boxType}`,
|
||||
}
|
||||
@@ -387,6 +398,9 @@ export async function run(context) {
|
||||
marker: context.marker, tenantId, orderId, productImageFileId,
|
||||
boxTypes: BOXES.map((box) => box.boxType), inboundId: null,
|
||||
receivingWarehouseId: id(master.receiving.id),
|
||||
destinationType: master.destinationType,
|
||||
destinationWarehouseId: id(master.destination.id),
|
||||
destinationWarehouseCode: master.destination.warehouseCode,
|
||||
}
|
||||
|
||||
const replayResponse = await request(context, 'POST', createPath, payload, merchantToken,
|
||||
|
||||
Reference in New Issue
Block a user