fix(acceptance): select booking agents by carrier
This commit is contained in:
@@ -59,11 +59,11 @@ test('Firstleg booking supplier snapshot requires ACTIVE BOOKING and has no dome
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('HTTP ocean booking acceptance selects an ACTIVE BOOKING supplier without a geographic scope', () => {
|
test('HTTP ocean booking acceptance selects an ACTIVE BOOKING supplier without a geographic scope', () => {
|
||||||
assert.match(oceanBookingFixture, /category=BOOKING&status=ACTIVE/)
|
assert.match(oceanBookingFixture, /resolveOceanBookingAgent\(context, MODULE, 'MATSON', OPTIONS\)/)
|
||||||
assert.match(oceanBookingFixture, /bookingAgentId: id\(agent\.id\), bookingAgentName: agent\.name/)
|
assert.match(oceanBookingFixture, /bookingAgentId: id\(agent\.id\), bookingAgentName: agent\.name/)
|
||||||
assert.match(oceanBookingFixture, /shippingCompanyId: id\(carrier\.id\), shippingCompanyName: carrier\.name, carrierCode: 'MATSON'/)
|
assert.match(oceanBookingFixture, /shippingCompanyId: id\(carrier\.id\), shippingCompanyName: carrier\.name, carrierCode: 'MATSON'/)
|
||||||
assert.match(oceanBookingFixture, /使用 ACTIVE BOOKING 订舱代理事实源/)
|
assert.match(oceanBookingFixture, /使用 ACTIVE BOOKING 订舱代理事实源/)
|
||||||
assert.doesNotMatch(oceanBookingFixture, /category=BOOKING[^'\n]*scope=/)
|
assert.doesNotMatch(oceanBookingFixture, /scope=/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('frozen MATSON and COSCO matrix is many-to-many and changing B to COSCO clears the selection', () => {
|
test('frozen MATSON and COSCO matrix is many-to-many and changing B to COSCO clears the selection', () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict'
|
|||||||
import { readFileSync } from 'node:fs'
|
import { readFileSync } from 'node:fs'
|
||||||
import test from 'node:test'
|
import test from 'node:test'
|
||||||
|
|
||||||
import { oceanBookingDraft, oceanBookingDraftWithoutCustomer, publishPlatformLogisticsChannels, resolveOceanBookingMaster } from './ocean-booking-fixture.mjs'
|
import { oceanBookingDraft, oceanBookingDraftWithoutCustomer, publishPlatformLogisticsChannels, resolveOceanBookingAgent, resolveOceanBookingMaster } from './ocean-booking-fixture.mjs'
|
||||||
|
|
||||||
const read = (name) => readFileSync(new URL(name, import.meta.url), 'utf8')
|
const read = (name) => readFileSync(new URL(name, import.meta.url), 'utf8')
|
||||||
const master = { customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运', warehouseId: '41', warehouseName: '深圳仓' }
|
const master = { customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运', warehouseId: '41', warehouseName: '深圳仓' }
|
||||||
@@ -68,6 +68,16 @@ test('platform channel producer uses the selected origin warehouse and publishes
|
|||||||
assert.deepEqual(context.createdResources.platformLogisticsChannels, catalog)
|
assert.deepEqual(context.createdResources.platformLogisticsChannels, catalog)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('booking agent fixture uses the carrier-filtered server candidates', async () => {
|
||||||
|
const calls = []
|
||||||
|
const context = { request: async (method, path) => {
|
||||||
|
calls.push({ method, path }); return { body: { code: '200', data: [{ supplierId: 81, name: 'MATSON 代理' }] } }
|
||||||
|
}, apiSuccess: (_module, _name, response) => response.body.data,
|
||||||
|
assert: (_module, _name, _response, condition) => assert.equal(condition, true) }
|
||||||
|
assert.deepEqual(await resolveOceanBookingAgent(context, 'test', 'matson'), { id: '81', name: 'MATSON 代理' })
|
||||||
|
assert.deepEqual(calls, [{ method: 'GET', path: '/api/platform/v1/ocean-bookings/booking-agents?carrierCode=MATSON' }])
|
||||||
|
})
|
||||||
|
|
||||||
test('all production booking fixtures use DRAFT release and the shared requirements contract', () => {
|
test('all production booking fixtures use DRAFT release and the shared requirements contract', () => {
|
||||||
for (const name of ['ocean-bookings.mjs', 'phase3-loading.mjs', 'phase3-container-loading.mjs', 'phase3-order-splitting.mjs', 'manual-state-loading-pda.mjs', 'manual-state-ocean-trucking.mjs']) {
|
for (const name of ['ocean-bookings.mjs', 'phase3-loading.mjs', 'phase3-container-loading.mjs', 'phase3-order-splitting.mjs', 'manual-state-loading-pda.mjs', 'manual-state-ocean-trucking.mjs']) {
|
||||||
const source = read(name)
|
const source = read(name)
|
||||||
|
|||||||
@@ -52,6 +52,19 @@ export async function resolveOceanBookingMaster(context, module) {
|
|||||||
return master
|
return master
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resolveOceanBookingAgent(context, module, carrierCode, options = {}) {
|
||||||
|
const normalizedCarrierCode = text(carrierCode).toUpperCase()
|
||||||
|
const response = await context.request('GET',
|
||||||
|
`/api/platform/v1/ocean-bookings/booking-agents?carrierCode=${encodeURIComponent(normalizedCarrierCode)}`)
|
||||||
|
const candidates = context.apiSuccess(module, '按船司读取可用订舱代理', response,
|
||||||
|
`carrierCode=${normalizedCarrierCode}`, options)
|
||||||
|
const selected = (Array.isArray(candidates) ? candidates : []).find((item) =>
|
||||||
|
text(item?.supplierId) && text(item?.name))
|
||||||
|
context.assert(module, '订舱代理已由服务端按船司关联过滤', response,
|
||||||
|
Boolean(selected), `carrierCode=${normalizedCarrierCode}`, options)
|
||||||
|
return selected ? { id: text(selected.supplierId), name: text(selected.name) } : null
|
||||||
|
}
|
||||||
|
|
||||||
export function oceanBookingDraft(context, suffix, master, labels = {}) {
|
export function oceanBookingDraft(context, suffix, master, labels = {}) {
|
||||||
const { channelName: _readOnlyChannelName, warehouseName: _readOnlyWarehouseName, ...writableMaster } = master
|
const { channelName: _readOnlyChannelName, warehouseName: _readOnlyWarehouseName, ...writableMaster } = master
|
||||||
const normalizedSuffix = text(suffix).replace(/[^A-Za-z0-9]/g, '').slice(-32) || 'BOOKING'
|
const normalizedSuffix = text(suffix).replace(/[^A-Za-z0-9]/g, '').slice(-32) || 'BOOKING'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const MODULE = 'ocean-bookings'
|
const MODULE = 'ocean-bookings'
|
||||||
const OPTIONS = { scenario: 'ocean-booking-lifecycle', deploymentBlocker: true }
|
const OPTIONS = { scenario: 'ocean-booking-lifecycle', deploymentBlocker: true }
|
||||||
const BASE = '/api/platform/v1/ocean-bookings'
|
const BASE = '/api/platform/v1/ocean-bookings'
|
||||||
import { oceanBookingDraft, resolveOceanBookingMaster } from './ocean-booking-fixture.mjs'
|
import { oceanBookingDraft, resolveOceanBookingAgent, resolveOceanBookingMaster } from './ocean-booking-fixture.mjs'
|
||||||
import { oceanBookingFieldGaps } from './fulfillment-list-field-contract.mjs'
|
import { oceanBookingFieldGaps } from './fulfillment-list-field-contract.mjs'
|
||||||
import { ensureMatsonCarrierPartner } from './carrier-partner-fixture.mjs'
|
import { ensureMatsonCarrierPartner } from './carrier-partner-fixture.mjs'
|
||||||
|
|
||||||
@@ -46,14 +46,12 @@ export async function run(context) {
|
|||||||
const master = await resolveOceanBookingMaster(context, MODULE)
|
const master = await resolveOceanBookingMaster(context, MODULE)
|
||||||
if (!master) return
|
if (!master) return
|
||||||
const suffix = context.marker.replace(/\D/g, '').slice(-10) || 'BOOKING'
|
const suffix = context.marker.replace(/\D/g, '').slice(-10) || 'BOOKING'
|
||||||
const agentResponse = await request(context, 'GET', '/api/platform/v1/suppliers?tenantId=0&category=BOOKING&status=ACTIVE&page=1&size=20')
|
const agent = await resolveOceanBookingAgent(context, MODULE, 'MATSON', OPTIONS)
|
||||||
const agentPage = payload(agentResponse)
|
step(context, '使用 ACTIVE BOOKING 订舱代理事实源', null, Boolean(agent?.id && agent?.name),
|
||||||
const agent = agentPage?.records?.find?.((item) => item?.category === 'BOOKING' && item?.status === 'ACTIVE')
|
`supplierId=${id(agent?.id) || 'missing'}`)
|
||||||
step(context, '使用 ACTIVE BOOKING 订舱代理事实源', agentResponse, Boolean(agent?.id && agent?.name),
|
|
||||||
`supplierId=${id(agent?.id) || 'missing'}, scope=${agent?.scope || 'not-exposed'}`)
|
|
||||||
if (!agent?.id || !agent?.name) return
|
if (!agent?.id || !agent?.name) return
|
||||||
const carrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
|
const carrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
|
||||||
step(context, '使用 ACTIVE CARRIER 船公司事实源', agentResponse,
|
step(context, '使用 ACTIVE CARRIER 船公司事实源', null,
|
||||||
Boolean(carrier?.id && carrier?.carrierCode === 'MATSON'), `partnerId=${id(carrier?.id) || 'missing'}`)
|
Boolean(carrier?.id && carrier?.carrierCode === 'MATSON'), `partnerId=${id(carrier?.id) || 'missing'}`)
|
||||||
if (!carrier?.id) return
|
if (!carrier?.id) return
|
||||||
const data = fullDraft(context, suffix, master, {
|
const data = fullDraft(context, suffix, master, {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { publishPlatformLogisticsChannels } from './ocean-booking-fixture.mjs'
|
import { publishPlatformLogisticsChannels, resolveOceanBookingAgent } from './ocean-booking-fixture.mjs'
|
||||||
import { diffPushPreloadStability, projectPushPreloadStability } from './push-preload-stability.mjs'
|
import { diffPushPreloadStability, projectPushPreloadStability } from './push-preload-stability.mjs'
|
||||||
import { containerFieldGaps, loadingListFieldGaps, preloadFieldGaps } from './fulfillment-list-field-contract.mjs'
|
import { containerFieldGaps, loadingListFieldGaps, preloadFieldGaps } from './fulfillment-list-field-contract.mjs'
|
||||||
import { ensureMatsonCarrierPartner } from './carrier-partner-fixture.mjs'
|
import { ensureMatsonCarrierPartner } from './carrier-partner-fixture.mjs'
|
||||||
@@ -162,16 +162,13 @@ export async function run(context) {
|
|||||||
const primaryRequirement = { containerType: '40HC', containerKind: 'FCL', capacityCbm: 76, maxWeightKg: 28000 }
|
const primaryRequirement = { containerType: '40HC', containerKind: 'FCL', capacityCbm: 76, maxWeightKg: 28000 }
|
||||||
const realContainerNo = iso6346ContainerNumber(`${context.marker}-initial`)
|
const realContainerNo = iso6346ContainerNumber(`${context.marker}-initial`)
|
||||||
const wrongContainerNo = `TST-${suffix}-02`.slice(0, 64)
|
const wrongContainerNo = `TST-${suffix}-02`.slice(0, 64)
|
||||||
const agentResponse = await context.request('GET',
|
const bookingAgent = await resolveOceanBookingAgent(context, MODULE, 'MATSON', OPTIONS)
|
||||||
'/api/platform/v1/suppliers?tenantId=0&category=BOOKING&status=ACTIVE&page=1&size=20')
|
context.assert(MODULE, '放舱订舱使用真实启用供应商', null,
|
||||||
const agentPage = context.apiSuccess(MODULE, '查询放舱所需 ACTIVE 订舱代理', agentResponse, '', OPTIONS)
|
|
||||||
const bookingAgent = records(agentPage).find((item) => item?.category === 'BOOKING' && item?.status === 'ACTIVE')
|
|
||||||
context.assert(MODULE, '放舱订舱使用真实启用供应商', agentResponse,
|
|
||||||
Boolean(bookingAgent?.id && bookingAgent?.name),
|
Boolean(bookingAgent?.id && bookingAgent?.name),
|
||||||
`supplierId=${id(bookingAgent?.id) || 'missing'}`, OPTIONS)
|
`supplierId=${id(bookingAgent?.id) || 'missing'}`, OPTIONS)
|
||||||
if (!bookingAgent?.id || !bookingAgent?.name) return
|
if (!bookingAgent?.id || !bookingAgent?.name) return
|
||||||
const shippingCarrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
|
const shippingCarrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
|
||||||
context.assert(MODULE, '放舱船公司使用真实启用CARRIER往来单位', agentResponse,
|
context.assert(MODULE, '放舱船公司使用真实启用CARRIER往来单位', null,
|
||||||
Boolean(shippingCarrier?.id && shippingCarrier?.carrierCode === 'MATSON'),
|
Boolean(shippingCarrier?.id && shippingCarrier?.carrierCode === 'MATSON'),
|
||||||
`partnerId=${id(shippingCarrier?.id) || 'missing'}`, OPTIONS)
|
`partnerId=${id(shippingCarrier?.id) || 'missing'}`, OPTIONS)
|
||||||
if (!shippingCarrier?.id) return
|
if (!shippingCarrier?.id) return
|
||||||
|
|||||||
Reference in New Issue
Block a user