Re_Backend/src/services/templateFieldResolver.service.ts

288 lines
8.3 KiB
TypeScript

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<number, ApprovalLevel>;
}
): Promise<Record<string, any>> {
const resolvedFields: Record<string, any> = {};
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<User | null> {
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<User | null> {
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;
}
}
}