feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -71,10 +71,10 @@ test('claim delete flow invalidates the matching financial assistant session', (
fileURLToPath(new URL('../src/views/AppShellRouteView.vue', import.meta.url)),
'utf8'
)
const createViewScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/TravelReimbursementCreateView.js', import.meta.url)),
'utf8'
)
const createViewScript = [
'../src/views/scripts/TravelReimbursementCreateView.js',
'../src/views/scripts/useTravelReimbursementCreateViewSessionCleanup.js'
].map((path) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')).join('\n')
assert.match(appShellScript, /clearAssistantSessionSnapshotForDraftClaim/)
assert.match(appShellScript, /async function handleRequestDeleted\(payload = \{\}\)/)
@@ -194,8 +194,12 @@ test('saving a draft keeps the financial assistant open for continued work', ()
})
test('detail smart entry is scoped to the current claim instead of the latest conversation', () => {
const detailViewScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/TravelRequestDetailView.js', import.meta.url)),
const detailExpenseEditorScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelRequestDetailExpenseEditor.js', import.meta.url)),
'utf8'
)
const smartEntryRecognitionScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/travelRequestDetailSmartEntryRecognition.js', import.meta.url)),
'utf8'
)
const appShellScript = readFileSync(
@@ -206,13 +210,17 @@ test('detail smart entry is scoped to the current claim instead of the latest co
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSessionState.js', import.meta.url)),
'utf8'
)
const submitComposerScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSubmitComposer.js', import.meta.url)),
'utf8'
)
const submitComposerScript = [
'../src/views/scripts/useTravelReimbursementSubmitComposer.js',
'../src/views/scripts/travelReimbursementSubmitApplicationPreview.js'
].map((path) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')).join('\n')
assert.match(detailViewScript, /restoreLatestConversation:\s*false/)
assert.match(detailViewScript, /scope:\s*claimId[\s\S]*type:\s*'claim'[\s\S]*claimId/)
assert.match(detailExpenseEditorScript, /if \(!request\.value\.claimId\) \{[\s\S]*当前草稿缺少 claimId/)
assert.match(detailExpenseEditorScript, /startSmartEntryRecognitionTask\(\{[\s\S]*claimId:\s*request\.value\.claimId/)
assert.match(detailExpenseEditorScript, /bindSmartEntryRecognitionTask\(request\.value\.claimId\)/)
assert.match(smartEntryRecognitionScript, /const normalizedClaimId = normalizeSmartEntryClaimId\(claimId\)/)
assert.match(smartEntryRecognitionScript, /smartEntryRecognitionTasks\.set\(normalizedClaimId, task\)/)
assert.doesNotMatch(detailExpenseEditorScript, /restoreLatestConversation/)
assert.match(appShellScript, /function isDetailClaimScopedPayload\(payload = \{\}\)/)
assert.match(appShellScript, /if \(isDetailClaimScopedPayload\(payload\)\) \{[\s\S]*return null[\s\S]*\}/)
assert.match(sessionStateScript, /const shouldPersistLocalSnapshot = props\.entrySource !== 'detail'/)