Re_Backend/src/services/enhancedTemplate.service.ts

242 lines
7.9 KiB
TypeScript

import { WorkflowTemplate } from '../models/WorkflowTemplate';
import { WorkflowRequest } from '../models/WorkflowRequest';
import { ApprovalLevel } from '../models/ApprovalLevel';
import { TemplateFieldResolver, FormStepConfig } from './templateFieldResolver.service';
import logger from '../utils/logger';
/**
* Enhanced Template Service
* Handles template-based workflow operations with dynamic form configuration
*/
export class EnhancedTemplateService {
private fieldResolver = new TemplateFieldResolver();
/**
* Get form configuration for a template with resolved user references
*/
async getFormConfig(
templateId: string,
requestId?: string,
currentUserId?: string
): Promise<FormStepConfig[]> {
try {
const template = await WorkflowTemplate.findByPk(templateId);
if (!template) {
throw new Error('Template not found');
}
const stepsConfig = (template.formStepsConfig || []) as FormStepConfig[];
// If request exists, resolve user references
if (requestId && currentUserId) {
const request = await WorkflowRequest.findByPk(requestId, {
include: [{ model: ApprovalLevel, as: 'approvalLevels' }]
});
if (request) {
return await this.resolveStepsWithUserData(stepsConfig, request, currentUserId);
}
}
return stepsConfig;
} catch (error) {
logger.error('[EnhancedTemplateService] Error getting form config:', error);
throw error;
}
}
/**
* Resolve user references in all steps
*/
private async resolveStepsWithUserData(
steps: FormStepConfig[],
request: WorkflowRequest,
currentUserId: string
): Promise<FormStepConfig[]> {
try {
// Get all approvers for context
const approvers = await ApprovalLevel.findAll({
where: { requestId: request.requestId }
});
const approverMap = new Map(
approvers.map(a => [a.levelNumber, a])
);
const resolvedSteps = await Promise.all(
steps.map(async (step) => {
const resolvedFields = await this.fieldResolver.resolveUserReferences(
step,
request,
currentUserId,
{
currentLevel: request.currentLevel,
approvers: approverMap
}
);
// Merge resolved values into field defaults
const enrichedFields = step.fields.map(field => ({
...field,
defaultValue: resolvedFields[field.fieldId] || field.defaultValue
}));
return {
...step,
fields: enrichedFields
};
})
);
return resolvedSteps;
} catch (error) {
logger.error('[EnhancedTemplateService] Error resolving steps:', error);
return steps; // Return original steps on error
}
}
/**
* Validate and save form data for a step
*/
async saveStepData(
templateId: string,
requestId: string,
stepNumber: number,
formData: Record<string, any>,
userId: string
): Promise<void> {
try {
const template = await WorkflowTemplate.findByPk(templateId);
if (!template) {
throw new Error('Template not found');
}
const stepsConfig = (template.formStepsConfig || []) as FormStepConfig[];
const stepConfig = stepsConfig.find(s => s.stepNumber === stepNumber);
if (!stepConfig) {
throw new Error(`Step ${stepNumber} not found in template`);
}
// Validate required fields
this.validateStepData(stepConfig, formData);
// Save to template-specific storage
await this.saveToTemplateStorage(template.workflowType, requestId, stepNumber, formData);
} catch (error) {
logger.error('[EnhancedTemplateService] Error saving step data:', error);
throw error;
}
}
/**
* Validate step data against configuration
*/
private validateStepData(stepConfig: FormStepConfig, formData: Record<string, any>): void {
for (const field of stepConfig.fields) {
if (field.required && !formData[field.fieldId]) {
throw new Error(`Field ${field.label} is required`);
}
// Apply validation rules
if (field.validation && formData[field.fieldId]) {
const value = formData[field.fieldId];
if (field.validation.min !== undefined && value < field.validation.min) {
throw new Error(`${field.label} must be at least ${field.validation.min}`);
}
if (field.validation.max !== undefined && value > field.validation.max) {
throw new Error(`${field.label} must be at most ${field.validation.max}`);
}
if (field.validation.pattern) {
const regex = new RegExp(field.validation.pattern);
if (!regex.test(String(value))) {
throw new Error(`${field.label} format is invalid`);
}
}
}
}
}
/**
* Save to template-specific storage based on workflow type
*/
private async saveToTemplateStorage(
workflowType: string,
requestId: string,
stepNumber: number,
formData: Record<string, any>
): Promise<void> {
switch (workflowType) {
case 'CLAIM_MANAGEMENT':
await this.saveClaimManagementStepData(requestId, stepNumber, formData);
break;
default:
// Generic storage for custom templates
logger.warn(`[EnhancedTemplateService] No specific storage handler for workflow type: ${workflowType}`);
}
}
/**
* Save claim management step data
*/
private async saveClaimManagementStepData(
requestId: string,
stepNumber: number,
formData: Record<string, any>
): Promise<void> {
const { DealerClaimDetails } = await import('../models/DealerClaimDetails');
const { DealerProposalDetails } = await import('../models/DealerProposalDetails');
const { DealerCompletionDetails } = await import('../models/DealerCompletionDetails');
switch (stepNumber) {
case 1:
// Save to dealer_claim_details
await DealerClaimDetails.upsert({
requestId,
activityName: formData.activity_name,
activityType: formData.activity_type,
dealerCode: formData.dealer_code,
dealerName: formData.dealer_name,
dealerEmail: formData.dealer_email,
dealerPhone: formData.dealer_phone,
dealerAddress: formData.dealer_address,
activityDate: formData.activity_date,
location: formData.location,
periodStartDate: formData.period_start_date,
periodEndDate: formData.period_end_date,
estimatedBudget: formData.estimated_budget,
});
break;
case 2:
// Save to dealer_proposal_details
await DealerProposalDetails.upsert({
requestId,
costBreakup: formData.cost_breakup,
totalEstimatedBudget: formData.total_estimated_budget,
timelineMode: formData.timeline_mode,
expectedCompletionDate: formData.expected_completion_date,
expectedCompletionDays: formData.expected_completion_days,
dealerComments: formData.dealer_comments,
proposalDocumentPath: formData.proposal_document_path,
proposalDocumentUrl: formData.proposal_document_url,
submittedAt: new Date(),
});
break;
case 5:
// Save to dealer_completion_details
await DealerCompletionDetails.upsert({
requestId,
activityCompletionDate: formData.activity_completion_date,
numberOfParticipants: formData.number_of_participants,
closedExpenses: formData.closed_expenses,
totalClosedExpenses: formData.total_closed_expenses,
completionDocuments: formData.completion_documents,
activityPhotos: formData.activity_photos,
submittedAt: new Date(),
});
break;
default:
logger.warn(`[EnhancedTemplateService] No storage handler for claim management step ${stepNumber}`);
}
}
}