import { WorkflowRequest } from '../models/WorkflowRequest'; import { ApprovalLevel } from '../models/ApprovalLevel'; import { User } from '../models/User'; import { Participant } from '../models/Participant'; import logger from '../utils/logger'; /** * Interface for user reference configuration in form fields */ export interface UserReference { role: 'initiator' | 'dealer' | 'approver' | 'team_lead' | 'department_lead' | 'current_approver' | 'previous_approver'; level?: number; // For approver: which approval level field: 'name' | 'email' | 'phone' | 'department' | 'employee_id' | 'all'; // Which user field to reference autoPopulate: boolean; // Auto-fill from user data editable: boolean; // Can user edit the auto-populated value } /** * Interface for form step configuration */ export interface FormStepConfig { stepNumber: number; stepName: string; stepDescription?: string; fields: FormFieldConfig[]; userReferences?: UserReferenceConfig[]; } export interface FormFieldConfig { fieldId: string; fieldType: string; label: string; required: boolean; defaultValue?: any; userReference?: UserReference; } export interface UserReferenceConfig { role: string; captureFields: string[]; autoPopulateFrom: 'workflow' | 'user_profile' | 'approval_level'; allowOverride: boolean; } /** * Service to resolve user references in template forms */ export class TemplateFieldResolver { /** * Resolve user reference fields in a step */ async resolveUserReferences( stepConfig: FormStepConfig, request: WorkflowRequest, currentUserId: string, context?: { currentLevel?: number; approvers?: Map; } ): Promise> { const resolvedFields: Record = {}; try { for (const field of stepConfig.fields) { if (field.userReference) { const userData = await this.getUserDataForReference( field.userReference, request, currentUserId, context ); if (field.userReference.autoPopulate && userData) { resolvedFields[field.fieldId] = this.extractUserField( userData, field.userReference.field ); } } } } catch (error) { logger.error('[TemplateFieldResolver] Error resolving user references:', error); } return resolvedFields; } /** * Get user data based on reference configuration */ private async getUserDataForReference( userRef: UserReference, request: WorkflowRequest, currentUserId: string, context?: any ): Promise { try { switch (userRef.role) { case 'initiator': return await User.findByPk(request.initiatorId); case 'dealer': // Get dealer from participants const dealerParticipant = await Participant.findOne({ where: { requestId: request.requestId, participantType: 'DEALER' as any, isActive: true }, include: [{ model: User, as: 'user' }] }); return dealerParticipant?.user || null; case 'approver': if (userRef.level && context?.approvers) { const approverLevel = context.approvers.get(userRef.level); if (approverLevel?.approverId) { return await User.findByPk(approverLevel.approverId); } } // Fallback to current approver const currentLevel = await ApprovalLevel.findOne({ where: { requestId: request.requestId, levelNumber: context?.currentLevel || request.currentLevel, status: 'PENDING' as any } }); if (currentLevel?.approverId) { return await User.findByPk(currentLevel.approverId); } return null; case 'team_lead': // Find team lead based on initiator's manager const initiator = await User.findByPk(request.initiatorId); if (initiator?.manager) { return await User.findOne({ where: { email: initiator.manager, role: 'MANAGEMENT' as any } }); } return null; case 'department_lead': const initiatorUser = await User.findByPk(request.initiatorId); if (initiatorUser?.department) { return await User.findOne({ where: { department: initiatorUser.department, role: 'MANAGEMENT' as any }, order: [['created_at', 'DESC']] }); } return null; case 'current_approver': const currentApprovalLevel = await ApprovalLevel.findOne({ where: { requestId: request.requestId, status: 'PENDING' as any }, order: [['level_number', 'ASC']] }); if (currentApprovalLevel?.approverId) { return await User.findByPk(currentApprovalLevel.approverId); } return null; case 'previous_approver': const previousLevel = request.currentLevel - 1; if (previousLevel > 0) { const previousApprovalLevel = await ApprovalLevel.findOne({ where: { requestId: request.requestId, levelNumber: previousLevel } }); if (previousApprovalLevel?.approverId) { return await User.findByPk(previousApprovalLevel.approverId); } } return null; default: return null; } } catch (error) { logger.error(`[TemplateFieldResolver] Error getting user data for role ${userRef.role}:`, error); return null; } } /** * Extract specific field from user data */ private extractUserField(user: User, field: string): any { if (!user) return null; switch (field) { case 'name': return user.displayName || `${user.firstName || ''} ${user.lastName || ''}`.trim(); case 'email': return user.email; case 'phone': return user.phone || user.mobilePhone; case 'department': return user.department; case 'employee_id': return user.employeeId; case 'all': return { name: user.displayName || `${user.firstName || ''} ${user.lastName || ''}`.trim(), email: user.email, phone: user.phone || user.mobilePhone, department: user.department, employeeId: user.employeeId }; default: return null; } } /** * Resolve dynamic approver based on configuration */ async resolveDynamicApprover( level: number, config: any, // DynamicApproverConfig request: WorkflowRequest ): Promise { if (!config?.enabled || !config?.approverSelection?.dynamicRules) { return null; } try { const rule = config.approverSelection.dynamicRules.find((r: any) => r.level === level); if (!rule) return null; const criteria = rule.selectionCriteria; switch (criteria.type) { case 'role': return await User.findOne({ where: { role: criteria.value as any }, order: [['created_at', 'DESC']] }); case 'department': const initiator = await User.findByPk(request.initiatorId); const deptValue = criteria.value?.replace('${initiator.department}', initiator?.department || '') || initiator?.department; if (deptValue) { return await User.findOne({ where: { department: deptValue, role: 'MANAGEMENT' as any } }); } return null; case 'manager': const initiatorUser = await User.findByPk(request.initiatorId); if (initiatorUser?.manager) { return await User.findOne({ where: { email: initiatorUser.manager } }); } return null; default: return null; } } catch (error) { logger.error('[TemplateFieldResolver] Error resolving dynamic approver:', error); return null; } } }