Merge branch 'test' of http://8.163.30.128:10082/java-project/baiyue-firstleg into dev
# Conflicts: # scripts/admin-test-env-modules/orders.mjs # scripts/admin-test-env-modules/phase3-container-loading.mjs
This commit is contained in:
@@ -38,6 +38,7 @@ test('验收只使用本轮 marker fixture,解析真实 XLSX 并覆盖门禁
|
||||
assert.match(source, /Provider 失败信息可见/)
|
||||
assert.match(source, /9223372036854775806/)
|
||||
assert.doesNotMatch(source, /exportType:\s*['"]CUSTOMER_LIST/)
|
||||
assert.doesNotMatch(source, /FINANCE_FEE_SOURCE_LIST:[^\n]*参与结算/)
|
||||
})
|
||||
|
||||
test('未部署时能力探测后记录部署前置,不借历史业务数据', async () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const REQUIRED_PERMISSIONS = Object.freeze([
|
||||
export const EXPORT_SPECS = Object.freeze({
|
||||
FINANCE_CUSTOMER_ACCOUNT_LIST: ['客户编码', '客户', '联系人', '联系电话', '待收(CNY)', '待开账(CNY)', '本期已收(CNY)', '待付(CNY)', '客户状态', '最近变动'],
|
||||
FINANCE_CUSTOMER_ACCOUNT_LEDGER: ['流水号', '发生时间', '业务类型', '关联单据', '应收变动(CNY)', '应付变动(CNY)', '变动后未收(CNY)', '操作人', '备注'],
|
||||
FINANCE_FEE_SOURCE_LIST: ['客户', '收付方向', '来源类型', '来源单据ID', '来源版本', '入仓号', '柜次号', '业务发生日', '费用项目', '计费量', '单价(CNY)', '金额(CNY)', '审核状态', '参与结算', '来源状态', '已生成对账单'],
|
||||
FINANCE_FEE_SOURCE_LIST: ['客户', '收付方向', '来源类型', '来源单据ID', '来源版本', '入仓号', '柜次号', '业务发生日', '费用项目', '计费量', '单价(CNY)', '金额(CNY)', '审核状态', '来源状态', '已生成对账单'],
|
||||
FINANCE_RECONCILIATION_STATEMENT_LIST: ['对账单号', '客户', '结算起始日', '结算结束日', '账期(天)', '货币', '总应收金额(CNY)', '已收金额(CNY)', '未收金额(CNY)', '是否结清', '审核状态', '备注', '创建人', '创建时间', '更新人', '更新时间'],
|
||||
FINANCE_RECEIVABLE_SETTLEMENT_LIST: ['结算单号', '客户', '供应商', '货币', '总金额(CNY)', '已收/付金额(CNY)', '未收/付金额(CNY)', '业务期间', '生成日期', '到期日期', '状态'],
|
||||
FINANCE_RECEIVABLE_SETTLEMENT_DETAIL: ['结算单号', '客户', '供应商', '费用项目', '入仓号', '柜次号', '业务发生日', '明细金额(CNY)', '结算总额(CNY)', '已收/付金额(CNY)', '未收/付金额(CNY)', '状态'],
|
||||
|
||||
@@ -38,6 +38,7 @@ test('PAYABLE fee selection uses only the explicit nullable supplierCategory fie
|
||||
assert.equal(supplierCategoryForFeeItem({ supplierCategory: 'BOOKING', feeTypeName: '操作费' }), 'BOOKING')
|
||||
assert.equal(supplierCategoryForFeeItem({ supplierCategory: null, feeTypeName: '拖车费' }), null)
|
||||
assert.equal(supplierCategoryForFeeItem({ supplierCategory: 'INVALID' }), null)
|
||||
assert.match(source, /payableSupplierCategory && source\.supplierCategory !== payableSupplierCategory/)
|
||||
})
|
||||
|
||||
test('Finance PAYABLE fixture creates an isolated ACTIVE supplier through the formal API', () => {
|
||||
|
||||
@@ -498,7 +498,7 @@ export async function run(context) {
|
||||
const invalidFixtureSources = fixtureSources.filter((source) => !source.feeSourceId || !source.customerId
|
||||
|| !source.businessDate || !(source.waybillId || source.shipmentId || source.orderNo) || source.marker !== context.marker
|
||||
|| (source.direction === 'PAYABLE' && (!source.supplierId || !source.supplierName
|
||||
|| source.supplierCategory !== payableSupplierCategory)))
|
||||
|| (payableSupplierCategory && source.supplierCategory !== payableSupplierCategory))))
|
||||
context.assert(MODULE, '输出完整 Finance 来源契约', null, invalidFixtureSources.length === 0,
|
||||
`sources=${fixtureSources.length}, invalid=${invalidFixtureSources.length};字段必须来自本轮费用来源、客户、订单/运单和真实柜次分摊`, OPTIONS)
|
||||
context.createdResources.financeFullFixtures = {
|
||||
|
||||
@@ -15,7 +15,8 @@ test('Finance AP consumes only published fin_fee_source rows and never substitut
|
||||
assert.match(source, /!isUnboundContainerCost\(source\.raw\) \|\| hasContainerAllocations/)
|
||||
})
|
||||
const financePermissions = [
|
||||
'platform:finance:customer-account:list', 'platform:finance:customer-statement:list',
|
||||
'platform:finance:customer-account:list', 'platform:finance:customer-arap:list',
|
||||
'platform:finance:fee-source:review', 'platform:finance:customer-statement:list',
|
||||
'platform:finance:reconciliation:create', 'platform:finance:reconciliation:review',
|
||||
'platform:finance:reconciliation:issue', 'platform:finance:reconciliation:adjust',
|
||||
'platform:finance:reconciliation:void', 'platform:finance:settlement-ar:create',
|
||||
@@ -29,6 +30,8 @@ function ok(data) {
|
||||
|
||||
function fakeContext(withFixture = true, permissions = financePermissions, denyFinanceWrites = false,
|
||||
missingSubject = false, omitPayableSource = false) {
|
||||
process.env.MERCHANT_TEST_USERNAME = 'fixture-client'
|
||||
process.env.MERCHANT_TEST_PASSWORD = 'fixture-password'
|
||||
const marker = 'FIN-TEST-MARKER'
|
||||
const receivables = [
|
||||
{ feeSourceId: '101', orderId: '201', direction: 'RECEIVABLE', currency: 'CNY', businessDate: '2026-08-10' },
|
||||
@@ -58,12 +61,13 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
||||
const calls = []
|
||||
const steps = []
|
||||
const coverageGaps = []
|
||||
const reviewedSourceIds = new Set()
|
||||
|
||||
function responseForStatement(statement) { return structuredClone(statement) }
|
||||
function applyAction(statement, body) {
|
||||
const status = { SUBMIT: 'PENDING_REVIEW', APPROVE: 'APPROVED', REJECT: 'REJECTED',
|
||||
ISSUE: 'ISSUED', ADJUST: 'ISSUED', VOID: 'VOIDED', REOPEN: 'DRAFT' }[body.action]
|
||||
statement.revisions.push({ action: `RECONCILIATION_${body.action}` })
|
||||
statement.revisions.push({ action: body.action })
|
||||
statement.status = status
|
||||
statement.version += 1
|
||||
if (body.action === 'ADJUST') {
|
||||
@@ -73,6 +77,8 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
||||
line.adjustmentReason = adjustment.reason
|
||||
}
|
||||
}
|
||||
if (body.action === 'ISSUE') statement.customerConfirmationStatus = 'PENDING_CONFIRMATION'
|
||||
if (body.action === 'VOID' || body.action === 'REOPEN') statement.customerConfirmationStatus = 'NOT_REQUIRED'
|
||||
return responseForStatement(statement)
|
||||
}
|
||||
|
||||
@@ -86,6 +92,9 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
||||
if (method === 'GET' && requestPath === '/api/platform/v1/iam/authorization/permissions') {
|
||||
return ok(permissions)
|
||||
}
|
||||
if (method === 'POST' && requestPath === '/api/auth/login') {
|
||||
return ok({ accessToken: 'client-token' })
|
||||
}
|
||||
if (method === 'GET' && requestPath === '/api/platform/finance/v1/accounts?page=1&pageSize=20&customerId=11') {
|
||||
return ok({ records: [{ customerId: '11', customerCode: 'TEN11', customerName: 'Fixture Account',
|
||||
contactName: 'Fixture Contact', contactPhone: '13800000000', outstandingReceivable: 100,
|
||||
@@ -112,6 +121,20 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
||||
reviewStatus: 'APPROVED', sourceStatus: 'AVAILABLE' }
|
||||
}))
|
||||
}
|
||||
const feeSourceDetailMatch = requestPath.match(/\/fee-sources\/(\d+)$/)
|
||||
if (method === 'GET' && feeSourceDetailMatch) {
|
||||
const localId = feeSourceDetailMatch[1]
|
||||
return ok({ feeSourceId: localId, reviewStatus: reviewedSourceIds.has(localId) ? 'APPROVED' : 'PENDING',
|
||||
sourceStatus: 'AVAILABLE', statementGenerated: false, operationVersion: reviewedSourceIds.has(localId) ? 1 : 0 })
|
||||
}
|
||||
const feeSourceReviewMatch = requestPath.match(/\/fee-sources\/(\d+)\/review$/)
|
||||
if (method === 'POST' && feeSourceReviewMatch) {
|
||||
reviewedSourceIds.add(feeSourceReviewMatch[1])
|
||||
const reviewed = { feeSourceId: feeSourceReviewMatch[1], reviewStatus: 'APPROVED',
|
||||
sourceStatus: 'AVAILABLE', statementGenerated: false, operationVersion: 1 }
|
||||
if (idem) idempotency.set(idem, reviewed)
|
||||
return ok(reviewed)
|
||||
}
|
||||
if (method === 'POST' && requestPath.endsWith('/statements')) {
|
||||
if (body.currency !== 'CNY') return { http: 200, body: { code: 'FIN_400' } }
|
||||
if (String(body.customerId) !== '11') return { http: 200, body: { code: 'FIN_404' } }
|
||||
@@ -166,6 +189,23 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
||||
createdBy: item.createdBy, createdAt: item.createdAt, updatedBy: item.updatedBy, updatedAt: item.updatedAt,
|
||||
})))
|
||||
}
|
||||
if (method === 'GET' && requestPath === '/api/client/finance/v1/statements') {
|
||||
return ok([...statements.values()].map((item) => ({
|
||||
statementId: item.statementId, customerId: item.customerId, status: item.status,
|
||||
version: item.version, customerConfirmationStatus: item.customerConfirmationStatus,
|
||||
})))
|
||||
}
|
||||
const clientConfirmMatch = requestPath.match(/\/api\/client\/finance\/v1\/statements\/(\d+)\/confirm$/)
|
||||
if (method === 'POST' && clientConfirmMatch) {
|
||||
const statement = statements.get(clientConfirmMatch[1])
|
||||
statement.customerConfirmationStatus = 'CONFIRMED'
|
||||
statement.version += 1
|
||||
const result = { statementId: statement.statementId, confirmationStatus: 'CONFIRMED',
|
||||
confirmedBy: '7002', confirmedByName: 'fixture-client', confirmedAt: '2026-08-10T10:00:00',
|
||||
version: statement.version }
|
||||
if (idem) idempotency.set(idem, result)
|
||||
return ok(structuredClone(result))
|
||||
}
|
||||
const detailMatch = requestPath.match(/\/reconciliation-statements\/(\d+)$/)
|
||||
if (method === 'GET' && detailMatch) return ok(responseForStatement(statements.get(detailMatch[1])))
|
||||
throw new Error(`Unexpected request: ${method} ${requestPath}`)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
const MODULE = 'finance-reconciliation-full'
|
||||
const SCENARIO = 'finance-full-reconciliation'
|
||||
const BASE = '/api/platform/finance/v1'
|
||||
const CLIENT_BASE = '/api/client/finance/v1'
|
||||
const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true }
|
||||
const STATEMENT_LIST_PERMISSION = 'platform:finance:customer-statement:list'
|
||||
const LEGACY_STATEMENT_VIEW_PERMISSION = 'platform:finance:bill:view'
|
||||
const REQUIRED_PERMISSIONS = [
|
||||
'platform:finance:customer-account:list',
|
||||
'platform:finance:customer-arap:list',
|
||||
'platform:finance:fee-source:review',
|
||||
STATEMENT_LIST_PERMISSION,
|
||||
'platform:finance:reconciliation:create',
|
||||
'platform:finance:reconciliation:review',
|
||||
@@ -116,6 +119,76 @@ function finish(context, start) {
|
||||
failed: steps.filter((step) => !step.passed).length,
|
||||
})
|
||||
}
|
||||
|
||||
async function confirmIssuedStatementAsClient(context, fixture, statement) {
|
||||
const username = process.env.MERCHANT_TEST_USERNAME
|
||||
const password = process.env.MERCHANT_TEST_PASSWORD
|
||||
const targetId = statementId(statement)
|
||||
if (!username || !password || !targetId) {
|
||||
context.assert(MODULE, '客户确认对账单凭据与单据门禁', null, false,
|
||||
`username=${username ? 'present' : 'missing'}, password=${password ? 'present' : 'missing'}, statementId=${targetId || 'missing'}`, OPTIONS)
|
||||
return null
|
||||
}
|
||||
const loginResponse = await context.request('POST', '/api/auth/login', { username, password }, '')
|
||||
const login = context.apiSuccess(MODULE, 'Client 客户登录用于确认对账单', loginResponse,
|
||||
`username=${username}`, OPTIONS)
|
||||
const clientToken = login?.accessToken
|
||||
if (!clientToken) return null
|
||||
const listResponse = await context.request('GET', `${CLIENT_BASE}/statements`, undefined, clientToken)
|
||||
const statements = context.apiSuccess(MODULE, 'Client 查询本企业对账单', listResponse,
|
||||
`customerId=${fixture.customerId}, statementId=${targetId}`, OPTIONS)
|
||||
const before = rows(statements).find((item) => statementId(item) === targetId)
|
||||
context.assert(MODULE, 'Client 仅取得本企业待确认已出账对账单', listResponse,
|
||||
id(before?.customerId) === fixture.customerId && before?.status === 'ISSUED'
|
||||
&& before?.customerConfirmationStatus === 'PENDING_CONFIRMATION',
|
||||
`statementId=${statementId(before) || 'missing'}, status=${before?.status ?? 'missing'}, confirmation=${before?.customerConfirmationStatus ?? 'missing'}`, OPTIONS)
|
||||
if (!before) return null
|
||||
const headers = { 'Idempotency-Key': key(context, `client-confirm-${targetId}`) }
|
||||
const body = { expectedVersion: Number(before.version) }
|
||||
const confirmResponse = await context.request('POST',
|
||||
`${CLIENT_BASE}/statements/${encodeURIComponent(targetId)}/confirm`, body, clientToken, headers)
|
||||
const confirmed = context.apiSuccess(MODULE, 'Client 确认本人已出账对账单', confirmResponse,
|
||||
`statementId=${targetId}`, OPTIONS)
|
||||
context.assert(MODULE, '客户确认状态、确认人和版本已落地', confirmResponse,
|
||||
statementId(confirmed) === targetId && confirmed?.confirmationStatus === 'CONFIRMED'
|
||||
&& present(confirmed?.confirmedBy) && present(confirmed?.confirmedByName)
|
||||
&& present(confirmed?.confirmedAt) && Number(confirmed?.version) === Number(before.version) + 1,
|
||||
`statementId=${statementId(confirmed) || 'missing'}, confirmation=${confirmed?.confirmationStatus ?? 'missing'}, version=${confirmed?.version ?? 'missing'}`, OPTIONS)
|
||||
const replayResponse = await context.request('POST',
|
||||
`${CLIENT_BASE}/statements/${encodeURIComponent(targetId)}/confirm`, body, clientToken, headers)
|
||||
const replay = context.apiSuccess(MODULE, 'Client 客户确认幂等重放', replayResponse,
|
||||
`statementId=${targetId}`, OPTIONS)
|
||||
context.assert(MODULE, '客户确认幂等不重复递增版本', replayResponse,
|
||||
statementId(replay) === targetId && Number(replay?.version) === Number(confirmed?.version),
|
||||
`firstVersion=${confirmed?.version ?? 'missing'}, replayVersion=${replay?.version ?? 'missing'}`, OPTIONS)
|
||||
fixture.root.clientConfirmation = confirmed
|
||||
return confirmed
|
||||
}
|
||||
|
||||
async function approveFixtureSources(context, fixture) {
|
||||
const approved = []
|
||||
for (const source of fixture.sources) {
|
||||
const detailResponse = await context.request('GET', `${BASE}/fee-sources/${encodeURIComponent(source.feeSourceId)}`)
|
||||
let detail = context.apiSuccess(MODULE, '回读本轮 Finance 费用来源审核状态', detailResponse,
|
||||
`feeSourceId=${source.feeSourceId}`, OPTIONS)
|
||||
if (!detail) return false
|
||||
if (detail.reviewStatus === 'PENDING') {
|
||||
const reviewResponse = await write(context, 'POST', `${BASE}/fee-sources/${encodeURIComponent(source.feeSourceId)}/review`, {
|
||||
action: 'APPROVE', reason: context.marker, expectedVersion: Number(detail.operationVersion),
|
||||
}, `fee-source-review-${source.feeSourceId}`)
|
||||
detail = context.apiSuccess(MODULE, '审核本轮 Finance 费用来源', reviewResponse,
|
||||
`feeSourceId=${source.feeSourceId}`, OPTIONS)
|
||||
context.assert(MODULE, '费用来源审核后成为结算候选', reviewResponse,
|
||||
detail?.reviewStatus === 'APPROVED' && detail?.sourceStatus === 'AVAILABLE'
|
||||
&& detail?.statementGenerated === false,
|
||||
`feeSourceId=${source.feeSourceId}, reviewStatus=${detail?.reviewStatus ?? 'missing'}, sourceStatus=${detail?.sourceStatus ?? 'missing'}`, OPTIONS)
|
||||
}
|
||||
if (detail?.reviewStatus !== 'APPROVED') return false
|
||||
approved.push(source.feeSourceId)
|
||||
}
|
||||
fixture.root.approvedFinanceFeeSourceIds = approved
|
||||
return approved.length === fixture.sources.length
|
||||
}
|
||||
function diagnostic(response) {
|
||||
return { http: response?.http ?? null,
|
||||
code: response?.body?.code == null ? null : String(response.body.code),
|
||||
@@ -306,8 +379,7 @@ async function issueCompleteChain(context, statement) {
|
||||
statement = await act(context, statement, 'ISSUE', 'chain-issue-final')
|
||||
const actions = new Set(list(statement?.revisions).map((revision) => revision.action))
|
||||
context.assert(MODULE, '完整链保留驳回、重开、调整、作废和再次出账 revision', null,
|
||||
['RECONCILIATION_REJECT', 'RECONCILIATION_REOPEN', 'RECONCILIATION_ADJUST',
|
||||
'RECONCILIATION_VOID', 'RECONCILIATION_ISSUE'].every((action) => actions.has(action)),
|
||||
['REJECT', 'REOPEN', 'ADJUST', 'VOID', 'ISSUE'].every((action) => actions.has(action)),
|
||||
`actions=${[...actions].join(',')}`, OPTIONS)
|
||||
return statement
|
||||
}
|
||||
@@ -445,6 +517,13 @@ export async function run(context) {
|
||||
const groups = [cutoffSources, ...(() => {
|
||||
return [remainingSources.slice(0, 1), remainingSources.slice(1)]
|
||||
})()]
|
||||
if (!await approveFixtureSources(context, fixture)) {
|
||||
blocker(context, '本轮 Finance 费用来源全部审核通过',
|
||||
'费用来源未完成 PENDING -> APPROVED,禁止跳过审核直接生成对账单',
|
||||
'finance-fee-source-review-failed')
|
||||
finish(context, start)
|
||||
return
|
||||
}
|
||||
const issuedStatements = []
|
||||
for (let index = 0; index < groups.length; index += 1) {
|
||||
const created = await createStatement(context, groups[index], `selected-${index + 1}`,
|
||||
@@ -506,6 +585,8 @@ export async function run(context) {
|
||||
}
|
||||
}
|
||||
|
||||
await confirmIssuedStatementAsClient(context, fixture, issuedStatements[0])
|
||||
|
||||
const receivableSettlements = []
|
||||
for (let index = 0; index < issuedStatements.length; index += 1) {
|
||||
const settlement = await createReceivable(context, issuedStatements[index], index + 1)
|
||||
|
||||
@@ -23,6 +23,12 @@ test('measurable inbound without fulfillment task remains pending', () => {
|
||||
}, { http: 200, body: { code: 'A0404' } }, tenantId, orderId), false)
|
||||
})
|
||||
|
||||
test('approval polling remains pending before the first fulfillment task response', () => {
|
||||
assert.equal(approvedInboundReady({
|
||||
summary: { inboundId: null, mainStatus: 'PENDING_REVIEW' },
|
||||
}, null, tenantId, orderId), false)
|
||||
})
|
||||
|
||||
test('approval convergence requires matching inbound state, order and customer task identity', () => {
|
||||
const detail = { summary: { inboundId: '40001', mainStatus: 'WAIT_MEASURE' } }
|
||||
assert.equal(approvedInboundReady(detail, taskResponse, tenantId, orderId), true)
|
||||
|
||||
@@ -144,22 +144,23 @@ export async function run(context) {
|
||||
OPTIONS)
|
||||
if (!selectedChannel) return
|
||||
const channelName = selectedChannel.channelName
|
||||
const supplierResponse = await context.request('GET',
|
||||
'/api/platform/v1/suppliers?tenantId=0&category=BOOKING&status=ACTIVE&page=1&size=20')
|
||||
const supplierPage = context.apiSuccess(MODULE, '读取 ACTIVE 订舱代理作为运输事实源', supplierResponse, '', OPTIONS)
|
||||
const supplier = supplierPage?.records?.find?.((value) => value?.id && value?.name)
|
||||
context.assert(MODULE, '存在可用于发布订舱的 ACTIVE 订舱代理', supplierResponse,
|
||||
Boolean(supplier?.id && supplier?.name), `supplierId=${id(supplier?.id) || 'missing'}`, OPTIONS)
|
||||
if (!supplier?.id) return
|
||||
const primaryRequirement = { containerType: '40HC', containerKind: 'FCL', capacityCbm: 76, maxWeightKg: 28000 }
|
||||
const realContainerNo = `TST-${suffix}-01`.slice(0, 64)
|
||||
const wrongContainerNo = `TST-${suffix}-02`.slice(0, 64)
|
||||
const agentResponse = await context.request('GET',
|
||||
'/api/platform/v1/suppliers?tenantId=0&category=BOOKING&status=ACTIVE&page=1&size=20')
|
||||
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),
|
||||
`supplierId=${id(bookingAgent?.id) || 'missing'}`, OPTIONS)
|
||||
if (!bookingAgent?.id || !bookingAgent?.name) return
|
||||
const bookingDraft = {
|
||||
customerId: tenantId, customerName: inbound?.summary?.customerName || `验收客户-${tenantId}`,
|
||||
channelCode, warehouseId,
|
||||
billOfLadingNo: `BL-${suffix}`.slice(0, 128),
|
||||
shippingCompanyId: id(supplier.id), shippingCompanyName: supplier.name,
|
||||
bookingAgentId: id(supplier.id), bookingAgentName: supplier.name,
|
||||
shippingCompanyId: id(bookingAgent.id), shippingCompanyName: bookingAgent.name,
|
||||
bookingAgentId: id(bookingAgent.id), bookingAgentName: bookingAgent.name,
|
||||
vesselName: `验收船-${suffix}`.slice(0, 128), voyageNo: `V-${suffix}`.slice(0, 64),
|
||||
departurePortCode: 'CNSZX', departurePortName: '深圳',
|
||||
destinationPortCode: 'USLAX', destinationPortName: '洛杉矶', placeOfDelivery: 'LAX',
|
||||
|
||||
@@ -372,10 +372,9 @@ test('container loading enforces channel isolation and stable PDA identity acros
|
||||
assert.match(value, /id\(replacedTask\?\.cabinetNo\) === stableIdentity\.cabinetNo/)
|
||||
assert.match(value, /afterReplacementSnapshot\?\.snapshotToken === beforeReplacementSnapshot\?\.snapshotToken/)
|
||||
assert.match(value, /pdaIdentityStable: true/)
|
||||
assert.match(value, /shippingCompanyId: null, shippingCompanyName: null/)
|
||||
assert.match(value, /bookingAgentId: null, bookingAgentName: null/)
|
||||
assert.doesNotMatch(value, /shippingCompanyName: `验收船公司-/)
|
||||
assert.doesNotMatch(value, /bookingAgentName: `验收代理-/)
|
||||
assert.match(value, /category=BOOKING&status=ACTIVE/)
|
||||
assert.match(value, /shippingCompanyId: id\(bookingAgent\.id\), shippingCompanyName: bookingAgent\.name/)
|
||||
assert.match(value, /bookingAgentId: id\(bookingAgent\.id\), bookingAgentName: bookingAgent\.name/)
|
||||
assert.match(value, /const frozenTransport = Object\.fromEntries\(transportFields\.map/)
|
||||
assert.match(value, /if \(!frozenTransportMatches\) return/)
|
||||
assert.match(value, /expectedVersion: Number\(bookingCreatedDraft\?\.oceanBooking\?\.version\), reason: context\.marker/)
|
||||
|
||||
Reference in New Issue
Block a user