import { ASSIGN_ROLE, canAccessBidModule, canAssignFetcher, canCreateBid, canCreateSpecialBid, canFetchBid, canFetchThisBid, canManageDraft, canManageThisDraft, canSeeAllBids, canSeeTerminated, canTerminateBid, canTerminateFrom, currentHandlerNames, isCommercialStage, isSpecialBid, isSpecialCoordinatorUser, stageForStatus, previousBidStatus, inferLegacyRestoreStatus, } from './bid-workflow'; describe('bid-workflow', () => { it('maps status to review stage', () => { expect(stageForStatus('TECH_INITIAL')).toBe('TECH_INITIAL'); expect(stageForStatus('TECH_FINAL')).toBe('TECH_FINAL'); expect(stageForStatus('REVIEW1')).toBe('REVIEW1'); expect(stageForStatus('REVIEW2')).toBe('REVIEW2'); expect(stageForStatus('FETCH')).toBeNull(); expect(stageForStatus('DRAFTING')).toBeNull(); }); it('treats commercial reject as bounce, not terminate', () => { expect(isCommercialStage('REVIEW1')).toBe(true); expect(isCommercialStage('REVIEW2')).toBe(true); expect(isCommercialStage('TECH_INITIAL')).toBe(false); }); it('lets every director see all bids', () => { expect(canSeeAllBids(['rd_director'])).toBe(true); expect(canSeeAllBids(['3d_director'])).toBe(true); expect(canSeeAllBids(['video_director'])).toBe(true); expect(canSeeAllBids(['material_director'])).toBe(true); expect(canSeeAllBids(['biz_director'])).toBe(true); expect(canSeeAllBids(['tech_director'])).toBe(true); expect(canSeeAllBids(['owner'])).toBe(true); expect(canSeeAllBids(['admin'])).toBe(true); expect(canSeeAllBids(['biz_staff'])).toBe(true); expect(canSeeAllBids(['employee'])).toBe(false); expect(canSeeAllBids(['pm'])).toBe(false); }); it('hides terminated bids from ordinary employees', () => { expect(canSeeTerminated(['employee'], ['office'])).toBe(false); expect(canSeeTerminated(['hr'], ['office', 'hr'])).toBe(false); expect(canSeeTerminated(['finance'], ['bid:result'])).toBe(false); expect(canSeeTerminated(['biz_staff'], ['bid:screening'])).toBe(false); expect(canSeeTerminated(['biz_director'], ['bid:terminated'])).toBe(true); expect(canSeeTerminated(['tech_director'], ['bid'])).toBe(true); expect(canSeeTerminated(['owner'], [])).toBe(true); expect(canSeeTerminated(['admin'], [])).toBe(true); }); it('restricts create / fetch / terminate by role', () => { expect(canCreateBid(['owner'])).toBe(true); expect(canCreateBid(['tech_director'])).toBe(false); expect(canCreateBid(['employee'], ['bid:screening'])).toBe(true); expect(canFetchBid(['biz_staff'])).toBe(true); expect(canFetchBid(['tech_director'])).toBe(false); expect(canTerminateBid(['biz_director'])).toBe(true); expect(canTerminateBid(['biz_staff'])).toBe(false); expect(canManageDraft(['biz_director'])).toBe(true); expect(canManageDraft(['biz_staff'])).toBe(false); }); it('keeps special screening on named coordinators', () => { expect(isSpecialBid('SPECIAL')).toBe(true); expect(isSpecialBid('FLOW')).toBe(false); expect(canCreateSpecialBid(['owner'])).toBe(true); expect(canCreateSpecialBid(['biz_director'])).toBe(false); expect(isSpecialCoordinatorUser({ username: 'liuyunze', displayName: '刘运泽' })).toBe(true); expect(isSpecialCoordinatorUser({ username: 'chencheng', displayName: '陈铖' })).toBe(true); expect(isSpecialCoordinatorUser({ username: 'other', displayName: '其他人' })).toBe(false); const coordinators = [{ userId: 'liu', role: ASSIGN_ROLE.COORDINATOR }]; const fetchers = [{ userId: 'staff', role: ASSIGN_ROLE.FETCHER }]; expect(canAssignFetcher(['3d_director'], 'SPECIAL', coordinators, 'liu', 'FETCH')).toBe(true); expect(canAssignFetcher(['3d_director'], 'SPECIAL', coordinators, 'other', 'FETCH')).toBe(false); expect(canAssignFetcher(['biz_director'], 'FLOW', [], 'x', 'FETCH')).toBe(true); expect(canAssignFetcher(['biz_staff'], 'FLOW', [], 'x', 'FETCH')).toBe(false); expect(canFetchThisBid(['employee'], 'SPECIAL', fetchers, 'staff')).toBe(true); expect(canFetchThisBid(['biz_staff'], 'SPECIAL', fetchers, 'other')).toBe(false); expect(canFetchThisBid(['biz_staff'], 'FLOW', [], 'other')).toBe(false); expect(canFetchThisBid(['employee'], 'FLOW', fetchers, 'staff')).toBe(true); expect(canManageThisDraft(['3d_director'], 'SPECIAL', coordinators, 'liu')).toBe(true); expect(canManageThisDraft(['biz_director'], 'SPECIAL', coordinators, 'biz')).toBe(false); expect(canManageThisDraft(['biz_director'], 'FLOW', [], 'biz')).toBe(true); }); it('allows terminate until execution, not after open', () => { expect(canTerminateFrom('TECH_INITIAL')).toBe(true); expect(canTerminateFrom('DRAFTING')).toBe(true); expect(canTerminateFrom('LIST')).toBe(true); expect(canTerminateFrom('PENDING')).toBe(false); expect(canTerminateFrom('WON')).toBe(false); }); it('shows the person who should act on the current step', () => { expect( currentHandlerNames({ status: 'TECH_INITIAL', reviewCycle: 1, createdBy: { displayName: '潘兴辉' }, reviewers: [ { stage: 'TECH_INITIAL', result: 'PENDING', cycle: 1, user: { displayName: '戴永康' } }, ], }), ).toBe('戴永康'); expect( currentHandlerNames({ status: 'FETCH', assignees: [{ role: 'FETCHER', user: { displayName: '何烨' } }], createdBy: { displayName: '潘兴辉' }, }), ).toBe('何烨'); expect( currentHandlerNames({ status: 'DRAFTING', assignees: [{ role: 'DRAFTER', user: { displayName: '张婧妍' } }], }), ).toBe('张婧妍'); expect(currentHandlerNames({ status: 'PRINT', createdBy: { displayName: '潘兴辉' } })).toBe('商务部'); expect(currentHandlerNames({ status: 'FETCH', createdBy: { displayName: '潘兴辉' } })).toBe('待派取标人'); }); it('rolls back one process step, except special fetch', () => { expect(previousBidStatus('FETCH', 'FLOW')).toBe('TECH_INITIAL'); expect(previousBidStatus('FETCH', 'SPECIAL')).toBeNull(); expect(previousBidStatus('TECH_FINAL')).toBe('FETCH'); expect(previousBidStatus('DRAFTING')).toBe('TECH_FINAL'); expect(previousBidStatus('REVIEW1')).toBe('DRAFTING'); expect(previousBidStatus('REVIEW2')).toBe('REVIEW1'); expect(previousBidStatus('PRINT')).toBe('REVIEW2'); expect(previousBidStatus('LIST')).toBe('PRINT'); expect(previousBidStatus('PENDING')).toBe('LIST'); expect(previousBidStatus('TECH_INITIAL')).toBeNull(); expect(previousBidStatus('WON')).toBeNull(); }); it('infers the restore step for legacy terminated records without an action log', () => { expect(inferLegacyRestoreStatus([], [])).toBe('DRAFTING'); expect( inferLegacyRestoreStatus( [{ stage: 'TECH_INITIAL', result: 'APPROVED' }], [{ role: 'FETCHER' }, { role: 'DRAFTER' }], ), ).toBe('DRAFTING'); expect(inferLegacyRestoreStatus([], [{ role: 'BIDDER' }])).toBe('PENDING'); expect(inferLegacyRestoreStatus([{ stage: 'REVIEW2', result: 'PENDING' }], [])).toBe('REVIEW2'); }); });