feat(web): 报销单新增关联申请单门控与草稿检测流程
- 新增 travelReimbursementAssociationGateModel,查询可关联申请单/草稿报销单并生成跳过/选择/单独新建动作,区分差旅费与业务招待费类型 - travelReimbursementApplicationLinkModel 补充 buildLinkedApplicationReferenceIndex/buildRequiredApplicationActions 等关联构建逻辑 - useTravelReimbursementSuggestedActions 接入 select_required_application/skip 系列动作,'我要报销'入口改为先走关联门控 - useWorkbenchAiActionRouter 新增 SKIP_REQUIRED_APPLICATION_LINK/SKIP_REIMBURSEMENT_DRAFT_CHECK 动作分发 - useWorkbenchAiExpenseFlow 暴露 startAiReimbursementAssociationGate,stewardPlanModel 待处理流程适配 - 新增 workbench-ai-action-router、workbench-ai-reimbursement-association-gate 测试并更新 guided-flow、steward-plan 测试
This commit is contained in:
@@ -56,6 +56,15 @@ import {
|
||||
filterRequiredApplicationCandidates,
|
||||
requiresApplicationBeforeReimbursement
|
||||
} from '../src/views/scripts/travelReimbursementApplicationLinkModel.js'
|
||||
import {
|
||||
SKIP_REQUIRED_APPLICATION_LINK_ACTION,
|
||||
buildReimbursementAssociationActions,
|
||||
buildReimbursementAssociationMissingText,
|
||||
buildReimbursementAssociationQueryPayload,
|
||||
buildReimbursementAssociationSelectionText,
|
||||
buildReimbursementAssociationSubmitOptions,
|
||||
buildReimbursementAssociationThinkingEvents
|
||||
} from '../src/views/scripts/travelReimbursementAssociationGateModel.js'
|
||||
import {
|
||||
ASSISTANT_SCOPE_ACTION_SWITCH,
|
||||
resolveAssistantScopeGuard
|
||||
@@ -335,6 +344,28 @@ test('guided reimbursement requires application selection for travel and enterta
|
||||
assert.equal(actions[0].action_type, GUIDED_ACTION_SELECT_REQUIRED_APPLICATION)
|
||||
assert.equal(actions[0].payload.application_claim_no, 'AP-202605-001')
|
||||
|
||||
const associationActions = buildReimbursementAssociationActions(travelApplications, '我要报销')
|
||||
assert.equal(associationActions[0].action_type, 'select_required_application')
|
||||
assert.equal(associationActions[0].payload.application_claim_no, 'AP-202605-001')
|
||||
assert.equal(associationActions.at(-1).action_type, SKIP_REQUIRED_APPLICATION_LINK_ACTION)
|
||||
assert.match(buildReimbursementAssociationSelectionText(travelApplications), /单独新建报销单/)
|
||||
assert.match(buildReimbursementAssociationSelectionText(travelApplications), /ai-document-card-list/)
|
||||
assert.match(buildReimbursementAssociationSelectionText(travelApplications), /ai-document-card--application/)
|
||||
assert.match(buildReimbursementAssociationMissingText(), /单独新建报销单/)
|
||||
const associationQueryPayload = buildReimbursementAssociationQueryPayload(travelApplications)
|
||||
assert.equal(associationQueryPayload.selectionMode, 'reimbursement_application_association')
|
||||
assert.equal(associationQueryPayload.records[0].claimNo, 'AP-202605-001')
|
||||
const completedThinking = buildReimbursementAssociationThinkingEvents('completed', { candidateCount: 1 })
|
||||
assert.equal(completedThinking[0].title, '判断用户意图')
|
||||
assert.equal(completedThinking.at(-1).status, 'completed')
|
||||
const associationSubmitOptions = buildReimbursementAssociationSubmitOptions(
|
||||
associationActions[0].payload,
|
||||
'我要报销'
|
||||
)
|
||||
assert.equal(associationSubmitOptions.skipDraftAssociationPrompt, true)
|
||||
assert.equal(associationSubmitOptions.extraContext.expense_scene_selection.application_claim_no, 'AP-202605-001')
|
||||
assert.equal(associationSubmitOptions.extraContext.review_form_values.application_business_time, '2026-05-20 至 2026-05-23')
|
||||
|
||||
let state = waitForGuidedApplicationSelection(createGuidedReimbursementState(), 'travel', travelApplications)
|
||||
assert.equal(state.stepKey, 'application_selection')
|
||||
assert.equal(state.applicationCandidates[0].claim_no, 'AP-202605-001')
|
||||
@@ -459,7 +490,13 @@ test('guided flow is local until final confirmation or collected query handoff',
|
||||
assert.match(messageHandlersScript, /if \(await handleGuidedComposerSubmit\(options\)\) return null[\s\S]*return submitComposerInternal\(options\)/)
|
||||
assert.match(suggestedActionsScript, /ASSISTANT_SCOPE_ACTION_SWITCH/)
|
||||
assert.match(suggestedActionsScript, /actionPayload\.carry_text/)
|
||||
assert.match(suggestedActionsScript, /targetSessionType === SESSION_TYPE_EXPENSE[\s\S]*carryText === '我要报销'[\s\S]*pushExpenseSceneSelectionPrompt\(carryText\)/)
|
||||
assert.match(suggestedActionsScript, /targetSessionType === SESSION_TYPE_EXPENSE[\s\S]*carryText === '我要报销'[\s\S]*pushExpenseAssociationGatePrompt\(carryText\)/)
|
||||
assert.match(suggestedActionsScript, /actionType === SKIP_REQUIRED_APPLICATION_LINK_ACTION[\s\S]*pushExpenseSceneSelectionPrompt/)
|
||||
assert.match(suggestedActionsScript, /actionType === 'confirm_expense_intent'[\s\S]*pushExpenseAssociationGatePrompt\(originalMessage\)/)
|
||||
assert.match(suggestedActionsScript, /pushReimbursementAssociationPromptMessage\(\{[\s\S]*skipDraftCheck: Boolean\(options\.skipDraftCheck\)/)
|
||||
assert.match(submitComposerScript, /waitForExpenseSceneSelection[\s\S]*pushReimbursementAssociationPromptMessage\(\{[\s\S]*rawText/)
|
||||
assert.match(submitComposerScript, /fetchExpenseClaims[\s\S]*currentUser/)
|
||||
assert.doesNotMatch(submitComposerScript, /if \(waitForExpenseSceneSelection\) \{[\s\S]{0,260}buildExpenseSceneSelectionMessage/)
|
||||
assert.match(submitComposerScript, /resolveAssistantScopeGuard/)
|
||||
assert.match(submitComposerScript, /skipScopeGuard/)
|
||||
assert.match(guidedFlowScript, /submitExistingComposer\(submitOptions\)/)
|
||||
|
||||
Reference in New Issue
Block a user