forked from Yara724/api
blame and claim refactored
This commit is contained in:
510
src/workflow-step-management/dto/claim-workflow-step.examples.ts
Normal file
510
src/workflow-step-management/dto/claim-workflow-step.examples.ts
Normal file
@@ -0,0 +1,510 @@
|
||||
/**
|
||||
* Swagger example bodies for CLAIM workflow step management
|
||||
*/
|
||||
|
||||
export const createClaimWorkflowStepExamples = {
|
||||
claimCreated: {
|
||||
summary: 'Claim Created Step',
|
||||
value: {
|
||||
stepKey: 'CLAIM_CREATED',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 1,
|
||||
isActive: true,
|
||||
stepName_fa: 'ایجاد درخواست خسارت',
|
||||
stepName_en: 'Create Claim Request',
|
||||
description_fa: 'ایجاد درخواست خسارت از پرونده تعیین تقصیر تکمیل شده - فقط طرف آسیبدیده میتواند درخواست دهد',
|
||||
description_en: 'Create claim request from completed blame case - only damaged party can proceed',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: [],
|
||||
nextPossibleSteps: ['SELECT_OUTER_PARTS'],
|
||||
allowedRoles: ['user'],
|
||||
estimatedDuration: 1,
|
||||
fields: [],
|
||||
metadata: {
|
||||
icon: 'file-plus',
|
||||
color: '#8B5CF6',
|
||||
requiresBlameCase: true,
|
||||
blameStatusRequired: 'COMPLETED',
|
||||
bothPartiesMustAccept: true,
|
||||
userMustBeDamagedParty: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
selectOuterParts: {
|
||||
summary: 'Select Outer Parts Step',
|
||||
value: {
|
||||
stepKey: 'SELECT_OUTER_PARTS',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 2,
|
||||
isActive: true,
|
||||
stepName_fa: 'انتخاب قطعات خارجی آسیبدیده',
|
||||
stepName_en: 'Select Damaged Outer Parts',
|
||||
description_fa: 'انتخاب قطعات خارجی خودرو که در حادثه آسیب دیدهاند (کاپوت، دربها، سپر و...)',
|
||||
description_en: 'Select vehicle outer parts damaged in the accident (hood, doors, bumpers, etc.)',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: ['CLAIM_CREATED'],
|
||||
nextPossibleSteps: ['SELECT_OTHER_PARTS'],
|
||||
allowedRoles: ['user'],
|
||||
estimatedDuration: 3,
|
||||
fields: [
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1001',
|
||||
name_fa: 'قطعات خارجی',
|
||||
name_en: 'outerParts',
|
||||
label_fa: 'قطعات خارجی آسیبدیده',
|
||||
label_en: 'Damaged Outer Parts',
|
||||
placeholder_fa: 'قطعات خارجی آسیبدیده را انتخاب کنید',
|
||||
placeholder_en: 'Select damaged outer parts',
|
||||
value: [
|
||||
{ label_fa: 'کاپوت', label_en: 'Hood', value: 'hood' },
|
||||
{ label_fa: 'درب جلو راست', label_en: 'Front Right Door', value: 'front_right_door' },
|
||||
{ label_fa: 'درب جلو چپ', label_en: 'Front Left Door', value: 'front_left_door' },
|
||||
{ label_fa: 'درب عقب راست', label_en: 'Rear Right Door', value: 'rear_right_door' },
|
||||
{ label_fa: 'درب عقب چپ', label_en: 'Rear Left Door', value: 'rear_left_door' },
|
||||
{ label_fa: 'سپر جلو', label_en: 'Front Bumper', value: 'front_bumper' },
|
||||
{ label_fa: 'سپر عقب', label_en: 'Rear Bumper', value: 'rear_bumper' },
|
||||
{ label_fa: 'گلگیر جلو راست', label_en: 'Front Right Fender', value: 'front_right_fender' },
|
||||
{ label_fa: 'گلگیر جلو چپ', label_en: 'Front Left Fender', value: 'front_left_fender' },
|
||||
{ label_fa: 'گلگیر عقب راست', label_en: 'Rear Right Fender', value: 'rear_right_fender' },
|
||||
{ label_fa: 'گلگیر عقب چپ', label_en: 'Rear Left Fender', value: 'rear_left_fender' },
|
||||
{ label_fa: 'صندوق عقب', label_en: 'Trunk', value: 'trunk' },
|
||||
{ label_fa: 'سقف', label_en: 'Roof', value: 'roof' }
|
||||
],
|
||||
dataType: 'multiselect',
|
||||
required: true,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
minItems: 1,
|
||||
maxItems: 13
|
||||
}
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
icon: 'car',
|
||||
color: '#3B82F6',
|
||||
requiresMultipleSelection: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
selectOtherParts: {
|
||||
summary: 'Select Other Parts & Bank Info Step',
|
||||
value: {
|
||||
stepKey: 'SELECT_OTHER_PARTS',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 3,
|
||||
isActive: true,
|
||||
stepName_fa: 'انتخاب سایر قطعات و اطلاعات بانکی',
|
||||
stepName_en: 'Select Other Parts & Bank Information',
|
||||
description_fa: 'انتخاب سایر قطعات آسیبدیده (غیر از بدنه)، شماره شبا و کد ملی بیمهگذار',
|
||||
description_en: 'Select other damaged parts (non-body), Sheba number and insurer national code',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: ['SELECT_OUTER_PARTS'],
|
||||
nextPossibleSteps: ['UPLOAD_REQUIRED_DOCUMENTS'],
|
||||
allowedRoles: ['user'],
|
||||
estimatedDuration: 4,
|
||||
fields: [
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1002',
|
||||
name_fa: 'سایر قطعات',
|
||||
name_en: 'otherParts',
|
||||
label_fa: 'سایر قطعات آسیبدیده',
|
||||
label_en: 'Other Damaged Parts',
|
||||
placeholder_fa: 'سایر قطعات آسیبدیده را انتخاب کنید',
|
||||
placeholder_en: 'Select other damaged parts',
|
||||
value: [
|
||||
{ label_fa: 'موتور', label_en: 'Engine', value: 'engine' },
|
||||
{ label_fa: 'سیستم تعلیق', label_en: 'Suspension', value: 'suspension' },
|
||||
{ label_fa: 'سیستم ترمز', label_en: 'Brake System', value: 'brake_system' },
|
||||
{ label_fa: 'سیستم برق', label_en: 'Electrical System', value: 'electrical' },
|
||||
{ label_fa: 'رادیاتور', label_en: 'Radiator', value: 'radiator' },
|
||||
{ label_fa: 'گیربکس', label_en: 'Transmission', value: 'transmission' },
|
||||
{ label_fa: 'اگزوز', label_en: 'Exhaust', value: 'exhaust' },
|
||||
{ label_fa: 'چراغ جلو', label_en: 'Headlight', value: 'headlight' },
|
||||
{ label_fa: 'چراغ عقب', label_en: 'Taillight', value: 'taillight' },
|
||||
{ label_fa: 'آینه', label_en: 'Mirror', value: 'mirror' },
|
||||
{ label_fa: 'شیشه', label_en: 'Glass', value: 'glass' }
|
||||
],
|
||||
dataType: 'multiselect',
|
||||
required: false,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
minItems: 0,
|
||||
maxItems: 11
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1003',
|
||||
name_fa: 'شماره شبا',
|
||||
name_en: 'shebaNumber',
|
||||
label_fa: 'شماره شبا',
|
||||
label_en: 'Sheba Number',
|
||||
placeholder_fa: 'شماره شبا (24 رقم بدون IR)',
|
||||
placeholder_en: 'Sheba number (24 digits without IR)',
|
||||
value: null,
|
||||
dataType: 'string',
|
||||
required: true,
|
||||
order: 2,
|
||||
isActive: true,
|
||||
validation: {
|
||||
pattern: '^[0-9]{24}$',
|
||||
minLength: 24,
|
||||
maxLength: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1004',
|
||||
name_fa: 'کد ملی مالک',
|
||||
name_en: 'nationalCodeOfOwner',
|
||||
label_fa: 'کد ملی مالک',
|
||||
label_en: 'National Code of Owner',
|
||||
placeholder_fa: 'کد ملی 10 رقمی مالک',
|
||||
placeholder_en: '10-digit national code of owner',
|
||||
value: null,
|
||||
dataType: 'string',
|
||||
required: true,
|
||||
order: 3,
|
||||
isActive: true,
|
||||
validation: {
|
||||
pattern: '^[0-9]{10}$',
|
||||
minLength: 10,
|
||||
maxLength: 10
|
||||
}
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
icon: 'list',
|
||||
color: '#10B981',
|
||||
requiresBankInfo: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
uploadRequiredDocuments: {
|
||||
summary: 'Upload Required Documents Step',
|
||||
value: {
|
||||
stepKey: 'UPLOAD_REQUIRED_DOCUMENTS',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 4,
|
||||
isActive: true,
|
||||
stepName_fa: 'آپلود مدارک مورد نیاز',
|
||||
stepName_en: 'Upload Required Documents',
|
||||
description_fa: 'آپلود مدارک مورد نیاز برای بررسی خسارت (لیست داینامیک بر اساس قطعات انتخاب شده)',
|
||||
description_en: 'Upload required documents for claim review (dynamic list based on selected parts)',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: ['SELECT_OTHER_PARTS'],
|
||||
nextPossibleSteps: ['CAPTURE_PART_DAMAGES'],
|
||||
allowedRoles: ['user'],
|
||||
estimatedDuration: 5,
|
||||
fields: [],
|
||||
metadata: {
|
||||
icon: 'upload',
|
||||
color: '#F59E0B',
|
||||
dynamicFields: true,
|
||||
requiresAPI: '/claim-request-management/car-part-image-required/:claimRequestID',
|
||||
note: 'لیست مدارک به صورت داینامیک از API دریافت میشود'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
capturePartDamages: {
|
||||
summary: 'Capture Part Damages Step',
|
||||
value: {
|
||||
stepKey: 'CAPTURE_PART_DAMAGES',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 5,
|
||||
isActive: true,
|
||||
stepName_fa: 'ثبت تصاویر آسیبهای قطعات',
|
||||
stepName_en: 'Capture Part Damages',
|
||||
description_fa: 'ثبت تصاویر آسیب برای هر قطعه (یک درخواست به ازای هر قطعه)',
|
||||
description_en: 'Capture damage images for each part (one request per part)',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: ['UPLOAD_REQUIRED_DOCUMENTS'],
|
||||
nextPossibleSteps: ['USER_SUBMISSION_COMPLETE'],
|
||||
allowedRoles: ['user'],
|
||||
estimatedDuration: 10,
|
||||
fields: [],
|
||||
metadata: {
|
||||
icon: 'camera',
|
||||
color: '#EF4444',
|
||||
dynamicFields: true,
|
||||
requiresAPI: '/claim-request-management/capture-car-part-damage/:claimRequestID/:partId',
|
||||
requiresCamera: true,
|
||||
multipleRequests: true,
|
||||
note: 'برای هر قطعه یک درخواست جداگانه ارسال میشود'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
userSubmissionComplete: {
|
||||
summary: 'User Submission Complete',
|
||||
value: {
|
||||
stepKey: 'USER_SUBMISSION_COMPLETE',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 6,
|
||||
isActive: true,
|
||||
stepName_fa: 'تکمیل ثبت اطلاعات کاربر',
|
||||
stepName_en: 'User Submission Complete',
|
||||
description_fa: 'اطلاعات کاربر به طور کامل ثبت شد و در انتظار بررسی کارشناس خسارت',
|
||||
description_en: 'User information completed, waiting for damage expert review',
|
||||
category: 'CLAIM',
|
||||
requiredPreviousSteps: ['CAPTURE_PART_DAMAGES'],
|
||||
nextPossibleSteps: ['EXPERT_DAMAGE_ASSESSMENT'],
|
||||
allowedRoles: ['user', 'damage_expert', 'admin'],
|
||||
estimatedDuration: 0,
|
||||
fields: [],
|
||||
metadata: {
|
||||
icon: 'check-circle',
|
||||
color: '#10B981',
|
||||
readOnly: true,
|
||||
isSystemStep: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
expertDamageAssessment: {
|
||||
summary: 'Expert Damage Assessment',
|
||||
value: {
|
||||
stepKey: 'EXPERT_DAMAGE_ASSESSMENT',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 7,
|
||||
isActive: true,
|
||||
stepName_fa: 'ارزیابی خسارت توسط کارشناس',
|
||||
stepName_en: 'Expert Damage Assessment',
|
||||
description_fa: 'بررسی و ارزیابی خسارت توسط کارشناس خسارت',
|
||||
description_en: 'Damage review and assessment by damage expert',
|
||||
category: 'EXPERT',
|
||||
requiredPreviousSteps: ['USER_SUBMISSION_COMPLETE'],
|
||||
nextPossibleSteps: ['EXPERT_COST_EVALUATION'],
|
||||
allowedRoles: ['damage_expert', 'admin'],
|
||||
estimatedDuration: 30,
|
||||
fields: [
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1005',
|
||||
name_fa: 'وضعیت ارزیابی',
|
||||
name_en: 'assessmentStatus',
|
||||
label_fa: 'وضعیت ارزیابی',
|
||||
label_en: 'Assessment Status',
|
||||
placeholder_fa: 'وضعیت ارزیابی را انتخاب کنید',
|
||||
placeholder_en: 'Select assessment status',
|
||||
value: [
|
||||
{ label_fa: 'تایید شده', label_en: 'Approved', value: 'approved' },
|
||||
{ label_fa: 'نیاز به بازنگری', label_en: 'Needs Revision', value: 'needs_revision' },
|
||||
{ label_fa: 'رد شده', label_en: 'Rejected', value: 'rejected' }
|
||||
],
|
||||
dataType: 'select',
|
||||
required: true,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
enum: ['approved', 'needs_revision', 'rejected']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1006',
|
||||
name_fa: 'توضیحات کارشناس',
|
||||
name_en: 'expertComments',
|
||||
label_fa: 'توضیحات کارشناس',
|
||||
label_en: 'Expert Comments',
|
||||
placeholder_fa: 'توضیحات خود را وارد کنید',
|
||||
placeholder_en: 'Enter your comments',
|
||||
value: null,
|
||||
dataType: 'textarea',
|
||||
required: true,
|
||||
order: 2,
|
||||
isActive: true,
|
||||
validation: {
|
||||
minLength: 10,
|
||||
maxLength: 2000
|
||||
}
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
icon: 'clipboard-check',
|
||||
color: '#8B5CF6',
|
||||
requiresExpertRole: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
expertCostEvaluation: {
|
||||
summary: 'Expert Cost Evaluation',
|
||||
value: {
|
||||
stepKey: 'EXPERT_COST_EVALUATION',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 8,
|
||||
isActive: true,
|
||||
stepName_fa: 'برآورد هزینه توسط کارشناس',
|
||||
stepName_en: 'Expert Cost Evaluation',
|
||||
description_fa: 'محاسبه و برآورد هزینه تعمیرات توسط کارشناس',
|
||||
description_en: 'Repair cost calculation and estimation by expert',
|
||||
category: 'EXPERT',
|
||||
requiredPreviousSteps: ['EXPERT_DAMAGE_ASSESSMENT'],
|
||||
nextPossibleSteps: ['INSURER_REVIEW'],
|
||||
allowedRoles: ['damage_expert', 'admin'],
|
||||
estimatedDuration: 20,
|
||||
fields: [
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1007',
|
||||
name_fa: 'هزینه کل برآورد شده',
|
||||
name_en: 'estimatedTotalCost',
|
||||
label_fa: 'هزینه کل برآورد شده (ریال)',
|
||||
label_en: 'Estimated Total Cost (Rials)',
|
||||
placeholder_fa: 'مبلغ را وارد کنید',
|
||||
placeholder_en: 'Enter amount',
|
||||
value: null,
|
||||
dataType: 'number',
|
||||
required: true,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
min: 0,
|
||||
max: 999999999999
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1008',
|
||||
name_fa: 'جزئیات هزینه',
|
||||
name_en: 'costBreakdown',
|
||||
label_fa: 'جزئیات هزینه',
|
||||
label_en: 'Cost Breakdown',
|
||||
placeholder_fa: 'جزئیات هزینه را وارد کنید',
|
||||
placeholder_en: 'Enter cost breakdown',
|
||||
value: null,
|
||||
dataType: 'textarea',
|
||||
required: true,
|
||||
order: 2,
|
||||
isActive: true,
|
||||
validation: {
|
||||
minLength: 20,
|
||||
maxLength: 5000
|
||||
}
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
icon: 'dollar-sign',
|
||||
color: '#F59E0B',
|
||||
requiresExpertRole: true,
|
||||
requiresCostCalculation: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
insurerReview: {
|
||||
summary: 'Insurer Review',
|
||||
value: {
|
||||
stepKey: 'INSURER_REVIEW',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 9,
|
||||
isActive: true,
|
||||
stepName_fa: 'بررسی بیمهگر',
|
||||
stepName_en: 'Insurer Review',
|
||||
description_fa: 'بررسی نهایی و تایید یا رد خسارت توسط بیمهگر',
|
||||
description_en: 'Final review and approval/rejection by insurer',
|
||||
category: 'INSURER',
|
||||
requiredPreviousSteps: ['EXPERT_COST_EVALUATION'],
|
||||
nextPossibleSteps: ['CLAIM_COMPLETED'],
|
||||
allowedRoles: ['insurer', 'admin'],
|
||||
estimatedDuration: 60,
|
||||
fields: [
|
||||
{
|
||||
id: '67fa10c259e15f231a2d1009',
|
||||
name_fa: 'تصمیم بیمهگر',
|
||||
name_en: 'insurerDecision',
|
||||
label_fa: 'تصمیم بیمهگر',
|
||||
label_en: 'Insurer Decision',
|
||||
placeholder_fa: 'تصمیم خود را انتخاب کنید',
|
||||
placeholder_en: 'Select your decision',
|
||||
value: [
|
||||
{ label_fa: 'تایید', label_en: 'Approved', value: 'approved' },
|
||||
{ label_fa: 'رد', label_en: 'Rejected', value: 'rejected' }
|
||||
],
|
||||
dataType: 'select',
|
||||
required: true,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
enum: ['approved', 'rejected']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '67fa10c259e15f231a2d100a',
|
||||
name_fa: 'توضیحات بیمهگر',
|
||||
name_en: 'insurerNotes',
|
||||
label_fa: 'توضیحات بیمهگر',
|
||||
label_en: 'Insurer Notes',
|
||||
placeholder_fa: 'توضیحات خود را وارد کنید',
|
||||
placeholder_en: 'Enter your notes',
|
||||
value: null,
|
||||
dataType: 'textarea',
|
||||
required: true,
|
||||
order: 2,
|
||||
isActive: true,
|
||||
validation: {
|
||||
minLength: 10,
|
||||
maxLength: 2000
|
||||
}
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
icon: 'shield-check',
|
||||
color: '#6366F1',
|
||||
requiresInsurerRole: true,
|
||||
isFinalStep: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
claimCompleted: {
|
||||
summary: 'Claim Completed',
|
||||
value: {
|
||||
stepKey: 'CLAIM_COMPLETED',
|
||||
type: 'CLAIM',
|
||||
stepNumber: 10,
|
||||
isActive: true,
|
||||
stepName_fa: 'تکمیل خسارت',
|
||||
stepName_en: 'Claim Completed',
|
||||
description_fa: 'خسارت به طور کامل بررسی و نتیجهگیری شده است',
|
||||
description_en: 'Claim has been fully reviewed and concluded',
|
||||
category: 'FINAL',
|
||||
requiredPreviousSteps: ['INSURER_REVIEW'],
|
||||
nextPossibleSteps: [],
|
||||
allowedRoles: ['user', 'damage_expert', 'insurer', 'admin'],
|
||||
estimatedDuration: 0,
|
||||
fields: [],
|
||||
metadata: {
|
||||
icon: 'check-circle',
|
||||
color: '#10B981',
|
||||
readOnly: true,
|
||||
isSystemStep: true,
|
||||
isFinalStep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const updateClaimWorkflowStepExamples = {
|
||||
example1: {
|
||||
summary: 'Update claim step name',
|
||||
value: {
|
||||
stepName_fa: 'انتخاب قطعات خارجی - بروزرسانی شده',
|
||||
stepName_en: 'Select Outer Parts - Updated',
|
||||
category: 'CLAIM',
|
||||
isActive: true
|
||||
}
|
||||
},
|
||||
example2: {
|
||||
summary: 'Update claim step metadata',
|
||||
value: {
|
||||
metadata: {
|
||||
icon: 'car',
|
||||
color: '#10B981',
|
||||
requiresMultipleSelection: true,
|
||||
priority: 'high'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,254 @@
|
||||
import { IsNotEmpty, IsString, IsNumber, IsBoolean, IsOptional, IsArray, IsEnum, ValidateNested, IsObject } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { ClaimWorkflowStep } from 'src/Types&Enums/claim-request-management/claim-workflow-steps.enum';
|
||||
import { WorkflowStep } from 'src/Types&Enums/blame-request-management/blameWorkflow-steps.enum';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
// Combined enum for validation
|
||||
const AllWorkflowSteps = { ...WorkflowStep, ...ClaimWorkflowStep };
|
||||
|
||||
export class ClaimStepFieldDto {
|
||||
@ApiProperty({
|
||||
description: 'Unique identifier for the field',
|
||||
example: '67fa10c259e15f231a2d1001'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
id: Types.ObjectId;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Field name in Farsi',
|
||||
example: 'قطعات خارجی'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name_fa: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Field name in English',
|
||||
example: 'outerParts'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name_en: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Field label in Farsi',
|
||||
example: 'قطعات خارجی آسیبدیده'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
label_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Field label in English',
|
||||
example: 'Damaged Outer Parts'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
label_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Placeholder text in Farsi',
|
||||
example: 'قطعات خارجی آسیبدیده را انتخاب کنید'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
placeholder_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Placeholder text in English',
|
||||
example: 'Select damaged outer parts'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
placeholder_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Possible values for select/multiselect fields (null for non-select fields)',
|
||||
example: null,
|
||||
nullable: true,
|
||||
default: null
|
||||
})
|
||||
@IsOptional()
|
||||
value?: Object[] | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Data type of the field',
|
||||
example: 'multiselect',
|
||||
enum: ['string', 'number', 'boolean', 'date', 'select', 'multiselect', 'file', 'textarea', 'email', 'phone', 'object']
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
dataType: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the field is required',
|
||||
example: true,
|
||||
default: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
required?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Validation rules for the field',
|
||||
example: { minItems: 1, maxItems: 13 }
|
||||
})
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
validation?: Object;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Display order of the field in the form',
|
||||
example: 1
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
order?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the field is active',
|
||||
example: true,
|
||||
default: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export class CreateClaimWorkflowStepDto {
|
||||
@ApiProperty({
|
||||
description: 'Unique key identifying the claim workflow step',
|
||||
example: 'CLAIM_CREATED',
|
||||
enum: AllWorkflowSteps
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsEnum(AllWorkflowSteps)
|
||||
stepKey: ClaimWorkflowStep | WorkflowStep;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Workflow type: CLAIM for claim workflow',
|
||||
example: 'CLAIM',
|
||||
default: 'CLAIM'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Order number of the step in the claim workflow',
|
||||
example: 1
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
stepNumber: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the step is active',
|
||||
example: true,
|
||||
default: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Step name in Farsi',
|
||||
example: 'ایجاد درخواست خسارت'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
stepName_fa: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Step name in English',
|
||||
example: 'Create Claim Request'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
stepName_en: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Step description in Farsi',
|
||||
example: 'ایجاد درخواست خسارت از پرونده تعیین تقصیر تکمیل شده'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Step description in English',
|
||||
example: 'Create claim request from completed blame case'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Category of the step',
|
||||
example: 'CLAIM',
|
||||
enum: ['CLAIM', 'EXPERT', 'INSURER', 'FINAL']
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
category?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Dynamic fields for this step',
|
||||
type: [ClaimStepFieldDto],
|
||||
example: []
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ClaimStepFieldDto)
|
||||
fields?: ClaimStepFieldDto[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Steps that must be completed before this step',
|
||||
example: [],
|
||||
enum: AllWorkflowSteps,
|
||||
isArray: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsEnum(AllWorkflowSteps, { each: true })
|
||||
requiredPreviousSteps?: (ClaimWorkflowStep | WorkflowStep)[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Possible next steps after completing this step',
|
||||
example: ['SELECT_OUTER_PARTS'],
|
||||
enum: AllWorkflowSteps,
|
||||
isArray: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsEnum(AllWorkflowSteps, { each: true })
|
||||
nextPossibleSteps?: (ClaimWorkflowStep | WorkflowStep)[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Additional metadata for the step',
|
||||
example: { icon: 'file-plus', color: '#8B5CF6' }
|
||||
})
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Estimated duration in minutes',
|
||||
example: 1
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
estimatedDuration?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Roles allowed to access this step',
|
||||
example: ['user', 'damage_expert', 'admin']
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
allowedRoles?: string[];
|
||||
}
|
||||
271
src/workflow-step-management/dto/create-workflow-step.dto.ts
Normal file
271
src/workflow-step-management/dto/create-workflow-step.dto.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import { IsNotEmpty, IsString, IsNumber, IsBoolean, IsOptional, IsArray, IsEnum, ValidateNested, IsObject } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { WorkflowStep } from 'src/Types&Enums/blame-request-management/blameWorkflow-steps.enum';
|
||||
import { ClaimWorkflowStep } from 'src/Types&Enums/claim-request-management/claim-workflow-steps.enum';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
// Combined enum for validation
|
||||
const AllWorkflowSteps = { ...WorkflowStep, ...ClaimWorkflowStep };
|
||||
|
||||
export class StepFieldDto {
|
||||
@ApiProperty({
|
||||
description: 'Unique identifier for the field',
|
||||
example: '507f1f77bcf86cd799439011'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
id: Types.ObjectId;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Field name in Farsi',
|
||||
example: 'فایل ویدیو'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name_fa: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Field name in English',
|
||||
example: 'videoFile'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name_en: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Field label in Farsi',
|
||||
example: 'فایل ویدیو'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
label_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Field label in English',
|
||||
example: 'Video File'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
label_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Placeholder text in Farsi',
|
||||
example: 'فایل ویدیو خود را آپلود کنید'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
placeholder_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Placeholder text in English',
|
||||
example: 'Upload your video file'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
placeholder_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Possible values for select/multiselect fields (null for non-select fields)',
|
||||
example: null,
|
||||
nullable: true,
|
||||
default: null
|
||||
})
|
||||
@IsOptional()
|
||||
value?: Object[] | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Data type of the field',
|
||||
example: 'file',
|
||||
enum: ['string', 'number', 'boolean', 'date', 'select', 'multiselect', 'file', 'textarea', 'email', 'phone']
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
dataType: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the field is required',
|
||||
example: true,
|
||||
default: false
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
required?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Validation rules for the field',
|
||||
example: { maxSize: 52428800, allowedTypes: ['video/mp4', 'video/webm'] }
|
||||
})
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
validation?: Object;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Display order of the field in the form',
|
||||
example: 1
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
order?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the field is active',
|
||||
example: true,
|
||||
default: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export class CreateWorkflowStepDto {
|
||||
@ApiProperty({
|
||||
description: 'Unique key identifying the workflow step (blame or claim)',
|
||||
example: 'FIRST_VIDEO',
|
||||
enum: AllWorkflowSteps
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsEnum(AllWorkflowSteps)
|
||||
stepKey: WorkflowStep | ClaimWorkflowStep;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Workflow type: THIRD_PARTY (blame) or CLAIM (claim workflow)',
|
||||
example: 'THIRD_PARTY',
|
||||
enum: ['THIRD_PARTY', 'CLAIM']
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Order number of the step within this workflow type',
|
||||
example: 2
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
stepNumber: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Whether the step is active',
|
||||
example: true,
|
||||
default: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Step name in Farsi',
|
||||
example: 'ویدیو طرف اول'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
stepName_fa: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Step name in English',
|
||||
example: 'First Party Video'
|
||||
})
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
stepName_en: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Step description in Farsi',
|
||||
example: 'ضبط ویدیو توسط طرف اول'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description_fa?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Step description in English',
|
||||
example: 'Video capture by first party'
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description_en?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Category of the step within workflow (e.g., INITIAL, FIRST_PARTY, EXPERT)',
|
||||
example: 'FIRST_PARTY',
|
||||
enum: ['INITIAL', 'FIRST_PARTY', 'SECOND_PARTY', 'CLAIM', 'EXPERT', 'INSURER', 'FINAL']
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
category?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Dynamic fields for this step',
|
||||
type: [StepFieldDto],
|
||||
example: [
|
||||
{
|
||||
id: '507f1f77bcf86cd799439011',
|
||||
name_fa: 'فایل ویدیو',
|
||||
name_en: 'videoFile',
|
||||
label_fa: 'فایل ویدیو',
|
||||
label_en: 'Video File',
|
||||
value: null,
|
||||
dataType: 'file',
|
||||
required: true,
|
||||
order: 1,
|
||||
isActive: true,
|
||||
validation: {
|
||||
maxSize: 52428800,
|
||||
allowedTypes: ['video/mp4', 'video/webm']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => StepFieldDto)
|
||||
fields?: StepFieldDto[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Steps that must be completed before this step',
|
||||
example: ['CREATED'],
|
||||
enum: AllWorkflowSteps,
|
||||
isArray: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsEnum(AllWorkflowSteps, { each: true })
|
||||
requiredPreviousSteps?: (WorkflowStep | ClaimWorkflowStep)[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Possible next steps after completing this step',
|
||||
example: ['FIRST_INITIAL_FORM'],
|
||||
enum: AllWorkflowSteps,
|
||||
isArray: true
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsEnum(AllWorkflowSteps, { each: true })
|
||||
nextPossibleSteps?: (WorkflowStep | ClaimWorkflowStep)[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Additional metadata for the step',
|
||||
example: { icon: 'video', color: '#3B82F6' }
|
||||
})
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Estimated duration in minutes',
|
||||
example: 5
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
estimatedDuration?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Roles allowed to access this step',
|
||||
example: ['user', 'expert', 'admin']
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
allowedRoles?: string[];
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateWorkflowStepDto } from './create-workflow-step.dto';
|
||||
|
||||
/**
|
||||
* Update DTO inherits all fields from Create DTO but makes them all optional
|
||||
* Supports both blame and claim workflow steps
|
||||
*/
|
||||
export class UpdateWorkflowStepDto extends PartialType(CreateWorkflowStepDto) {}
|
||||
1541
src/workflow-step-management/dto/workflow-step.examples.ts
Normal file
1541
src/workflow-step-management/dto/workflow-step.examples.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,156 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { WorkflowStepModel, WorkflowStepsDB } from '../schema/workflow-step.schema';
|
||||
import { CreateWorkflowStepDto } from '../../dto/create-workflow-step.dto';
|
||||
import { UpdateWorkflowStepDto } from '../../dto/update-workflow-step.dto';
|
||||
import { WorkflowStep } from 'src/Types&Enums/blame-request-management/blameWorkflow-steps.enum';
|
||||
import { ClaimWorkflowStep } from 'src/Types&Enums/claim-request-management/claim-workflow-steps.enum';
|
||||
|
||||
// Type alias for combined workflow steps
|
||||
type AnyWorkflowStep = WorkflowStep | ClaimWorkflowStep;
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowStepDbService {
|
||||
constructor(
|
||||
@InjectModel(WorkflowStepsDB)
|
||||
private readonly workflowStepModel: Model<WorkflowStepModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create a new workflow step
|
||||
*/
|
||||
async create(createDto: CreateWorkflowStepDto): Promise<WorkflowStepModel> {
|
||||
const created = new this.workflowStepModel(createDto);
|
||||
return await created.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all active workflow steps
|
||||
*/
|
||||
async findAll(): Promise<WorkflowStepModel[]> {
|
||||
return await this.workflowStepModel
|
||||
.find({ isActive: true })
|
||||
.sort({ stepNumber: 1 })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all workflow steps including inactive ones
|
||||
*/
|
||||
async findAllIncludingInactive(): Promise<WorkflowStepModel[]> {
|
||||
return await this.workflowStepModel
|
||||
.find()
|
||||
.sort({ stepNumber: 1 })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a workflow step by ID
|
||||
*/
|
||||
async findById(id: string): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel.findById(id).exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a workflow step by step key (supports both blame and claim)
|
||||
*/
|
||||
async findByStepKey(stepKey: AnyWorkflowStep): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel.findOne({ stepKey }).exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a workflow step by step number
|
||||
*/
|
||||
async findByStepNumber(stepNumber: number): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel.findOne({ stepNumber }).exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find steps by category
|
||||
*/
|
||||
async findByCategory(category: string): Promise<WorkflowStepModel[]> {
|
||||
return await this.workflowStepModel
|
||||
.find({ category, isActive: true })
|
||||
.sort({ stepNumber: 1 })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a workflow step by ID
|
||||
*/
|
||||
async update(id: string, updateDto: UpdateWorkflowStepDto): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel
|
||||
.findByIdAndUpdate(id, updateDto, { new: true })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a workflow step by step key (supports both blame and claim)
|
||||
*/
|
||||
async updateByStepKey(
|
||||
stepKey: AnyWorkflowStep,
|
||||
updateDto: UpdateWorkflowStepDto,
|
||||
): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel
|
||||
.findOneAndUpdate({ stepKey }, updateDto, { new: true })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete (deactivate) a workflow step
|
||||
*/
|
||||
async remove(id: string): Promise<WorkflowStepModel | null> {
|
||||
return await this.workflowStepModel
|
||||
.findByIdAndUpdate(id, { isActive: false }, { new: true })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert a workflow step by step key (supports both blame and claim)
|
||||
*/
|
||||
async upsertByStepKey(
|
||||
stepKey: AnyWorkflowStep,
|
||||
data: CreateWorkflowStepDto | UpdateWorkflowStepDto,
|
||||
): Promise<WorkflowStepModel> {
|
||||
return await this.workflowStepModel
|
||||
.findOneAndUpdate({ stepKey }, data, { upsert: true, new: true })
|
||||
.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk upsert workflow steps
|
||||
*/
|
||||
async bulkUpsert(steps: CreateWorkflowStepDto[]): Promise<any> {
|
||||
const bulkOps = steps.map((step) => ({
|
||||
updateOne: {
|
||||
filter: { stepKey: step.stepKey },
|
||||
update: { $set: step as any },
|
||||
upsert: true,
|
||||
},
|
||||
}));
|
||||
|
||||
return await this.workflowStepModel.bulkWrite(bulkOps as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next step number
|
||||
*/
|
||||
async getNextStepNumber(): Promise<number> {
|
||||
const maxStep = await this.workflowStepModel
|
||||
.findOne()
|
||||
.sort({ stepNumber: -1 })
|
||||
.select('stepNumber')
|
||||
.exec();
|
||||
|
||||
return maxStep ? maxStep.stepNumber + 1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a step exists (supports both blame and claim)
|
||||
*/
|
||||
async exists(stepKey: AnyWorkflowStep): Promise<boolean> {
|
||||
const count = await this.workflowStepModel.countDocuments({ stepKey }).exec();
|
||||
return count > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
import { WorkflowStep } from 'src/Types&Enums/blame-request-management/blameWorkflow-steps.enum';
|
||||
import { ClaimWorkflowStep } from 'src/Types&Enums/claim-request-management/claim-workflow-steps.enum';
|
||||
|
||||
// Combined enum values for both blame and claim workflows
|
||||
const allWorkflowSteps = [...Object.values(WorkflowStep), ...Object.values(ClaimWorkflowStep)];
|
||||
|
||||
export const WorkflowStepsDB = 'WorkflowSteps';
|
||||
|
||||
export type FieldValueType = Object | string | number | boolean | null;
|
||||
|
||||
/**
|
||||
* Field definition for dynamic form generation
|
||||
* Each step can have multiple fields that represent form inputs
|
||||
*/
|
||||
@Schema({ _id: false })
|
||||
export class StepField {
|
||||
@Prop({ required: true, type: Types.ObjectId })
|
||||
id: Types.ObjectId;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
name_fa: string;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
name_en: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
label_fa?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
label_en?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
placeholder_fa?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
placeholder_en?: string;
|
||||
|
||||
@Prop({ type: [Object], default: null })
|
||||
value: Object[] | null;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
dataType: string; // e.g., 'string', 'number', 'boolean', 'date', 'select', 'multiselect', 'file', etc.
|
||||
|
||||
@Prop({ type: Boolean, default: false })
|
||||
required?: boolean;
|
||||
|
||||
@Prop({ type: Object })
|
||||
validation?: Object; // Validation rules (min, max, pattern, etc.)
|
||||
|
||||
@Prop({ type: Number })
|
||||
order?: number; // Field order in the form
|
||||
|
||||
@Prop({ type: Boolean, default: true })
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export const StepFieldSchema = SchemaFactory.createForClass(StepField);
|
||||
|
||||
/**
|
||||
* Workflow Step Model
|
||||
* Defines the configuration for each step in the workflow
|
||||
*/
|
||||
@Schema({ versionKey: false, collection: 'workflowSteps', timestamps: true })
|
||||
export class WorkflowStepModel {
|
||||
@Prop({ required: true, type: String, enum: allWorkflowSteps, unique: true })
|
||||
stepKey: WorkflowStep | ClaimWorkflowStep;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
type: string; // 'THIRD_PARTY' (blame workflow) or 'CLAIM' (claim workflow)
|
||||
|
||||
@Prop({ required: true, type: Number })
|
||||
stepNumber: number;
|
||||
|
||||
@Prop({ type: Boolean, default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
stepName_fa: string;
|
||||
|
||||
@Prop({ required: true, type: String })
|
||||
stepName_en: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
description_fa?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
description_en?: string;
|
||||
|
||||
@Prop({ type: String })
|
||||
category?: string; // e.g., 'INITIAL', 'FIRST_PARTY', 'SECOND_PARTY', 'EXPERT', 'INSURER'
|
||||
|
||||
@Prop({ type: [StepFieldSchema], default: [] })
|
||||
fields?: StepField[];
|
||||
|
||||
@Prop({ type: [String], enum: allWorkflowSteps, default: [] })
|
||||
requiredPreviousSteps?: (WorkflowStep | ClaimWorkflowStep)[]; // Steps that must be completed before this step
|
||||
|
||||
@Prop({ type: [String], enum: allWorkflowSteps, default: [] })
|
||||
nextPossibleSteps?: (WorkflowStep | ClaimWorkflowStep)[]; // Possible next steps after completing this step
|
||||
|
||||
@Prop({ type: Object })
|
||||
metadata?: Record<string, any>; // Additional metadata for the step
|
||||
|
||||
@Prop({ type: Number })
|
||||
estimatedDuration?: number; // Estimated duration in minutes
|
||||
|
||||
@Prop({ type: [String], default: [] })
|
||||
allowedRoles?: string[]; // Roles allowed to access this step
|
||||
}
|
||||
|
||||
export const WorkflowStepSchema = SchemaFactory.createForClass(WorkflowStepModel);
|
||||
|
||||
// Create indexes for better query performance
|
||||
WorkflowStepSchema.index({ stepKey: 1 }); // Unique via @Prop
|
||||
WorkflowStepSchema.index({ type: 1, isActive: 1 }); // Query by workflow type
|
||||
WorkflowStepSchema.index({ type: 1, stepNumber: 1 }, { unique: true }); // Unique stepNumber within workflow type
|
||||
WorkflowStepSchema.index({ type: 1, category: 1, stepNumber: 1 }); // Sorting within workflow and category
|
||||
8
src/workflow-step-management/index.ts
Normal file
8
src/workflow-step-management/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './workflow-step-management.module';
|
||||
export * from './workflow-step-management.service';
|
||||
export * from './workflow-step-management.controller';
|
||||
export * from './dto/create-workflow-step.dto';
|
||||
export * from './dto/update-workflow-step.dto';
|
||||
export * from './dto/workflow-step.examples';
|
||||
export * from './entities/schema/workflow-step.schema';
|
||||
export * from './entities/db-service/workflow-step.db.service';
|
||||
@@ -0,0 +1,353 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiBody,
|
||||
ApiParam,
|
||||
ApiQuery,
|
||||
ApiBearerAuth
|
||||
} from '@nestjs/swagger';
|
||||
import { WorkflowStepManagementService } from './workflow-step-management.service';
|
||||
import { CreateWorkflowStepDto } from './dto/create-workflow-step.dto';
|
||||
import { UpdateWorkflowStepDto } from './dto/update-workflow-step.dto';
|
||||
import { WorkflowStep } from 'src/Types&Enums/blame-request-management/blameWorkflow-steps.enum';
|
||||
import { ClaimWorkflowStep } from 'src/Types&Enums/claim-request-management/claim-workflow-steps.enum';
|
||||
import {
|
||||
createWorkflowStepExamples,
|
||||
updateWorkflowStepExamples,
|
||||
bulkUpsertWorkflowStepExamples,
|
||||
validateAccessExamples,
|
||||
claimWorkflowStepExamples,
|
||||
} from './dto/workflow-step.examples';
|
||||
|
||||
@ApiTags('Workflow Step Management')
|
||||
@Controller('workflow-step-management')
|
||||
export class WorkflowStepManagementController {
|
||||
constructor(
|
||||
private readonly workflowStepManagementService: WorkflowStepManagementService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create a new workflow step configuration
|
||||
*/
|
||||
@Post()
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@ApiOperation({
|
||||
summary: 'Create a new workflow step',
|
||||
description: 'Creates a new workflow step configuration with fields and validation rules'
|
||||
})
|
||||
@ApiBody({
|
||||
type: CreateWorkflowStepDto,
|
||||
examples: {
|
||||
...createWorkflowStepExamples,
|
||||
...claimWorkflowStepExamples
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 201, description: 'Workflow step created successfully' })
|
||||
@ApiResponse({ status: 400, description: 'Bad request - Invalid input data' })
|
||||
@ApiResponse({ status: 409, description: 'Conflict - Step already exists' })
|
||||
async create(@Body() createDto: CreateWorkflowStepDto) {
|
||||
return await this.workflowStepManagementService.create(createDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active workflow steps
|
||||
*/
|
||||
@Get()
|
||||
@ApiOperation({
|
||||
summary: 'Get all workflow steps',
|
||||
description: 'Retrieves all workflow steps, optionally including inactive ones'
|
||||
})
|
||||
@ApiQuery({
|
||||
name: 'includeInactive',
|
||||
required: false,
|
||||
type: String,
|
||||
description: 'Set to "true" to include inactive steps',
|
||||
example: 'false'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'List of workflow steps retrieved successfully' })
|
||||
async findAll(@Query('includeInactive') includeInactive?: string) {
|
||||
if (includeInactive === 'true') {
|
||||
return await this.workflowStepManagementService.findAllIncludingInactive();
|
||||
}
|
||||
return await this.workflowStepManagementService.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a workflow step by ID
|
||||
*/
|
||||
@Patch(':id')
|
||||
@ApiOperation({
|
||||
summary: 'Update workflow step by ID',
|
||||
description: 'Updates a workflow step using its database ID'
|
||||
})
|
||||
@ApiParam({ name: 'id', description: 'Workflow step ID', example: '507f1f77bcf86cd799439011' })
|
||||
@ApiBody({
|
||||
type: UpdateWorkflowStepDto,
|
||||
examples: updateWorkflowStepExamples
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Workflow step updated successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async update(@Param('id') id: string, @Body() updateDto: UpdateWorkflowStepDto) {
|
||||
return await this.workflowStepManagementService.update(id, updateDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a workflow step by key
|
||||
*/
|
||||
@Patch('step/:stepKey')
|
||||
@ApiOperation({
|
||||
summary: 'Update workflow step by key',
|
||||
description: 'Updates a workflow step using its step key from the enum'
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'stepKey',
|
||||
description: 'Workflow step key (blame or claim)',
|
||||
example: 'FIRST_VIDEO'
|
||||
})
|
||||
@ApiBody({
|
||||
type: UpdateWorkflowStepDto,
|
||||
examples: updateWorkflowStepExamples
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Workflow step updated successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async updateByStepKey(
|
||||
@Param('stepKey') stepKey: WorkflowStep | ClaimWorkflowStep,
|
||||
@Body() updateDto: UpdateWorkflowStepDto,
|
||||
) {
|
||||
return await this.workflowStepManagementService.updateByStepKey(stepKey as any, updateDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default workflow steps
|
||||
*/
|
||||
@Post('initialize')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Initialize default workflow steps',
|
||||
description: 'Creates default workflow step configurations for the system'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Default steps initialized successfully' })
|
||||
async initialize() {
|
||||
return await this.workflowStepManagementService.initializeDefaultSteps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific step by stepKey or stepNumber (intelligent lookup)
|
||||
*/
|
||||
@Get('find/:identifier')
|
||||
@ApiOperation({
|
||||
summary: 'Get workflow step by stepKey or stepNumber',
|
||||
description: 'Retrieves a workflow step by either its stepKey (enum) or stepNumber. Automatically detects which one is provided.'
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'identifier',
|
||||
description: 'Step key (e.g., "FIRST_VIDEO") or step number (e.g., 3)',
|
||||
example: 'FIRST_VIDEO',
|
||||
examples: {
|
||||
byStepKey: {
|
||||
summary: 'By Step Key',
|
||||
value: 'FIRST_VIDEO'
|
||||
},
|
||||
byStepNumber: {
|
||||
summary: 'By Step Number',
|
||||
value: 3
|
||||
}
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Workflow step retrieved successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async findByStepKeyOrNumber(@Param('identifier') identifier: string) {
|
||||
return await this.workflowStepManagementService.findByStepKeyOrNumber(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific step by ID
|
||||
*/
|
||||
@Get(':id')
|
||||
@ApiOperation({
|
||||
summary: 'Get workflow step by ID',
|
||||
description: 'Retrieves a specific workflow step by its database ID'
|
||||
})
|
||||
@ApiParam({ name: 'id', description: 'Workflow step ID', example: '507f1f77bcf86cd799439011' })
|
||||
@ApiResponse({ status: 200, description: 'Workflow step retrieved successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async findById(@Param('id') id: string) {
|
||||
return await this.workflowStepManagementService.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete a step
|
||||
*/
|
||||
@Delete(':id')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Delete workflow step',
|
||||
description: 'Soft deletes a workflow step by setting isActive to false'
|
||||
})
|
||||
@ApiParam({ name: 'id', description: 'Workflow step ID', example: '507f1f77bcf86cd799439011' })
|
||||
@ApiResponse({ status: 200, description: 'Workflow step deleted successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async remove(@Param('id') id: string) {
|
||||
return await this.workflowStepManagementService.remove(id);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// DEPRECATED ENDPOINTS - Will be removed in future versions
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* @deprecated Use individual create endpoint instead
|
||||
* Bulk upsert steps (for initial setup or migrations)
|
||||
*/
|
||||
@Post('bulk-upsert')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Bulk upsert workflow steps',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version. Use the individual create endpoint instead.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiBody({
|
||||
type: [CreateWorkflowStepDto],
|
||||
examples: bulkUpsertWorkflowStepExamples
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Steps upserted successfully' })
|
||||
async bulkUpsert(@Body() steps: CreateWorkflowStepDto[]) {
|
||||
return await this.workflowStepManagementService.bulkUpsert(steps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This endpoint is no longer needed
|
||||
* Validate step access
|
||||
*/
|
||||
@Post('validate-access')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Validate step access',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
stepKey: {
|
||||
type: 'string',
|
||||
description: 'Workflow step key (blame or claim)',
|
||||
example: 'FIRST_VIDEO'
|
||||
},
|
||||
completedSteps: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
example: ['CREATED']
|
||||
}
|
||||
}
|
||||
},
|
||||
examples: validateAccessExamples
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Validation result returned' })
|
||||
async validateAccess(
|
||||
@Body() body: { stepKey: WorkflowStep | ClaimWorkflowStep; completedSteps: (WorkflowStep | ClaimWorkflowStep)[] },
|
||||
) {
|
||||
return await this.workflowStepManagementService.validateStepAccess(
|
||||
body.stepKey as any,
|
||||
body.completedSteps as any,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This endpoint is no longer needed
|
||||
* Get steps by category
|
||||
*/
|
||||
@Get('category/:category')
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Get steps by category',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'category',
|
||||
description: 'Step category',
|
||||
example: 'FIRST_PARTY',
|
||||
enum: ['INITIAL', 'FIRST_PARTY', 'SECOND_PARTY', 'RESOLUTION']
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Steps retrieved successfully' })
|
||||
async findByCategory(@Param('category') category: string) {
|
||||
return await this.workflowStepManagementService.findByCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This endpoint is no longer needed
|
||||
* Get next possible steps from current step
|
||||
*/
|
||||
@Get('next-steps/:stepKey')
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Get next possible steps',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'stepKey',
|
||||
description: 'Current workflow step key (blame or claim)',
|
||||
example: 'FIRST_VIDEO'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Next steps retrieved successfully' })
|
||||
async getNextSteps(@Param('stepKey') stepKey: WorkflowStep | ClaimWorkflowStep) {
|
||||
return await this.workflowStepManagementService.getNextSteps(stepKey as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This endpoint is no longer needed
|
||||
* Get step fields (for form generation)
|
||||
*/
|
||||
@Get('fields/:stepKey')
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Get step fields',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'stepKey',
|
||||
description: 'Workflow step key (blame or claim)',
|
||||
example: 'FIRST_VIDEO'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Step fields retrieved successfully' })
|
||||
async getStepFields(@Param('stepKey') stepKey: WorkflowStep | ClaimWorkflowStep) {
|
||||
return await this.workflowStepManagementService.getStepFields(stepKey as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use findById instead
|
||||
* Get a specific step by key
|
||||
*/
|
||||
@Get('step/:stepKey')
|
||||
@ApiOperation({
|
||||
summary: '[DEPRECATED] Get workflow step by key',
|
||||
description: 'This endpoint is deprecated and will be removed in a future version. Use GET /:id instead.',
|
||||
deprecated: true
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'stepKey',
|
||||
description: 'Workflow step key (blame or claim)',
|
||||
example: 'FIRST_VIDEO'
|
||||
})
|
||||
@ApiResponse({ status: 200, description: 'Workflow step retrieved successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Workflow step not found' })
|
||||
async findByStepKey(@Param('stepKey') stepKey: WorkflowStep | ClaimWorkflowStep) {
|
||||
return await this.workflowStepManagementService.findByStepKey(stepKey as any);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { WorkflowStepManagementService } from './workflow-step-management.service';
|
||||
import { WorkflowStepManagementController } from './workflow-step-management.controller';
|
||||
import { WorkflowStepDbService } from './entities/db-service/workflow-step.db.service';
|
||||
import {
|
||||
WorkflowStepsDB,
|
||||
WorkflowStepSchema,
|
||||
} from './entities/schema/workflow-step.schema';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([
|
||||
{ name: WorkflowStepsDB, schema: WorkflowStepSchema },
|
||||
]),
|
||||
],
|
||||
controllers: [WorkflowStepManagementController],
|
||||
providers: [WorkflowStepManagementService, WorkflowStepDbService],
|
||||
exports: [WorkflowStepManagementService, WorkflowStepDbService],
|
||||
})
|
||||
export class WorkflowStepManagementModule {}
|
||||
1131
src/workflow-step-management/workflow-step-management.service.ts
Normal file
1131
src/workflow-step-management/workflow-step-management.service.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user