import assert from 'node:assert/strict'; import test from 'node:test'; import { actionLabel, allowedTaskActions, buildBusinessOverview, buildAcceptanceChecklist, buildJobReferenceChain, buildNodeCallbackRequestBody, buildNodeDetails, buildNodeCallbackTemplate, jobStatusBucketLabel, jobStatusBucketMatches, nodeCallbackActionLabel, reasonLabel, roleLabel, taskCandidates, taskStatusLabel, } from './stateMachine.ts'; test('待办相关编码全部显示为中文', () => { assert.equal(roleLabel('PLANNER'), '计划员'); assert.equal(taskStatusLabel('OPEN'), '待处理'); assert.equal(actionLabel('SELECT_CANDIDATE'), '选择候选货品'); assert.equal(reasonLabel('MATCH_MULTI'), '存在多个候选货品'); assert.equal(reasonLabel('SOURCE_ADD'), '需要新增货源'); }); test('候选货品兼容数据库返回的数组和 JSON 文本', () => { const task = { task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'PRODUCT_MATCH', role_code: 'PLANNER', status: 'OPEN', title: '对码待确认', reason_type: 'MATCH_MULTI', }; assert.equal(taskCandidates({ ...task, candidates_json: '[{"goods_id":"G-1"}]' })[0]?.goods_id, 'G-1'); assert.equal(taskCandidates({ ...task, candidates_json: [{ goods_id: 'G-2' }] })[0]?.goods_id, 'G-2'); }); test('对码待办只提供后端支持的业务动作', () => { assert.deepEqual( allowedTaskActions({ task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'PRODUCT_MATCH', role_code: 'PLANNER', status: 'OPEN', title: '对码待确认', reason_type: 'MATCH_MULTI', }), ['SELECT_CANDIDATE', 'TRANSFER_MANUAL', 'TERMINATE_JOB'], ); }); test('合同建单成功回调模板带必填合同编号', () => { const template = buildNodeCallbackTemplate('CONTRACT_CREATE', 'complete'); assert.equal(template.attemptNo, 1); assert.equal(template.executorPlatform, 'iS-RPA'); assert.equal(template.businessRefs.contractSystemNo, 'HT-联调-001'); assert.equal(template.artifacts[0].artifactType, 'CONTRACT_PDF'); }); test('节点失败回调模板带后端必填错误字段', () => { const template = buildNodeCallbackTemplate('REBATE_IMPORT', 'fail'); assert.equal(nodeCallbackActionLabel('fail'), '失败'); assert.equal(template.errorCode, 'SYS_PAGE_TIMEOUT'); assert.equal(template.retryable, true); assert.equal(template.routeRole, 'OPS'); }); test('成功回调请求自动补幂等键并保留手工覆盖字段', () => { const body = buildNodeCallbackRequestBody( 'complete', 'CONTRACT_CREATE', { summary: { contractAmount: 1200 }, businessRefs: { contractSystemNo: 'HT-20260811-001' }, }, 'idem-001', ); assert.equal(body.idempotencyKey, 'idem-001'); assert.equal(body.businessRefs.contractSystemNo, 'HT-20260811-001'); assert.equal(body.summary.contractAmount, 1200); }); test('单据状态筛选按验收口径归类', () => { assert.equal(jobStatusBucketLabel('ACTIVE'), '进行中'); assert.equal(jobStatusBucketMatches('ALL', 'SUCCESS'), true); assert.equal(jobStatusBucketMatches('ACTIVE', 'RUNNING'), true); assert.equal(jobStatusBucketMatches('ACTIVE', 'WAITING_EXTERNAL'), true); assert.equal(jobStatusBucketMatches('WAITING', 'WAITING_USER'), true); assert.equal(jobStatusBucketMatches('WAITING', 'RUNNING'), false); assert.equal(jobStatusBucketMatches('FAILED', 'FAILED_MANUAL'), true); assert.equal(jobStatusBucketMatches('SUCCESS', 'SUCCESS'), true); }); test('节点详情按节点关联执行记录、证据和待办', () => { const details = buildNodeDetails( [ { node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'SOURCE_ADD', display_status: 'WAITING_USER', attempt_no: 1, status: 'WAITING_USER' }, ], [ { node_code: 'ORDER_FETCH', attempt_no: 1, status: 'SUCCESS', started_at: '2026-08-11T09:00:00', ended_at: '2026-08-11T09:00:12', output_json: '{"summary":{"orderCount":2}}', }, ], [ { node_code: 'ORDER_FETCH', attempt_no: 1, artifact_type: 'SOURCE_EXCEL', storage_path: '/data/jobs/JOB-1/orders.xlsx', }, ], [ { task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'SOURCE_ADD', role_code: 'PURCHASER', status: 'OPEN', title: '新增货源', reason_type: 'SOURCE_ADD', }, ], ); assert.equal(details[0].summaryText, 'orderCount:2'); assert.equal(details[0].artifacts[0].storage_path, '/data/jobs/JOB-1/orders.xlsx'); assert.equal(details[1].openTask?.task_id, 'TASK-1'); }); test('编号链能标出已回填和待回填项', () => { const chain = buildJobReferenceChain({ job_id: 'JOB-1', supplier_name: '重庆亿昊药业', order_date: '2026-08-04', source_type: 'YYC', status: 'RUNNING', current_node_code: 'CONTRACT_CREATE', order_no_list_json: '["ZQD-1","ZQD-2"]', contract_system_no: 'HT-1', rebate_order_no: null, m5_order_no: '', receipt_notice_no: 'SH-1', }); assert.deepEqual(chain.map((item) => item.status), ['DONE', 'DONE', 'MISSING', 'MISSING', 'DONE']); assert.equal(chain[0].value, 'ZQD-1、ZQD-2'); }); test('业务概览只保留首屏必须信息', () => { const overview = buildBusinessOverview( { job_id: 'JOB-1', supplier_name: '重庆亿昊药业', order_date: '2026-08-04', source_type: 'YYC', status: 'SUCCESS', current_node_code: 'ARCHIVE_JOB', current_role: 'OPS', contract_system_no: 'HT-1', rebate_order_no: 'FL-1', m5_order_no: 'M5-1', receipt_notice_no: 'SH-1', order_no_list_json: '["ZQD-1","ZQD-2"]', }, [ { node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'PRODUCT_MATCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'SOURCE_ADD', display_status: 'WAITING_USER', attempt_no: 1, status: 'WAITING_USER' }, { node_code: 'CONTRACT_CREATE', display_status: 'PENDING' }, { node_code: 'ARCHIVE_JOB', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, ], [ { task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'SOURCE_ADD', role_code: 'PURCHASER', status: 'OPEN', title: '新增货源:重庆亿昊药业', reason_type: 'SOURCE_ADD' }, ], [ { node_code: 'ORDER_FETCH', attempt_no: 1, artifact_type: 'SOURCE_EXCEL', storage_path: '/data/jobs/JOB-1/orders.xlsx' }, ], { node_order: ['ORDER_FETCH', 'PRODUCT_MATCH', 'SOURCE_ADD', 'CONTRACT_CREATE', 'ARCHIVE_JOB'], transitions: {}, conditional_nodes: [], human_node_roles: {}, node_executor: {}, node_labels: { ORDER_FETCH: '订单获取', PRODUCT_MATCH: '对码', SOURCE_ADD: '新增货源', CONTRACT_CREATE: '合同建单', ARCHIVE_JOB: '归档', }, executor_labels: {}, status_labels: { SUCCESS: '已完成', WAITING_USER: '等待人工', PENDING: '待处理', }, }, ); assert.equal(overview.header.currentStatusText, '已完成'); assert.equal(overview.header.currentNodeText, '归档'); assert.equal(overview.referenceChain.length, 5); assert.equal(overview.primaryTask?.title, '新增货源:重庆亿昊药业'); assert.equal(overview.flow.visibleNodes.length, 5); assert.equal(overview.summary.evidenceCount, 1); }); test('验收收口能识别真库一单的完成和缺口', () => { const checklist = buildAcceptanceChecklist( { job_id: 'JOB-1', supplier_name: '重庆亿昊药业', order_date: '2026-08-04', source_type: 'YYC', status: 'SUCCESS', current_node_code: 'ARCHIVE_JOB', contract_system_no: 'HT-1', rebate_order_no: 'FL-1', m5_order_no: 'M5-1', receipt_notice_no: 'SH-1', order_no_list_json: '["ZQD-1"]', }, [ { node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'OCR_EXTRACT', display_status: 'SKIPPED' }, { node_code: 'PRODUCT_MATCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'SOURCE_ADD', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'CONTRACT_CREATE', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'CONTRACT_APPROVE_WAIT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'REBATE_HEADER', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'REBATE_IMPORT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'REBATE_SUBMIT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'M5_CREATE_ORDER', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'M5_MANUAL_CONFIRM', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'M5_RECEIPT_NOTICE', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, { node_code: 'ARCHIVE_JOB', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' }, ], [ { node_code: 'ORDER_FETCH', attempt_no: 1, status: 'SUCCESS', started_at: '2026-08-11T09:00:00', ended_at: '2026-08-11T09:00:12', output_json: '{"summary":{"orderCount":1}}' }, { node_code: 'CONTRACT_CREATE', attempt_no: 1, status: 'SUCCESS', started_at: '2026-08-11T09:02:00', ended_at: '2026-08-11T09:02:12', output_json: '{"summary":{"saved":true}}' }, ], [ { node_code: 'ORDER_FETCH', attempt_no: 1, artifact_type: 'SOURCE_EXCEL', storage_path: '/data/jobs/JOB-1/orders.xlsx' }, ], [ { task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'M5_MANUAL_CONFIRM', role_code: 'PURCHASER', status: 'SUBMITTED', title: 'M5人工确认', reason_type: 'SOURCE_ADD' }, ], ); assert.equal(checklist.find((item) => item.key === 'timeline_complete')?.status, 'DONE'); assert.equal(checklist.find((item) => item.key === 'node_detail_coverage')?.status, 'PARTIAL'); assert.equal(checklist.find((item) => item.key === 'reference_chain')?.status, 'DONE'); });