Files
yara724api/src/workflow-step-management/workflow-step-management.service.ts
2026-03-14 13:36:05 +03:30

1169 lines
40 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Injectable, NotFoundException, BadRequestException, ConflictException } from '@nestjs/common';
import { WorkflowStepDbService } from './entities/db-service/workflow-step.db.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 { WorkflowStepModel } from './entities/schema/workflow-step.schema';
// Type alias for combined workflow steps
type AnyWorkflowStep = WorkflowStep | ClaimWorkflowStep;
@Injectable()
export class WorkflowStepManagementService {
constructor(
private readonly workflowStepDbService: WorkflowStepDbService,
) {}
/**
* Create a new workflow step
*/
async create(createDto: CreateWorkflowStepDto): Promise<WorkflowStepModel> {
// Check if step already exists
const exists = await this.workflowStepDbService.exists(createDto.stepKey);
if (exists) {
throw new ConflictException(`Workflow step with key ${createDto.stepKey} already exists`);
}
return await this.workflowStepDbService.create(createDto);
}
/**
* Get all active workflow steps
*/
async findAll(): Promise<WorkflowStepModel[]> {
return await this.workflowStepDbService.findAll();
}
/**
* Get all workflow steps including inactive ones
*/
async findAllIncludingInactive(): Promise<WorkflowStepModel[]> {
return await this.workflowStepDbService.findAllIncludingInactive();
}
/**
* Find a workflow step by ID
*/
async findById(id: string): Promise<WorkflowStepModel> {
const step = await this.workflowStepDbService.findById(id);
if (!step) {
throw new NotFoundException(`Workflow step with ID ${id} not found`);
}
return step;
}
/**
* Find a workflow step by step key (supports both blame and claim workflows)
*/
async findByStepKey(stepKey: AnyWorkflowStep): Promise<WorkflowStepModel> {
const step = await this.workflowStepDbService.findByStepKey(stepKey as any);
if (!step) {
throw new NotFoundException(`Workflow step with key ${stepKey} not found`);
}
return step;
}
/**
* Find a workflow step by stepKey or stepNumber (intelligent lookup)
*/
async findByStepKeyOrNumber(identifier: string | number): Promise<WorkflowStepModel> {
// If identifier is a number or can be parsed as a number, search by stepNumber
const stepNumber = typeof identifier === 'number' ? identifier : Number(identifier);
if (!isNaN(stepNumber)) {
// Search by step number
const step = await this.workflowStepDbService.findByStepNumber(stepNumber);
if (step) {
return step;
}
}
// Otherwise, search by stepKey (supports both blame and claim)
const step = await this.workflowStepDbService.findByStepKey(identifier as any);
if (!step) {
throw new NotFoundException(`Workflow step with identifier ${identifier} not found`);
}
return step;
}
/**
* Find steps by category
*/
async findByCategory(category: string): Promise<WorkflowStepModel[]> {
return await this.workflowStepDbService.findByCategory(category);
}
/**
* Update a workflow step by ID
*/
async update(id: string, updateDto: UpdateWorkflowStepDto): Promise<WorkflowStepModel> {
const updated = await this.workflowStepDbService.update(id, updateDto);
if (!updated) {
throw new NotFoundException(`Workflow step with ID ${id} not found`);
}
return updated;
}
/**
* Update a workflow step by step key (supports both blame and claim)
*/
async updateByStepKey(stepKey: AnyWorkflowStep, updateDto: UpdateWorkflowStepDto): Promise<WorkflowStepModel> {
const updated = await this.workflowStepDbService.updateByStepKey(stepKey as any, updateDto);
if (!updated) {
throw new NotFoundException(`Workflow step with key ${stepKey} not found`);
}
return updated;
}
/**
* Soft delete a workflow step
*/
async remove(id: string): Promise<WorkflowStepModel> {
const removed = await this.workflowStepDbService.remove(id);
if (!removed) {
throw new NotFoundException(`Workflow step with ID ${id} not found`);
}
return removed;
}
/**
* Get the next possible steps from a given step (supports both blame and claim)
*/
async getNextSteps(stepKey: AnyWorkflowStep): Promise<WorkflowStepModel[]> {
const currentStep = await this.findByStepKey(stepKey);
if (!currentStep.nextPossibleSteps || currentStep.nextPossibleSteps.length === 0) {
return [];
}
const nextSteps: WorkflowStepModel[] = [];
for (const nextStepKey of currentStep.nextPossibleSteps) {
const step = await this.workflowStepDbService.findByStepKey(nextStepKey as any);
if (step && step.isActive) {
nextSteps.push(step);
}
}
return nextSteps;
}
/**
* Validate if a step can be accessed based on completed steps (supports both blame and claim)
*/
async validateStepAccess(
stepKey: AnyWorkflowStep,
completedSteps: AnyWorkflowStep[],
): Promise<{ canAccess: boolean; missingSteps: AnyWorkflowStep[] }> {
const step = await this.findByStepKey(stepKey);
if (!step.requiredPreviousSteps || step.requiredPreviousSteps.length === 0) {
return { canAccess: true, missingSteps: [] };
}
const missingSteps = step.requiredPreviousSteps.filter(
(requiredStep) => !completedSteps.includes(requiredStep as any),
);
return {
canAccess: missingSteps.length === 0,
missingSteps,
};
}
/**
* Get the fields for a specific step (for dynamic form generation)
*/
async getStepFields(stepKey: AnyWorkflowStep): Promise<any> {
const step = await this.findByStepKey(stepKey);
return {
stepKey: step.stepKey,
stepName_fa: step.stepName_fa,
stepName_en: step.stepName_en,
description_fa: step.description_fa,
description_en: step.description_en,
fields: step.fields || [],
};
}
/**
* Bulk upsert workflow steps
*/
async bulkUpsert(steps: CreateWorkflowStepDto[]): Promise<any> {
return await this.workflowStepDbService.bulkUpsert(steps);
}
/**
* Initialize default workflow steps
*/
async initializeDefaultSteps(): Promise<any> {
const defaultSteps: CreateWorkflowStepDto[] = [
{
stepKey: WorkflowStep.CREATED,
type: 'THIRD_PARTY',
stepNumber: 1,
isActive: true,
stepName_fa: 'ایجاد نوع درخواست',
stepName_en: 'request_type',
description_fa: 'انتخاب نوع درخواست توسط کاربر (شخص ثالث یا بدنه)',
description_en: 'Select request type by user (THIRD_PARTY or CAR_BODY)',
category: 'INITIAL',
requiredPreviousSteps: [],
nextPossibleSteps: [WorkflowStep.FIRST_BLAME_CONFESSION],
allowedRoles: ['user'],
estimatedDuration: 1,
fields: [
{
id: '507f1f77bcf86cd799439011' as any,
name_fa: 'نوع درخواست',
name_en: 'requestType',
label_fa: 'نوع درخواست',
label_en: 'Request Type',
placeholder_fa: 'نوع درخواست خود را انتخاب کنید',
placeholder_en: 'Select your request type',
value: [
{ label_fa: 'شخص ثالث', label_en: 'Third Party', value: 'THIRD_PARTY' },
{ label_fa: 'بدنه', label_en: 'Car Body', value: 'CAR_BODY' }
],
dataType: 'select',
required: true,
order: 1,
isActive: true,
validation: {
enum: ['THIRD_PARTY', 'CAR_BODY']
}
}
],
metadata: {
icon: 'clipboard-list',
color: '#8B5CF6'
}
},
{
stepKey: WorkflowStep.FIRST_BLAME_CONFESSION,
type: 'THIRD_PARTY',
stepNumber: 2,
isActive: true,
stepName_fa: 'اعتراف طرف اول',
stepName_en: 'First Party Blame Confession',
description_fa: 'طرف اول وضعیت تقصیر و اعتراف خود را مشخص می‌کند',
description_en: 'First party declares blame status and confession',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.CREATED],
nextPossibleSteps: [WorkflowStep.FIRST_VIDEO],
allowedRoles: ['user'],
estimatedDuration: 2,
fields: [
{
id: '507f1f77bcf86cd799439012' as any,
name_fa: 'وضعیت تقصیر',
name_en: 'blameStatus',
label_fa: 'وضعیت تقصیر',
label_en: 'Blame Status',
placeholder_fa: 'وضعیت تقصیر را انتخاب کنید',
placeholder_en: 'Select blame status',
value: [
{ label_fa: 'موافق', label_en: 'Agreed', value: 'AGREED' },
{ label_fa: 'مخالف', label_en: 'Disagreement', value: 'DISAGREEMENT' }
],
dataType: 'select',
required: true,
order: 1,
isActive: true,
validation: {
enum: ['AGREED', 'DISAGREEMENT']
}
},
{
id: '507f1f77bcf86cd799439013' as any,
name_fa: 'اعتراف',
name_en: 'confess',
label_fa: 'اعتراف',
label_en: 'Confession',
placeholder_fa: 'نوع اعتراف خود را انتخاب کنید',
placeholder_en: 'Select your confession type',
value: [
{ label_fa: 'مقصر', label_en: 'Guilty', value: 'guilty' },
{ label_fa: 'آسیب دیده', label_en: 'Damaged', value: 'damaged' }
],
dataType: 'select',
required: true,
order: 2,
isActive: true,
validation: {
enum: ['guilty', 'damaged']
}
}
],
metadata: {
icon: 'balance-scale',
color: '#F59E0B',
requiresAuth: true
}
},
{
stepKey: WorkflowStep.FIRST_VIDEO,
type: 'THIRD_PARTY',
stepNumber: 3,
isActive: true,
stepName_fa: 'ضبط ویدیو طرف اول',
stepName_en: 'First Party Video Capture',
description_fa: 'ضبط ویدیو مستقیم از دوربین توسط طرف اول (گالری مجاز نیست)',
description_en: 'Live video capture from camera by first party (gallery not allowed)',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_BLAME_CONFESSION],
nextPossibleSteps: [WorkflowStep.FIRST_INITIAL_FORM],
allowedRoles: ['user'],
estimatedDuration: 3,
fields: [
{
id: '507f1f77bcf86cd799439014' as any,
name_fa: 'فایل ویدیو',
name_en: 'file',
label_fa: 'ضبط ویدیو',
label_en: 'Video Recording',
placeholder_fa: 'ویدیو خود را از دوربین ضبط کنید',
placeholder_en: 'Record your video from camera',
value: null,
dataType: 'video-capture',
required: true,
order: 1,
isActive: true,
validation: {
maxSize: 52428800,
maxDuration: 180,
allowedTypes: ['video/mp4', 'video/webm'],
captureMode: 'camera-only',
allowGallery: false
}
}
],
metadata: {
icon: 'video-camera',
color: '#EF4444',
captureType: 'live-camera',
allowGallerySelection: false,
requiresCamera: true
}
},
{
stepKey: WorkflowStep.FIRST_INITIAL_FORM,
type: 'THIRD_PARTY',
stepNumber: 4,
isActive: true,
stepName_fa: 'فرم اصلی اطلاعات راننده و بیمه‌گذار',
stepName_en: 'Main Form - Driver & Insurer Information',
description_fa: 'وارد کردن اطلاعات کامل راننده، بیمه‌گذار و خودرو',
description_en: 'Enter complete driver, insurer and vehicle information',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_VIDEO],
nextPossibleSteps: [WorkflowStep.FIRST_LOCATION],
allowedRoles: ['user'],
estimatedDuration: 5,
fields: [
{
id: '507f1f77bcf86cd799439015' as any,
name_fa: 'کد ملی مالک',
name_en: 'nationalCodeOfOwner',
label_fa: 'کد ملی مالک',
label_en: 'National Code of Owner',
placeholder_fa: 'کد ملی ۱۰ رقمی مالک را وارد کنید',
placeholder_en: 'Enter 10-digit national code of insurer',
value: null,
dataType: 'string',
required: true,
order: 1,
isActive: true,
validation: {
pattern: '^[0-9]{10}$',
minLength: 10,
maxLength: 10
}
},
{
id: '507f1f77bcf86cd799439016' as any,
name_fa: 'کد ملی راننده',
name_en: 'nationalCodeOfDriver',
label_fa: 'کد ملی راننده',
label_en: 'National Code of Driver',
placeholder_fa: 'کد ملی ۱۰ رقمی راننده را وارد کنید',
placeholder_en: 'Enter 10-digit national code of driver',
value: null,
dataType: 'string',
required: true,
order: 2,
isActive: true,
validation: {
pattern: '^[0-9]{10}$',
minLength: 10,
maxLength: 10
}
},
{
id: '507f1f77bcf86cd799439017' as any,
name_fa: 'شماره گواهینامه بیمه‌گذار',
name_en: 'insurerLicense',
label_fa: 'شماره گواهینامه بیمه‌گذار',
label_en: 'Insurer License Number',
placeholder_fa: 'شماره گواهینامه بیمه‌گذار را وارد کنید',
placeholder_en: 'Enter insurer license number',
value: null,
dataType: 'string',
required: true,
order: 3,
isActive: true,
validation: {
minLength: 5,
maxLength: 20
}
},
{
id: '507f1f77bcf86cd799439018' as any,
name_fa: 'شماره گواهینامه راننده',
name_en: 'driverLicense',
label_fa: 'شماره گواهینامه راننده',
label_en: 'Driver License Number',
placeholder_fa: 'شماره گواهینامه راننده را وارد کنید',
placeholder_en: 'Enter driver license number',
value: null,
dataType: 'string',
required: true,
order: 4,
isActive: true,
validation: {
minLength: 5,
maxLength: 20
}
},
{
id: '507f1f77bcf86cd799439019' as any,
name_fa: 'پلاک خودرو',
name_en: 'plate',
label_fa: 'پلاک خودرو',
label_en: 'Vehicle Plate',
placeholder_fa: 'اطلاعات پلاک خودرو را وارد کنید',
placeholder_en: 'Enter vehicle plate information',
value: null,
dataType: 'object',
required: true,
order: 5,
isActive: true,
validation: {
type: 'object',
properties: {
leftDigits: { type: 'number', min: 10, max: 99 },
centerAlphabet: { type: 'string', pattern: '^[آ-ی]{1}$' },
centerDigits: { type: 'number', min: 100, max: 999 },
ir: { type: 'number', min: 10, max: 99 }
}
}
},
{
id: '507f1f77bcf86cd79943901a' as any,
name_fa: 'راننده همان بیمه‌گذار است',
name_en: 'driverIsInsurer',
label_fa: 'راننده همان بیمه‌گذار است',
label_en: 'Driver is Insurer',
placeholder_fa: '',
placeholder_en: '',
value: null,
dataType: 'boolean',
required: true,
order: 6,
isActive: true,
validation: {
type: 'boolean'
}
},
{
id: '507f1f77bcf86cd79943901b' as any,
name_fa: 'خودرو صفر است',
name_en: 'isNewCar',
label_fa: 'خودرو صفر است',
label_en: 'Is New Car',
placeholder_fa: '',
placeholder_en: '',
value: null,
dataType: 'boolean',
required: true,
order: 7,
isActive: true,
validation: {
type: 'boolean'
}
},
{
id: '507f1f77bcf86cd79943901c' as any,
name_fa: 'بدون گواهی',
name_en: 'userNoCertificate',
label_fa: 'کاربر گواهی ندارد',
label_en: 'User Has No Certificate',
placeholder_fa: '',
placeholder_en: '',
value: null,
dataType: 'boolean',
required: true,
order: 8,
isActive: true,
validation: {
type: 'boolean'
}
},
{
id: '507f1f77bcf86cd79943901d' as any,
name_fa: 'تاریخ تولد بیمه‌گذار',
name_en: 'insurerBirthday',
label_fa: 'تاریخ تولد بیمه‌گذار',
label_en: 'Insurer Birthday',
placeholder_fa: 'تاریخ تولد بیمه‌گذار (timestamp)',
placeholder_en: 'Insurer birthday (timestamp)',
value: null,
dataType: 'number',
required: true,
order: 9,
isActive: true,
validation: {
type: 'number',
min: 0
}
},
{
id: '507f1f77bcf86cd79943901e' as any,
name_fa: 'تاریخ تولد راننده',
name_en: 'driverBirthday',
label_fa: 'تاریخ تولد راننده',
label_en: 'Driver Birthday',
placeholder_fa: 'تاریخ تولد راننده را وارد کنید',
placeholder_en: 'Enter driver birthday',
value: null,
dataType: 'string',
required: true,
order: 10,
isActive: true,
validation: {
format: 'date'
}
}
],
metadata: {
icon: 'clipboard-list',
color: '#8B5CF6',
formType: 'detailed',
hasComplexValidation: true
}
},
{
stepKey: WorkflowStep.FIRST_LOCATION,
type: 'THIRD_PARTY',
stepNumber: 5,
isActive: true,
stepName_fa: 'موقعیت مکانی حادثه',
stepName_en: 'Accident Location',
description_fa: 'ثبت موقعیت جغرافیایی محل وقوع حادثه',
description_en: 'Record geographical location of accident scene',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_INITIAL_FORM],
nextPossibleSteps: [WorkflowStep.FIRST_DESCRIPTION],
allowedRoles: ['user'],
estimatedDuration: 2,
fields: [
{
id: '507f1f77bcf86cd79943901f' as any,
name_fa: 'عرض جغرافیایی',
name_en: 'lat',
label_fa: 'عرض جغرافیایی',
label_en: 'Latitude',
placeholder_fa: 'عرض جغرافیایی را وارد کنید',
placeholder_en: 'Enter latitude',
value: null,
dataType: 'number',
required: true,
order: 1,
isActive: true,
validation: {
type: 'number',
min: -90,
max: 90
}
},
{
id: '507f1f77bcf86cd799439020' as any,
name_fa: 'طول جغرافیایی',
name_en: 'lon',
label_fa: 'طول جغرافیایی',
label_en: 'Longitude',
placeholder_fa: 'طول جغرافیایی را وارد کنید',
placeholder_en: 'Enter longitude',
value: null,
dataType: 'number',
required: true,
order: 2,
isActive: true,
validation: {
type: 'number',
min: -180,
max: 180
}
}
],
metadata: {
icon: 'map-pin',
color: '#10B981',
requiresGPS: true,
autoDetectLocation: true
}
},
{
stepKey: WorkflowStep.FIRST_VOICE,
type: 'THIRD_PARTY',
stepNumber: 6,
isActive: true,
stepName_fa: 'ضبط صدای طرف اول',
stepName_en: 'First Party Voice Recording',
description_fa: 'ضبط توضیحات صوتی توسط طرف اول',
description_en: 'Voice recording of explanations by first party',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_LOCATION],
nextPossibleSteps: [WorkflowStep.FIRST_DESCRIPTION],
allowedRoles: ['user'],
estimatedDuration: 3,
fields: [
{
id: '507f1f77bcf86cd799439021' as any,
name_fa: 'فایل صوتی',
name_en: 'file',
label_fa: 'ضبط صدا',
label_en: 'Voice Recording',
placeholder_fa: 'توضیحات صوتی خود را ضبط کنید',
placeholder_en: 'Record your voice explanation',
value: null,
dataType: 'audio-file',
required: true,
order: 1,
isActive: true,
validation: {
maxSize: 10485760,
maxDuration: 300,
allowedTypes: ['audio/mp3', 'audio/mpeg', 'audio/wav', 'audio/m4a', 'audio/webm']
}
}
],
metadata: {
icon: 'microphone',
color: '#F59E0B',
recordingType: 'voice',
requiresMicrophone: true
}
},
{
stepKey: WorkflowStep.FIRST_DESCRIPTION,
type: 'THIRD_PARTY',
stepNumber: 7,
isActive: true,
stepName_fa: 'توضیحات طرف اول',
stepName_en: 'First Party Description',
description_fa: 'ثبت توضیحات و جزئیات حادثه توسط طرف اول',
description_en: 'Record accident description and details by first party',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_VOICE],
nextPossibleSteps: [WorkflowStep.FIRST_INVITE_SECOND],
allowedRoles: ['user'],
estimatedDuration: 4,
fields: [
{
id: '507f1f77bcf86cd799439022' as any,
name_fa: 'توضیحات',
name_en: 'desc',
label_fa: 'توضیحات حادثه',
label_en: 'Accident Description',
placeholder_fa: 'توضیحات کامل حادثه را وارد کنید',
placeholder_en: 'Enter complete accident description',
value: null,
dataType: 'textarea',
required: true,
order: 1,
isActive: true,
validation: {
minLength: 10,
maxLength: 2000
}
},
{
id: '507f1f77bcf86cd799439023' as any,
name_fa: 'تاریخ حادثه',
name_en: 'accidentDate',
label_fa: 'تاریخ حادثه',
label_en: 'Accident Date',
placeholder_fa: 'تاریخ وقوع حادثه را وارد کنید',
placeholder_en: 'Enter accident date',
value: null,
dataType: 'date',
required: true,
order: 2,
isActive: true,
validation: {
format: 'YYYY-MM-DD'
}
},
{
id: '507f1f77bcf86cd799439024' as any,
name_fa: 'زمان حادثه',
name_en: 'accidentTime',
label_fa: 'زمان حادثه',
label_en: 'Accident Time',
placeholder_fa: 'زمان وقوع حادثه را وارد کنید',
placeholder_en: 'Enter accident time',
value: null,
dataType: 'time',
required: true,
order: 3,
isActive: true,
validation: {
format: 'HH:mm'
}
}
],
metadata: {
icon: 'file-text',
color: '#6366F1'
}
},
{
stepKey: WorkflowStep.FIRST_INVITE_SECOND,
type: 'THIRD_PARTY',
stepNumber: 8,
isActive: true,
stepName_fa: 'دعوت طرف دوم',
stepName_en: 'Invite Second Party',
description_fa: 'ارسال دعوتنامه به طرف دوم برای ثبت اطلاعات',
description_en: 'Send invitation to second party to register information',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_DESCRIPTION],
nextPossibleSteps: [WorkflowStep.SECOND_INITIAL_FORM],
allowedRoles: ['user'],
estimatedDuration: 1,
fields: [],
metadata: {
icon: 'user-plus',
color: '#8B5CF6',
requiresPhoneNumber: true,
actionType: 'invitation'
}
},
{
stepKey: WorkflowStep.SECOND_INITIAL_FORM,
type: 'THIRD_PARTY',
stepNumber: 9,
isActive: true,
stepName_fa: 'فرم اصلی اطلاعات طرف دوم',
stepName_en: 'Second Party Main Form',
description_fa: 'وارد کردن اطلاعات کامل راننده، بیمه‌گذار و خودرو طرف دوم',
description_en: 'Enter complete driver, insurer and vehicle information for second party',
category: 'SECOND_PARTY',
requiredPreviousSteps: [WorkflowStep.FIRST_INVITE_SECOND],
nextPossibleSteps: [WorkflowStep.SECOND_LOCATION],
allowedRoles: ['user'],
estimatedDuration: 5,
fields: [
{
id: '507f1f77bcf86cd799439025' as any,
name_fa: 'کد ملی مالک',
name_en: 'nationalCodeOfOwner',
label_fa: 'کد ملی مالک',
label_en: 'National Code of Owner',
placeholder_fa: 'کد ملی ۱۰ رقمی مالک را وارد کنید',
placeholder_en: 'Enter 10-digit national code of insurer',
value: null,
dataType: 'string',
required: true,
order: 1,
isActive: true,
validation: {
pattern: '^[0-9]{10}$',
minLength: 10,
maxLength: 10
}
},
{
id: '507f1f77bcf86cd799439026' as any,
name_fa: 'کد ملی راننده',
name_en: 'nationalCodeOfDriver',
label_fa: 'کد ملی راننده',
label_en: 'National Code of Driver',
placeholder_fa: 'کد ملی ۱۰ رقمی راننده را وارد کنید',
placeholder_en: 'Enter 10-digit national code of driver',
value: null,
dataType: 'string',
required: true,
order: 2,
isActive: true,
validation: {
pattern: '^[0-9]{10}$',
minLength: 10,
maxLength: 10
}
},
{
id: '507f1f77bcf86cd799439027' as any,
name_fa: 'شماره گواهینامه بیمه‌گذار',
name_en: 'insurerLicense',
label_fa: 'شماره گواهینامه بیمه‌گذار',
label_en: 'Insurer License Number',
placeholder_fa: 'شماره گواهینامه بیمه‌گذار را وارد کنید',
placeholder_en: 'Enter insurer license number',
value: null,
dataType: 'string',
required: true,
order: 3,
isActive: true,
validation: {
minLength: 5,
maxLength: 20
}
},
{
id: '507f1f77bcf86cd799439028' as any,
name_fa: 'شماره گواهینامه راننده',
name_en: 'driverLicense',
label_fa: 'شماره گواهینامه راننده',
label_en: 'Driver License Number',
placeholder_fa: 'شماره گواهینامه راننده را وارد کنید',
placeholder_en: 'Enter driver license number',
value: null,
dataType: 'string',
required: true,
order: 4,
isActive: true,
validation: {
minLength: 5,
maxLength: 20
}
},
{
id: '507f1f77bcf86cd799439029' as any,
name_fa: 'پلاک خودرو',
name_en: 'plate',
label_fa: 'پلاک خودرو',
label_en: 'Vehicle Plate',
placeholder_fa: 'اطلاعات پلاک خودرو را وارد کنید',
placeholder_en: 'Enter vehicle plate information',
value: null,
dataType: 'object',
required: true,
order: 5,
isActive: true,
validation: {
type: 'object',
properties: {
leftDigits: { type: 'number', min: 10, max: 99 },
centerAlphabet: { type: 'string', pattern: '^[آ-ی]{1}$' },
centerDigits: { type: 'number', min: 100, max: 999 },
ir: { type: 'number', min: 10, max: 99 }
}
}
},
{
id: '507f1f77bcf86cd79943902a' as any,
name_fa: 'راننده همان بیمه‌گذار است',
name_en: 'driverIsInsurer',
label_fa: 'راننده همان بیمه‌گذار است',
label_en: 'Driver is Insurer',
value: null,
dataType: 'boolean',
required: true,
order: 6,
isActive: true
},
{
id: '507f1f77bcf86cd79943902b' as any,
name_fa: 'خودرو صفر است',
name_en: 'isNewCar',
label_fa: 'خودرو صفر است',
label_en: 'Is New Car',
value: null,
dataType: 'boolean',
required: true,
order: 7,
isActive: true
},
{
id: '507f1f77bcf86cd79943902c' as any,
name_fa: 'بدون گواهی',
name_en: 'userNoCertificate',
label_fa: 'کاربر گواهی ندارد',
label_en: 'User Has No Certificate',
value: null,
dataType: 'boolean',
required: true,
order: 8,
isActive: true
},
{
id: '507f1f77bcf86cd79943902d' as any,
name_fa: 'تاریخ تولد بیمه‌گذار',
name_en: 'insurerBirthday',
label_fa: 'تاریخ تولد بیمه‌گذار',
label_en: 'Insurer Birthday',
placeholder_fa: 'تاریخ تولد بیمه‌گذار (timestamp)',
placeholder_en: 'Insurer birthday (timestamp)',
value: null,
dataType: 'number',
required: true,
order: 9,
isActive: true,
validation: {
type: 'number',
min: 0
}
},
{
id: '507f1f77bcf86cd79943902e' as any,
name_fa: 'تاریخ تولد راننده',
name_en: 'driverBirthday',
label_fa: 'تاریخ تولد راننده',
label_en: 'Driver Birthday',
placeholder_fa: 'تاریخ تولد راننده را وارد کنید',
placeholder_en: 'Enter driver birthday',
value: null,
dataType: 'string',
required: true,
order: 10,
isActive: true,
validation: {
format: 'date'
}
}
],
metadata: {
icon: 'clipboard-list',
color: '#8B5CF6',
formType: 'detailed',
hasComplexValidation: true,
party: 'second'
}
},
{
stepKey: WorkflowStep.SECOND_LOCATION,
type: 'THIRD_PARTY',
stepNumber: 10,
isActive: true,
stepName_fa: 'موقعیت مکانی طرف دوم',
stepName_en: 'Second Party Location',
description_fa: 'ثبت موقعیت جغرافیایی طرف دوم',
description_en: 'Record second party geographical location',
category: 'SECOND_PARTY',
requiredPreviousSteps: [WorkflowStep.SECOND_INITIAL_FORM],
nextPossibleSteps: [WorkflowStep.SECOND_VOICE],
allowedRoles: ['user'],
estimatedDuration: 2,
fields: [
{
id: '507f1f77bcf86cd79943902f' as any,
name_fa: 'عرض جغرافیایی',
name_en: 'lat',
label_fa: 'عرض جغرافیایی',
label_en: 'Latitude',
placeholder_fa: 'عرض جغرافیایی را وارد کنید',
placeholder_en: 'Enter latitude',
value: null,
dataType: 'number',
required: true,
order: 1,
isActive: true,
validation: {
type: 'number',
min: -90,
max: 90
}
},
{
id: '507f1f77bcf86cd799439030' as any,
name_fa: 'طول جغرافیایی',
name_en: 'lon',
label_fa: 'طول جغرافیایی',
label_en: 'Longitude',
placeholder_fa: 'طول جغرافیایی را وارد کنید',
placeholder_en: 'Enter longitude',
value: null,
dataType: 'number',
required: true,
order: 2,
isActive: true,
validation: {
type: 'number',
min: -180,
max: 180
}
}
],
metadata: {
icon: 'map-pin',
color: '#10B981',
requiresGPS: true,
autoDetectLocation: true,
party: 'second'
}
},
{
stepKey: WorkflowStep.SECOND_VOICE,
type: 'THIRD_PARTY',
stepNumber: 11,
isActive: true,
stepName_fa: 'ضبط صدای طرف دوم',
stepName_en: 'Second Party Voice Recording',
description_fa: 'ضبط توضیحات صوتی توسط طرف دوم',
description_en: 'Voice recording of explanations by second party',
category: 'SECOND_PARTY',
requiredPreviousSteps: [WorkflowStep.SECOND_LOCATION],
nextPossibleSteps: [WorkflowStep.SECOND_DESCRIPTION],
allowedRoles: ['user'],
estimatedDuration: 3,
fields: [
{
id: '507f1f77bcf86cd799439031' as any,
name_fa: 'فایل صوتی',
name_en: 'file',
label_fa: 'ضبط صدا',
label_en: 'Voice Recording',
placeholder_fa: 'توضیحات صوتی خود را ضبط کنید',
placeholder_en: 'Record your voice explanation',
value: null,
dataType: 'audio-file',
required: true,
order: 1,
isActive: true,
validation: {
maxSize: 10485760,
maxDuration: 300,
allowedTypes: ['audio/mp3', 'audio/mpeg', 'audio/wav', 'audio/m4a', 'audio/webm']
}
}
],
metadata: {
icon: 'microphone',
color: '#F59E0B',
recordingType: 'voice',
requiresMicrophone: true,
party: 'second'
}
},
{
stepKey: WorkflowStep.SECOND_DESCRIPTION,
type: 'THIRD_PARTY',
stepNumber: 12,
isActive: true,
stepName_fa: 'توضیحات طرف دوم',
stepName_en: 'Second Party Description',
description_fa: 'ثبت توضیحات و جزئیات حادثه توسط طرف دوم',
description_en: 'Record accident description and details by second party',
category: 'SECOND_PARTY',
requiredPreviousSteps: [WorkflowStep.SECOND_VOICE],
nextPossibleSteps: [WorkflowStep.WAITING_FOR_GUILT_DECISION],
allowedRoles: ['user'],
estimatedDuration: 4,
fields: [
{
id: '507f1f77bcf86cd799439032' as any,
name_fa: 'توضیحات',
name_en: 'desc',
label_fa: 'توضیحات حادثه',
label_en: 'Accident Description',
placeholder_fa: 'توضیحات کامل حادثه را وارد کنید',
placeholder_en: 'Enter complete accident description',
value: null,
dataType: 'textarea',
required: true,
order: 1,
isActive: true,
validation: {
minLength: 10,
maxLength: 2000
}
},
{
id: '507f1f77bcf86cd799439033' as any,
name_fa: 'تاریخ حادثه',
name_en: 'accidentDate',
label_fa: 'تاریخ حادثه',
label_en: 'Accident Date',
placeholder_fa: 'تاریخ وقوع حادثه را وارد کنید',
placeholder_en: 'Enter accident date',
value: null,
dataType: 'date',
required: true,
order: 2,
isActive: true,
validation: {
format: 'YYYY-MM-DD'
}
},
{
id: '507f1f77bcf86cd799439034' as any,
name_fa: 'زمان حادثه',
name_en: 'accidentTime',
label_fa: 'زمان حادثه',
label_en: 'Accident Time',
placeholder_fa: 'زمان وقوع حادثه را وارد کنید',
placeholder_en: 'Enter accident time',
value: null,
dataType: 'time',
required: true,
order: 3,
isActive: true,
validation: {
format: 'HH:mm'
}
}
],
metadata: {
icon: 'file-text',
color: '#6366F1',
party: 'second'
}
},
// CAR_BODY workflow: accident type form (replaces confession for CAR_BODY)
{
stepKey: WorkflowStep.CAR_BODY_ACCIDENT_TYPE,
type: 'CAR_BODY',
stepNumber: 2,
isActive: true,
stepName_fa: 'نوع حادثه بدنه',
stepName_en: 'Car Body Accident Type',
description_fa: 'انتخاب اینکه حادثه با خودرو بوده یا با شیء',
description_en: 'Select whether accident was with another car or with an object',
category: 'FIRST_PARTY',
requiredPreviousSteps: [WorkflowStep.CREATED],
nextPossibleSteps: [WorkflowStep.FIRST_VIDEO],
allowedRoles: ['user'],
estimatedDuration: 1,
fields: [
{
id: '507f1f77bcf86cd7994390cb' as any,
name_fa: 'نوع حادثه',
name_en: 'accidentType',
label_fa: 'نوع حادثه',
label_en: 'Accident Type',
placeholder_fa: 'حادثه با خودرو یا شیء؟',
placeholder_en: 'Accident with car or object?',
value: [
{ label_fa: 'با خودرو', label_en: 'With Car', value: 'car' },
{ label_fa: 'با شیء', label_en: 'With Object', value: 'object' }
],
dataType: 'select',
required: true,
order: 1,
isActive: true,
validation: { enum: ['car', 'object'] }
}
],
metadata: { icon: 'car', color: '#10B981' }
},
// Add more default steps as needed...
];
return await this.bulkUpsert(defaultSteps);
}
}