blame and claim refactored

This commit is contained in:
2026-02-24 12:19:25 +03:30
parent 0d8858f458
commit 35732dd70a
81 changed files with 11603 additions and 77 deletions

View File

@@ -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;
}
}

View File

@@ -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