5.7 KiB
5.7 KiB
Workflow Email Service Guide
Overview
This guide explains how to add new workflow-specific email services without breaking existing implementations. The architecture uses a factory pattern with interfaces to ensure isolation between different workflow types.
Architecture
┌─────────────────────────────────────┐
│ notification.service.ts │
│ (Main Router) │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ workflowEmail.factory.ts │
│ (Factory Pattern) │
└──────────────┬──────────────────────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Dealer Claim │ │ Custom/ │
│ Email Service│ │ Default │
└──────────────┘ └──────────────┘
Adding a New Workflow Type
Step 1: Create the Email Service
Create a new file: Re_Backend/src/services/[workflowType]Email.service.ts
Example: Re_Backend/src/services/vendorPaymentEmail.service.ts
import { ApprovalLevel } from '@models/ApprovalLevel';
import { User } from '@models/User';
import logger from '@utils/logger';
import { IWorkflowEmailService } from './workflowEmail.interface';
import { emailNotificationService } from './emailNotification.service';
export class VendorPaymentEmailService implements IWorkflowEmailService {
async sendAssignmentEmail(
requestData: any,
approverUser: User,
initiatorData: any,
currentLevel: ApprovalLevel | null,
allLevels: ApprovalLevel[]
): Promise<void> {
try {
// Your workflow-specific logic here
// Example: Check if it's a vendor-specific step
const levelName = currentLevel?.levelName || '';
const isVendorStep = levelName.toLowerCase().includes('vendor');
if (isVendorStep) {
// Use vendor-specific template
await this.sendVendorSpecificEmail(requestData, approverUser, initiatorData, currentLevel);
} else {
// Use standard template
await this.sendStandardApprovalEmail(requestData, approverUser, initiatorData, currentLevel);
}
} catch (error) {
logger.error(`[VendorPaymentEmail] Error sending assignment email:`, error);
throw error;
}
}
private async sendVendorSpecificEmail(
requestData: any,
vendorUser: User,
initiatorData: any,
currentLevel: ApprovalLevel | null
): Promise<void> {
// Implementation for vendor-specific email
}
private async sendStandardApprovalEmail(
requestData: any,
approverUser: User,
initiatorData: any,
currentLevel: ApprovalLevel | null
): Promise<void> {
// Implementation for standard approval email
}
}
export const vendorPaymentEmailService = new VendorPaymentEmailService();
Step 2: Register in Factory
Update Re_Backend/src/services/workflowEmail.factory.ts:
import { vendorPaymentEmailService } from './vendorPaymentEmail.service';
class WorkflowEmailServiceFactory {
getService(workflowType: string): IWorkflowEmailService {
switch (workflowType) {
case 'CLAIM_MANAGEMENT':
return dealerClaimEmailService;
case 'VENDOR_PAYMENT': // Add your new workflow type
return vendorPaymentEmailService;
default:
return null as any;
}
}
hasDedicatedService(workflowType: string): boolean {
return workflowType === 'CLAIM_MANAGEMENT'
|| workflowType === 'VENDOR_PAYMENT'; // Add your new workflow type
}
}
Step 3: Create Email Templates (if needed)
If your workflow needs custom templates, create them in:
Re_Backend/src/emailtemplates/[templateName].template.ts
Then add the send method to emailNotification.service.ts:
async sendVendorPaymentRequired(
requestData: any,
vendorData: any,
initiatorData: any,
paymentData?: any
): Promise<void> {
// Implementation
}
Benefits of This Architecture
- Isolation: Each workflow type has its own service, preventing cross-workflow breakage
- Scalability: Easy to add new workflow types without modifying existing code
- Maintainability: Changes to one workflow don't affect others
- Type Safety: Interface ensures consistent implementation
- Testability: Each service can be tested independently
Best Practices
- Always implement IWorkflowEmailService: Ensures consistency
- Use levelName, not levelNumber: Handles additional approvers correctly
- Log workflow-specific actions: Helps with debugging
- Handle errors gracefully: Don't break the entire notification system
- Document workflow-specific logic: Makes it easier for others to understand
Example: Dealer Claim Service
See dealerClaimEmail.service.ts for a complete example of:
- Dynamic step identification
- Multiple template types (proposal, completion, standard)
- Additional approver handling
- Proper error handling
Testing
When adding a new workflow type:
- Test assignment emails for all steps
- Test with additional approvers
- Test with missing levelName (fallback to levelNumber)
- Test error handling
- Verify custom workflows still work (regression test)