优化
This commit is contained in:
@@ -11,7 +11,8 @@ const financePermissions = [
|
|||||||
'platform:finance:reconciliation:create', 'platform:finance:reconciliation:review',
|
'platform:finance:reconciliation:create', 'platform:finance:reconciliation:review',
|
||||||
'platform:finance:reconciliation:issue', 'platform:finance:reconciliation:adjust',
|
'platform:finance:reconciliation:issue', 'platform:finance:reconciliation:adjust',
|
||||||
'platform:finance:reconciliation:void', 'platform:finance:settlement-ar:create',
|
'platform:finance:reconciliation:void', 'platform:finance:settlement-ar:create',
|
||||||
'platform:finance:settlement-ap:create',
|
'platform:finance:settlement-ar:view', 'platform:finance:settlement-ap:create',
|
||||||
|
'platform:finance:settlement-ap:view',
|
||||||
]
|
]
|
||||||
|
|
||||||
function ok(data) {
|
function ok(data) {
|
||||||
@@ -116,18 +117,26 @@ function fakeContext(withFixture = true, permissions = financePermissions, denyF
|
|||||||
const statement = statements.get(String(body.reconciliationStatementId))
|
const statement = statements.get(String(body.reconciliationStatementId))
|
||||||
assert.equal(statement.status, 'ISSUED')
|
assert.equal(statement.status, 'ISSUED')
|
||||||
const settlement = { settlementId: String(++settlementSequence), direction: 'RECEIVABLE',
|
const settlement = { settlementId: String(++settlementSequence), direction: 'RECEIVABLE',
|
||||||
reconciliationStatementId: body.reconciliationStatementId, status: 'OPEN',
|
customerId: '11', reconciliationStatementId: body.reconciliationStatementId, status: 'OPEN',
|
||||||
paymentStatus: 'UNPAID', version: 0 }
|
paymentStatus: 'UNPAID', totalAmount: 100, paidAmount: 0, outstandingAmount: 100, version: 0 }
|
||||||
settlements.set(settlement.settlementId, settlement)
|
settlements.set(settlement.settlementId, settlement)
|
||||||
if (idem) idempotency.set(idem, settlement)
|
if (idem) idempotency.set(idem, settlement)
|
||||||
return ok(structuredClone(settlement))
|
return ok(structuredClone(settlement))
|
||||||
}
|
}
|
||||||
if (method === 'POST' && requestPath.endsWith('/payable-settlements')) {
|
if (method === 'POST' && requestPath.endsWith('/payable-settlements')) {
|
||||||
const settlement = { settlementId: String(++settlementSequence), direction: 'PAYABLE',
|
const settlement = { settlementId: String(++settlementSequence), direction: 'PAYABLE',
|
||||||
supplierId: String(body.supplierId), status: 'OPEN', paymentStatus: 'UNPAID', version: 0 }
|
customerId: String(body.customerId), supplierId: String(body.supplierId), status: 'OPEN',
|
||||||
|
paymentStatus: 'UNPAID', totalAmount: 100, paidAmount: 0, outstandingAmount: 100, version: 0 }
|
||||||
|
settlements.set(settlement.settlementId, settlement)
|
||||||
if (idem) idempotency.set(idem, settlement)
|
if (idem) idempotency.set(idem, settlement)
|
||||||
return ok(structuredClone(settlement))
|
return ok(structuredClone(settlement))
|
||||||
}
|
}
|
||||||
|
if (method === 'GET' && requestPath.startsWith('/api/platform/finance/v1/receivable-settlements?')) {
|
||||||
|
return ok([...settlements.values()].filter((item) => item.direction === 'RECEIVABLE'))
|
||||||
|
}
|
||||||
|
if (method === 'GET' && requestPath.startsWith('/api/platform/finance/v1/payable-settlements?')) {
|
||||||
|
return ok([...settlements.values()].filter((item) => item.direction === 'PAYABLE'))
|
||||||
|
}
|
||||||
throw new Error(`Unexpected request: ${method} ${requestPath}`)
|
throw new Error(`Unexpected request: ${method} ${requestPath}`)
|
||||||
},
|
},
|
||||||
addStep(module, name, response, passed, evidence = '', options = {}) {
|
addStep(module, name, response, passed, evidence = '', options = {}) {
|
||||||
@@ -160,6 +169,10 @@ test('静态契约覆盖日期、SELECTED、三 AR、AP、状态机和数组输
|
|||||||
assert.match(source, /receivables\.length < 4/)
|
assert.match(source, /receivables\.length < 4/)
|
||||||
assert.match(source, /const groups = \[cutoffSources/)
|
assert.match(source, /const groups = \[cutoffSources/)
|
||||||
assert.match(source, /\/payable-settlements/)
|
assert.match(source, /\/payable-settlements/)
|
||||||
|
assert.match(source, /findSettlementInOfficialList/)
|
||||||
|
assert.match(source, /`正式\$\{label\}列表包含同一 settlementId`/)
|
||||||
|
assert.match(source, /findSettlementInOfficialList\(context, 'RECEIVABLE'/)
|
||||||
|
assert.match(source, /findSettlementInOfficialList\(context, 'PAYABLE'/)
|
||||||
assert.match(source, /fixture\.root\.statements = statementRows/)
|
assert.match(source, /fixture\.root\.statements = statementRows/)
|
||||||
assert.match(source, /fixture\.root\.receivableSettlements = arRows/)
|
assert.match(source, /fixture\.root\.receivableSettlements = arRows/)
|
||||||
assert.doesNotMatch(source, /generationMode:\s*['"]BY_CUTOFF['"]/)
|
assert.doesNotMatch(source, /generationMode:\s*['"]BY_CUTOFF['"]/)
|
||||||
@@ -193,6 +206,10 @@ test('run 发布一条日期链、三张独立已出账单、三张 AR 和一张
|
|||||||
assert.equal(context.calls.some((call) => call.body?.feeSourceIds?.includes(null)), false)
|
assert.equal(context.calls.some((call) => call.body?.feeSourceIds?.includes(null)), false)
|
||||||
assert.equal(context.calls.some((call) => call.body?.feeSourceIds?.includes('106')), false)
|
assert.equal(context.calls.some((call) => call.body?.feeSourceIds?.includes('106')), false)
|
||||||
assert.equal(context.calls.some((call) => call.path.includes('/fee-pool')), true)
|
assert.equal(context.calls.some((call) => call.path.includes('/fee-pool')), true)
|
||||||
|
assert.equal(context.calls.filter((call) => call.method === 'GET'
|
||||||
|
&& call.path.includes('/receivable-settlements?customerId=11')).length, 3)
|
||||||
|
assert.equal(context.calls.filter((call) => call.method === 'GET'
|
||||||
|
&& call.path.includes('/payable-settlements?customerId=11')).length, 1)
|
||||||
assert.equal(context.calls.some((call) => call.body?.generationMode === 'BY_CUTOFF'), false)
|
assert.equal(context.calls.some((call) => call.body?.generationMode === 'BY_CUTOFF'), false)
|
||||||
const failedSteps = context.steps.filter((step) => !step.passed)
|
const failedSteps = context.steps.filter((step) => !step.passed)
|
||||||
assert.equal(failedSteps.length, 0, JSON.stringify(failedSteps, null, 2))
|
assert.equal(failedSteps.length, 0, JSON.stringify(failedSteps, null, 2))
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ const REQUIRED_PERMISSIONS = [
|
|||||||
'platform:finance:reconciliation:adjust',
|
'platform:finance:reconciliation:adjust',
|
||||||
'platform:finance:reconciliation:void',
|
'platform:finance:reconciliation:void',
|
||||||
'platform:finance:settlement-ar:create',
|
'platform:finance:settlement-ar:create',
|
||||||
|
'platform:finance:settlement-ar:view',
|
||||||
'platform:finance:settlement-ap:create',
|
'platform:finance:settlement-ap:create',
|
||||||
|
'platform:finance:settlement-ap:view',
|
||||||
]
|
]
|
||||||
|
|
||||||
function id(value) { return value == null ? '' : String(value) }
|
function id(value) { return value == null ? '' : String(value) }
|
||||||
@@ -43,6 +45,19 @@ function sameIds(actual, expected) {
|
|||||||
const right = [...new Set(expected)].sort()
|
const right = [...new Set(expected)].sort()
|
||||||
return left.length === right.length && left.every((value, index) => value === right[index])
|
return left.length === right.length && left.every((value, index) => value === right[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function findSettlementInOfficialList(context, direction, customerId, targetId, label) {
|
||||||
|
const segment = direction === 'RECEIVABLE' ? 'receivable-settlements' : 'payable-settlements'
|
||||||
|
const response = await context.request('GET',
|
||||||
|
`${BASE}/${segment}?customerId=${encodeURIComponent(customerId)}`)
|
||||||
|
const summaries = context.apiSuccess(MODULE, `从正式${label}列表查回结算单`, response,
|
||||||
|
`settlementId=${targetId}, customerId=${customerId}`, OPTIONS)
|
||||||
|
const found = rows(summaries).find((item) => settlementId(item) === targetId)
|
||||||
|
context.assert(MODULE, `正式${label}列表包含同一 settlementId`, response,
|
||||||
|
found?.direction === direction && id(found?.customerId) === id(customerId),
|
||||||
|
`expected=${targetId}, actual=${settlementId(found) || 'missing'}, direction=${found?.direction ?? 'missing'}`, OPTIONS)
|
||||||
|
return found ?? null
|
||||||
|
}
|
||||||
function dates(sources) {
|
function dates(sources) {
|
||||||
const values = sources.map((source) => source.businessDate).sort()
|
const values = sources.map((source) => source.businessDate).sort()
|
||||||
return { start: values[0], end: values.at(-1) }
|
return { start: values[0], end: values.at(-1) }
|
||||||
@@ -214,6 +229,12 @@ async function createReceivable(context, statement, index) {
|
|||||||
const replay = context.apiSuccess(MODULE, `第 ${index} 张应收结算单幂等重放`, replayResponse, '', OPTIONS)
|
const replay = context.apiSuccess(MODULE, `第 ${index} 张应收结算单幂等重放`, replayResponse, '', OPTIONS)
|
||||||
context.assert(MODULE, `第 ${index} 张 AR 幂等保持同一资源`, replayResponse,
|
context.assert(MODULE, `第 ${index} 张 AR 幂等保持同一资源`, replayResponse,
|
||||||
settlementId(replay) === settlementId(settlement), `settlementId=${settlementId(settlement)}`, OPTIONS)
|
settlementId(replay) === settlementId(settlement), `settlementId=${settlementId(settlement)}`, OPTIONS)
|
||||||
|
const listed = await findSettlementInOfficialList(context, 'RECEIVABLE', settlement.customerId,
|
||||||
|
settlementId(settlement), `应收第 ${index} 张`)
|
||||||
|
context.assert(MODULE, `第 ${index} 张 AR 正式列表金额与创建结果一致`, null,
|
||||||
|
listed && Number(listed.totalAmount) === Number(settlement.totalAmount)
|
||||||
|
&& Number(listed.outstandingAmount) === Number(settlement.outstandingAmount),
|
||||||
|
`createdTotal=${settlement.totalAmount ?? 'missing'}, listedTotal=${listed?.totalAmount ?? 'missing'}`, OPTIONS)
|
||||||
return settlement
|
return settlement
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +408,14 @@ export async function run(context) {
|
|||||||
const response = await write(context, 'POST', `${BASE}/payable-settlements`, body, 'payable-settlement')
|
const response = await write(context, 'POST', `${BASE}/payable-settlements`, body, 'payable-settlement')
|
||||||
payableSettlement = context.apiSuccess(MODULE, '按 Fixture PAYABLE 与 partnerId/supplierId 生成应付结算单',
|
payableSettlement = context.apiSuccess(MODULE, '按 Fixture PAYABLE 与 partnerId/supplierId 生成应付结算单',
|
||||||
response, `sources=${payables.length}, supplierId=${supplierId}, supplierName=${supplierName ?? 'null'}`, OPTIONS)
|
response, `sources=${payables.length}, supplierId=${supplierId}, supplierName=${supplierName ?? 'null'}`, OPTIONS)
|
||||||
|
if (payableSettlement) {
|
||||||
|
const listed = await findSettlementInOfficialList(context, 'PAYABLE', fixture.customerId,
|
||||||
|
settlementId(payableSettlement), '应付')
|
||||||
|
context.assert(MODULE, 'AP 正式列表供应商与金额和创建结果一致', null,
|
||||||
|
listed && id(listed.supplierId) === id(payableSettlement.supplierId)
|
||||||
|
&& Number(listed.totalAmount) === Number(payableSettlement.totalAmount),
|
||||||
|
`createdSupplier=${id(payableSettlement.supplierId)}, listedSupplier=${id(listed?.supplierId) || 'missing'}`, OPTIONS)
|
||||||
|
}
|
||||||
if (!payableSettlement) {
|
if (!payableSettlement) {
|
||||||
blocker(context, 'PAYABLE 真实接口创建失败',
|
blocker(context, 'PAYABLE 真实接口创建失败',
|
||||||
`HTTP=${response.http}, code=${response.body?.code ?? 'missing'}, requestId=${response.requestId ?? 'null'}, traceId=${response.traceId ?? 'null'}`,
|
`HTTP=${response.http}, code=${response.body?.code ?? 'missing'}, requestId=${response.requestId ?? 'null'}, traceId=${response.traceId ?? 'null'}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user