This commit is contained in:
2026-08-12 12:35:29 +08:00
parent 0fceece378
commit e4f65dae5f
8 changed files with 434 additions and 41 deletions
@@ -9,17 +9,50 @@ function rows(value) { return Array.isArray(value) ? value : Array.isArray(value
function dateOffset(days) { const value = new Date(); value.setUTCDate(value.getUTCDate() + days); return value.toISOString().slice(0, 10) }
function blocker(context, name, evidence) { context.addStep(MODULE, name, null, false, evidence, OPTIONS) }
async function createOrder(context, suffix) {
if (context.financeFixtureHooks?.createOrder) return context.financeFixtureHooks.createOrder(context, suffix)
async function createOrder(context, suffix, pricingProfile = null) {
if (context.financeFixtureHooks?.createOrder) return context.financeFixtureHooks.createOrder(context, suffix, pricingProfile)
const originalMarker = context.marker
const original = context.createdResources.orders
const originalFetch = globalThis.fetch
context.marker = `${originalMarker}-${suffix}`
if (pricingProfile && typeof originalFetch === 'function') {
// The orders module discovers candidates independently. Reorder only those
// real responses so its payload uses one exact active price-book row.
globalThis.fetch = async (input, init = {}) => {
const response = await originalFetch(input, init)
const url = String(input)
if ((init.method || 'GET') === 'GET' && (url.includes('/api/merchant/v1/warehouses?')
|| url.includes('/api/merchant/v1/first-leg/pricing/channels?'))) {
const body = await response.clone().json().catch(() => null)
const data = body?.code === '200' ? body.data : null
if (url.includes('warehouseType=FBA')) {
const records = Array.isArray(data?.records) ? data.records : []
const match = records.find((item) => id(item.warehouseCode || item.code) === id(pricingProfile.destinationCode))
if (match) body.data = { ...data, records: [match, ...records.filter((item) => item !== match)] }
} else if (url.includes('warehouseType=DOMESTIC')) {
const records = Array.isArray(data?.records) ? data.records : []
const match = records.find((item) => id(item.id) === id(pricingProfile.originWarehouseId))
if (match) body.data = { ...data, records: [match, ...records.filter((item) => item !== match)] }
} else {
const channels = Array.isArray(data) ? data : []
const match = channels.find((item) => id(item.channelCode) === id(pricingProfile.channelCode))
if (match) body.data = [{ ...match, pricingTaxModes: [pricingProfile.pricingTaxMode,
...(match.pricingTaxModes || []).filter((mode) => mode !== pricingProfile.pricingTaxMode)] },
...channels.filter((item) => item !== match)]
}
return new Response(JSON.stringify(body), { status: response.status, statusText: response.statusText, headers: response.headers })
}
return response
}
}
try {
const orders = await import(new URL('./orders.mjs', import.meta.url))
await orders.run(context)
const created = context.createdResources.orders
return created?.marker === context.marker && created?.orderId && created?.inboundId ? { ...created } : null
return created?.marker === context.marker && created?.orderId && created?.inboundId
? { ...created, pricingProfile } : null
} finally {
globalThis.fetch = originalFetch
context.marker = originalMarker
if (original) context.createdResources.orders = original
else delete context.createdResources.orders
@@ -169,7 +202,55 @@ async function loadPricingEvidence(context) {
if (context.financeFixtureHooks?.loadPricingEvidence) return context.financeFixtureHooks.loadPricingEvidence(context)
const response = await context.request('GET', '/api/platform/v1/fba-pricing/carrier-rates/price-books?status=ACTIVE')
const versions = context.apiSuccess(MODULE, '读取真实生效价格配置', response, '', OPTIONS)
return { activeVersionIds: rows(versions).map((item) => id(item.id)).filter(Boolean) }
const active = rows(versions).filter((item) => item?.status === 'ACTIVE')
const profiles = []
for (const version of active) {
const detailResponse = await context.request('GET', `/api/platform/v1/fba-pricing/carrier-rates/price-books/${encodeURIComponent(version.id)}`)
const detail = context.apiSuccess(MODULE, '读取 ACTIVE 价格簿真实费率行', detailResponse, `versionId=${version.id}`, OPTIONS)
const rates = Array.isArray(detail?.rates) ? detail.rates : []
for (const rate of rates) {
if (rate?.destinationType !== 'FBA' || !rate?.originWarehouseId || !rate?.channelCode || !rate?.pricingTaxMode || !rate?.destinationCode) continue
if (!['KG', 'CBM'].includes(rate.chargeUnit) || profiles.some((item) => item.chargeUnit === rate.chargeUnit)) continue
const matches = rates.filter((item) => id(item.originWarehouseId) === id(rate.originWarehouseId)
&& item.channelCode === rate.channelCode && item.destinationType === rate.destinationType
&& String(item.destinationCode).toUpperCase() === String(rate.destinationCode).toUpperCase()
&& item.pricingTaxMode === rate.pricingTaxMode)
if (matches.length !== 1) continue
profiles.push({ versionId: id(version.id), rateId: id(rate.id), chargeUnit: rate.chargeUnit,
originWarehouseId: id(rate.originWarehouseId), channelCode: rate.channelCode,
pricingTaxMode: rate.pricingTaxMode, destinationCode: rate.destinationCode })
}
}
context.assert(MODULE, 'ACTIVE 价格簿包含 KG 与 CBM 可用真实组合', response,
profiles.some((item) => item.chargeUnit === 'KG') && profiles.some((item) => item.chargeUnit === 'CBM'),
`units=${profiles.map((item) => item.chargeUnit).join(',') || 'none'}`, OPTIONS)
return { activeVersionIds: active.map((item) => id(item.id)).filter(Boolean), profiles }
}
async function createMarkerContainer(context, resource) {
if (context.financeFixtureHooks?.createMarkerContainer) return context.financeFixtureHooks.createMarkerContainer(context, resource)
if (context.financeFixtureHooks) return null
const original = context.createdResources.phase3ContainerLoading
const originalMarker = context.marker
const snapshots = ['orders', 'inboundMeasurement'].map((key) => [key,
{ present: Object.hasOwn(context.createdResources, key), value: context.createdResources[key] }])
context.createdResources.orders = resource
context.createdResources.inboundMeasurement = resource
context.marker = resource.marker || originalMarker
try {
const module = await import(new URL('./phase3-container-loading.mjs', import.meta.url))
await module.run(context)
const created = context.createdResources.phase3ContainerLoading
return created?.marker === context.marker && created?.containerBookingId && created?.loadingPlanId ? { ...created } : null
} finally {
context.marker = originalMarker
for (const [key, snapshot] of snapshots) {
if (snapshot.present) context.createdResources[key] = snapshot.value
else delete context.createdResources[key]
}
if (original) context.createdResources.phase3ContainerLoading = original
else delete context.createdResources.phase3ContainerLoading
}
}
async function createContainerCost(context, customerId, container, voucherFileId) {
@@ -186,8 +267,9 @@ export async function run(context) {
const start = context.steps.length
const pricingEvidence = await loadPricingEvidence(context)
const resources = []
for (const suffix of ['AUTO1', 'AUTO2', 'AUTO3', 'AUTO4', 'MANUAL']) {
const resource = await createOrder(context, suffix)
const profiles = pricingEvidence.profiles || []
for (const [index, suffix] of ['AUTO1', 'AUTO2', 'AUTO3', 'AUTO4', 'MANUAL'].entries()) {
const resource = await createOrder(context, suffix, profiles[index % profiles.length] || null)
context.assert(MODULE, `创建 ${suffix} marker 隔离订单与入库单`, null, Boolean(resource),
resource ? `orderId=${resource.orderId}, inboundId=${resource.inboundId}` : `marker=${context.marker}-${suffix} 未返回独立入库单`, OPTIONS)
if (resource) resources.push(resource)
@@ -236,8 +318,9 @@ export async function run(context) {
}
}
const container = context.createdResources?.phase3ContainerLoading
const containerOwned = String(container?.marker || '').startsWith(`${context.marker}-`)
const container = await createMarkerContainer(context, measured[0])
|| context.createdResources?.phase3ContainerLoading
const containerOwned = (container?.marker === context.marker || String(container?.marker || '').startsWith(`${context.marker}-`))
&& container?.loadingPlanId && container?.containerBookingId
let containerFixture = null
if (containerOwned && feeItems.PAYABLE && supplier?.id) {