247 lines
7.1 KiB
TypeScript
247 lines
7.1 KiB
TypeScript
import { WorkflowTemplate } from '../models/WorkflowTemplate';
|
|
import { WorkflowRequest } from '../models/WorkflowRequest';
|
|
import { User } from '../models/User';
|
|
import { Op } from 'sequelize';
|
|
import logger from '../utils/logger';
|
|
|
|
/**
|
|
* Template Service
|
|
* Handles CRUD operations for workflow templates
|
|
*/
|
|
export class TemplateService {
|
|
/**
|
|
* Create a new workflow template
|
|
*/
|
|
async createTemplate(
|
|
userId: string,
|
|
templateData: {
|
|
templateName: string;
|
|
templateCode?: string;
|
|
templateDescription?: string;
|
|
templateCategory?: string;
|
|
workflowType?: string;
|
|
approvalLevelsConfig?: any;
|
|
defaultTatHours?: number;
|
|
formStepsConfig?: any;
|
|
userFieldMappings?: any;
|
|
dynamicApproverConfig?: any;
|
|
isActive?: boolean;
|
|
}
|
|
): Promise<WorkflowTemplate> {
|
|
try {
|
|
// Validate template code uniqueness if provided
|
|
if (templateData.templateCode) {
|
|
const existing = await WorkflowTemplate.findOne({
|
|
where: { templateCode: templateData.templateCode }
|
|
});
|
|
if (existing) {
|
|
throw new Error(`Template code '${templateData.templateCode}' already exists`);
|
|
}
|
|
}
|
|
|
|
const template = await WorkflowTemplate.create({
|
|
templateName: templateData.templateName,
|
|
templateCode: templateData.templateCode,
|
|
templateDescription: templateData.templateDescription,
|
|
templateCategory: templateData.templateCategory,
|
|
workflowType: templateData.workflowType || templateData.templateCode?.toUpperCase(),
|
|
approvalLevelsConfig: templateData.approvalLevelsConfig,
|
|
defaultTatHours: templateData.defaultTatHours || 24,
|
|
formStepsConfig: templateData.formStepsConfig,
|
|
userFieldMappings: templateData.userFieldMappings,
|
|
dynamicApproverConfig: templateData.dynamicApproverConfig,
|
|
isActive: templateData.isActive !== undefined ? templateData.isActive : true,
|
|
isSystemTemplate: false, // Admin-created templates are not system templates
|
|
usageCount: 0,
|
|
createdBy: userId,
|
|
});
|
|
|
|
logger.info(`[TemplateService] Created template: ${template.templateId}`);
|
|
return template;
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error creating template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get template by ID
|
|
*/
|
|
async getTemplate(templateId: string): Promise<WorkflowTemplate | null> {
|
|
try {
|
|
return await WorkflowTemplate.findByPk(templateId, {
|
|
include: [{ model: User, as: 'creator' }]
|
|
});
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error getting template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get template by code
|
|
*/
|
|
async getTemplateByCode(templateCode: string): Promise<WorkflowTemplate | null> {
|
|
try {
|
|
return await WorkflowTemplate.findOne({
|
|
where: { templateCode },
|
|
include: [{ model: User, as: 'creator' }]
|
|
});
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error getting template by code:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all templates with filters
|
|
*/
|
|
async listTemplates(filters?: {
|
|
category?: string;
|
|
workflowType?: string;
|
|
isActive?: boolean;
|
|
isSystemTemplate?: boolean;
|
|
search?: string;
|
|
}): Promise<WorkflowTemplate[]> {
|
|
try {
|
|
const where: any = {};
|
|
|
|
if (filters?.category) {
|
|
where.templateCategory = filters.category;
|
|
}
|
|
|
|
if (filters?.workflowType) {
|
|
where.workflowType = filters.workflowType;
|
|
}
|
|
|
|
if (filters?.isActive !== undefined) {
|
|
where.isActive = filters.isActive;
|
|
}
|
|
|
|
if (filters?.isSystemTemplate !== undefined) {
|
|
where.isSystemTemplate = filters.isSystemTemplate;
|
|
}
|
|
|
|
if (filters?.search) {
|
|
where[Op.or] = [
|
|
{ templateName: { [Op.iLike]: `%${filters.search}%` } },
|
|
{ templateCode: { [Op.iLike]: `%${filters.search}%` } },
|
|
{ templateDescription: { [Op.iLike]: `%${filters.search}%` } }
|
|
];
|
|
}
|
|
|
|
return await WorkflowTemplate.findAll({
|
|
where,
|
|
include: [{ model: User, as: 'creator' }],
|
|
order: [['createdAt', 'DESC']]
|
|
});
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error listing templates:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update template
|
|
*/
|
|
async updateTemplate(
|
|
templateId: string,
|
|
userId: string,
|
|
updateData: {
|
|
templateName?: string;
|
|
templateDescription?: string;
|
|
templateCategory?: string;
|
|
approvalLevelsConfig?: any;
|
|
defaultTatHours?: number;
|
|
formStepsConfig?: any;
|
|
userFieldMappings?: any;
|
|
dynamicApproverConfig?: any;
|
|
isActive?: boolean;
|
|
}
|
|
): Promise<WorkflowTemplate> {
|
|
try {
|
|
const template = await WorkflowTemplate.findByPk(templateId);
|
|
if (!template) {
|
|
throw new Error('Template not found');
|
|
}
|
|
|
|
// Check if template is system template (system templates should not be modified)
|
|
if (template.isSystemTemplate && updateData.approvalLevelsConfig) {
|
|
throw new Error('Cannot modify approval levels of system templates');
|
|
}
|
|
|
|
await template.update(updateData);
|
|
|
|
logger.info(`[TemplateService] Updated template: ${templateId}`);
|
|
return template;
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error updating template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete template (soft delete by setting isActive to false)
|
|
*/
|
|
async deleteTemplate(templateId: string): Promise<void> {
|
|
try {
|
|
const template = await WorkflowTemplate.findByPk(templateId);
|
|
if (!template) {
|
|
throw new Error('Template not found');
|
|
}
|
|
|
|
// Check if template is in use
|
|
const usageCount = await WorkflowRequest.count({
|
|
where: { templateId }
|
|
});
|
|
|
|
if (usageCount > 0) {
|
|
throw new Error(`Cannot delete template: ${usageCount} request(s) are using this template`);
|
|
}
|
|
|
|
// System templates cannot be deleted
|
|
if (template.isSystemTemplate) {
|
|
throw new Error('Cannot delete system templates');
|
|
}
|
|
|
|
// Soft delete by deactivating
|
|
await template.update({ isActive: false });
|
|
|
|
logger.info(`[TemplateService] Deleted (deactivated) template: ${templateId}`);
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error deleting template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get active templates for workflow creation
|
|
*/
|
|
async getActiveTemplates(): Promise<WorkflowTemplate[]> {
|
|
try {
|
|
return await WorkflowTemplate.findAll({
|
|
where: { isActive: true },
|
|
order: [['templateName', 'ASC']]
|
|
});
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error getting active templates:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Increment usage count when template is used
|
|
*/
|
|
async incrementUsageCount(templateId: string): Promise<void> {
|
|
try {
|
|
await WorkflowTemplate.increment('usageCount', {
|
|
where: { templateId }
|
|
});
|
|
} catch (error) {
|
|
logger.error('[TemplateService] Error incrementing usage count:', error);
|
|
// Don't throw - this is not critical
|
|
}
|
|
}
|
|
}
|
|
|