Files
yara724api/src/request-management/entities/schema/request-management.schema.ts
2026-04-20 18:19:25 +03:30

495 lines
11 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 { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose";
import {
ResendFirstPartyDto,
ResendSecondPartyDto,
} from "src/expert-blame/dto/reply.dto";
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum";
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
export class InitialForm {
@Prop({ required: false, default: false })
expertOpinion?: boolean;
@Prop({ required: false, default: false })
imDamaged?: boolean;
@Prop({ required: false, default: false })
imGuilty?: boolean;
}
export class FirstPartyFile {
@Prop()
descriptions?: string[];
@Prop()
voices?: string[];
@Prop()
firstPartyVideoId?: string;
// CAR_BODY specific accident details
@Prop({ type: Date })
accidentDate?: Date;
@Prop()
accidentTime?: string;
@Prop()
weatherCondition?: string;
@Prop()
roadCondition?: string;
@Prop()
lightCondition?: string;
}
export class CarDetail {
carName?: string;
carModel?: string;
carType?: string;
isNewCar?: boolean;
insurerBirthday?: string;
driverBirthday?: string | null;
}
export class InsuranceDetail {
insuranceId?: string;
insuranceCompany?: string;
financialCommitmentCeiling?: string;
startDate?: string;
endDate?: string;
}
// CAR_BODY specific insurance details for the damaged party
export class CarBodyInsuranceDetail {
@Prop()
policyNumber?: string;
@Prop()
startDate?: string;
@Prop()
endDate?: string;
@Prop()
insurerCompany?: string;
@Prop({ type: [String] })
coverages?: string[];
}
export class SecondPartyFile {
@Prop()
descriptions?: string[];
@Prop()
voices?: string[];
@Prop()
secondPartyVideoId?: string;
}
export class FirstPartyClient {
clientName?: string;
clientId?: Types.ObjectId;
}
export class SecondPartyClient {
clientName?: string;
clientId?: Types.ObjectId;
}
export class FirstPartyDetail {
@Prop({ type: String })
firstPartyId?: Types.ObjectId;
@Prop({ type: String })
firstPartyPhoneNumber?: string;
@Prop({ type: String })
firstPartyClientId?: string;
@Prop({
type: AddPlateDto,
description: "first add plate and send plate id to this field",
})
firstPartyPlate?: AddPlateDto;
@Prop({ type: CarDetail })
firstPartyCarDetail?: CarDetail;
@Prop({ type: InsuranceDetail })
firstPartyInsuranceDetail?: InsuranceDetail;
// Only used for CAR_BODY type requests car body insurance info
@Prop({ type: CarBodyInsuranceDetail })
firstPartyCarBodyInsuranceDetail?: CarBodyInsuranceDetail;
@Prop({ type: FirstPartyFile })
firstPartyClient?: FirstPartyClient;
@Prop({ type: FirstPartyFile })
firstPartyFile?: FirstPartyFile;
@Prop({ type: FirstPartyFile })
firstPartyInitialForm?: InitialForm;
@Prop({ type: Boolean })
firstPartyCertificate: boolean | null;
}
export class SecondPartyDetail {
@Prop({ type: String })
secondPartyId?: Types.ObjectId;
@Prop({ type: String })
secondPartyPhoneNumber?: string;
@Prop({ type: SecondPartyClient })
secondPartyClient?: SecondPartyClient;
@Prop({
type: AddPlateDto,
description: "secondParty add plate and send plate id to this field",
})
secondPartyPlate?: AddPlateDto;
@Prop()
secondPartyCarDetail?: CarDetail;
@Prop({ type: InsuranceDetail })
secondPartyInsuranceDetail?: InsuranceDetail;
@Prop()
secondPartyPlateId?: string;
@Prop({ type: InitialForm })
secondPartyInitialForm: InitialForm;
@Prop({ type: SecondPartyFile })
secondPartyFiles?: SecondPartyFile;
}
export class LocationDto {
@Prop({ type: Number, required: true })
lat: number;
@Prop({ type: Number, required: true })
lon: number;
}
export class AccidentWayIF {
@Prop({ required: true })
id: string;
@Prop({ required: true })
label: string;
}
export class AccidentReasonIF {
@Prop({ required: true })
id: string;
@Prop({ required: true })
label: string;
@Prop({ required: true })
fanavaran: number;
}
export class FieldsInterface {
@Prop({ required: true, type: AccidentWayIF })
accidentWay: AccidentWayIF;
@Prop({ required: true, type: AccidentReasonIF })
accidentReason: AccidentReasonIF;
@Prop({ required: true, type: AccidentWayIF })
accidentType: AccidentWayIF;
}
export class SignDetail {
@Prop()
fileId: string;
@Prop()
fileName: string;
@Prop()
fileUrl: string;
}
export class PartiesComment {
@Prop({ required: true, type: Boolean })
isAccept?: boolean;
@Prop({ required: true })
signDetail?: SignDetail;
}
export class SubmitReply {
@Prop({ required: true })
description: string;
@Prop({ required: true, type: String })
guiltyUserId: Types.ObjectId;
@Prop({ required: true })
fields: FieldsInterface;
@Prop({ required: true, default: () => new Date() })
submitTime: Date;
@Prop({ type: PartiesComment })
firstPartyComment?: PartiesComment;
@Prop({ type: PartiesComment })
secondPartyComment?: PartiesComment;
@Prop()
systemResponse: string;
/** Field expert profile at reply time (`expert` collection). */
@Prop({ type: Object })
expertProfileSnapshot?: Record<string, string | undefined>;
}
export class ExpertResendParties {
userId?: Types.ObjectId;
description?: string;
}
export class ExpertResendReply {
// TODO change DTOs to dynamic Exmple SubmitReply Parties comment
@Prop()
firstParty?: ResendFirstPartyDto;
@Prop()
secondParty?: ResendSecondPartyDto;
}
export class ActorLockDetails {
@Prop({ type: String })
fullName?: string;
@Prop({ type: Types.ObjectId })
actorId?: Types.ObjectId;
/** Field expert profile when lock was taken (`expert` collection). */
@Prop({ type: Object })
expertProfileSnapshot?: Record<string, string | undefined>;
}
export enum CreationMethod {
NORMAL = "NORMAL",
LINK = "LINK",
IN_PERSON = "IN_PERSON",
}
export enum FilledBy {
CUSTOMER = "customer",
EXPERT = "expert",
REGISTRAR = "registrar",
}
export class ExpertLinkInfo {
@Prop({ type: String, required: true })
token: string;
@Prop({ type: Date, required: true })
expiresAt: Date;
@Prop({ type: Boolean, default: false })
isUsed: boolean;
@Prop({ type: Boolean, default: false })
isExpired: boolean;
@Prop({ type: Date })
generatedAt: Date;
@Prop({ type: String })
sentTo?: string; // Phone number or email
@Prop({ type: Number, default: 0 })
openCount: number;
@Prop({ type: Date })
lastOpenedAt?: Date;
@Prop({ type: Date })
usedAt?: Date;
}
export class FileHistoryEvent {
@Prop({ type: String, required: true })
eventType: string; // "LINK_SENT", "LINK_OPENED", "FILLED_BY_CUSTOMER", "FILLED_BY_EXPERT", "ASSIGNED_TO_EXPERT", "EVALUATION_STARTED", "EVALUATION_COMPLETED", "USER_ACCEPTED", "USER_REJECTED", "SENT_TO_FANAVARAN", "CODE_RECEIVED"
@Prop({ type: Types.ObjectId })
actorId?: Types.ObjectId;
@Prop({ type: String })
actorName?: string;
@Prop({ type: String })
actorType?: string; // "expert", "damage_expert", "customer"
@Prop({ type: Date, default: () => new Date() })
timestamp: Date;
@Prop({ type: Object })
metadata?: any; // Additional event-specific data
}
@Schema({ _id: false })
export class FileRating {
@Prop({ type: Number, min: 0, max: 5 })
collisionMethodAccuracy: number;
@Prop({ type: Number, min: 0, max: 5 })
evaluationTimeliness: number;
@Prop({ type: Number, min: 0, max: 5 })
accidentCauseAccuracy: number;
@Prop({ type: Number, min: 0, max: 5 })
guiltyVehicleIdentification: number;
@Prop({ type: Number, min: 0, max: 5 })
botRating: number;
}
// TODO clean all sub Object to other files
export type RequestManagementDocs = RequestManagementModel & Document;
@Schema({
collection: "requests-management",
versionKey: false,
timestamps: true,
autoIndex: true,
_id: true,
})
export class RequestManagementModel {
@Prop({ type: String, required: false })
type: string;
@Prop({ type: String, required: true, index: { unique: true } })
requestNumber: string;
/**
* Human-friendly shared id across blame+claim (e.g. A14235).
* Generated once at creation time and reused by claim.
*
* NOTE: `sparse` keeps existing documents without this field valid.
*/
@Prop({ type: String, unique: true, index: true, sparse: true, trim: true })
publicId?: string;
@Prop()
blameStatus: ReqBlameStatus | StepsEnum;
@Prop()
steps?: StepsEnum[];
@Prop()
userComment?: string;
@Prop()
currentStep: StepsEnum;
@Prop()
nextStep?: StepsEnum;
@Prop({ description: "first step" })
initialForm?: InitialForm;
@Prop({ required: false })
firstPartyDetails?: FirstPartyDetail;
@Prop({ required: false, type: Types.ObjectId })
sanHubId?: Types.ObjectId;
@Prop({ required: false })
secondPartyDetails?: SecondPartyDetail;
@Prop({ required: false, description: "third step call" })
firstPartyLocation?: LocationDto;
@Prop({ required: false })
secondPartyLocation?: LocationDto;
@Prop({ type: Date, default: Date.now })
createdAt?: Date | string;
@Prop({ type: Date, default: Date.now })
updatedAt?: Date;
@Prop()
lockFile?: boolean;
@Prop({ type: Date })
lockTime?: Date | string | number;
@Prop({ type: Date })
unlockTime?: Date | number;
@Prop()
actorLocked?: ActorLockDetails;
@Prop()
actorsChecker?: [];
@Prop({ required: false })
expertSubmitReply?: SubmitReply | null;
@Prop()
expertResendReply?: ExpertResendReply;
@Prop()
expertSubmitReplyFinal?: ExpertResendReply;
@Prop({ type: FileRating, required: false })
rating?: FileRating;
@Prop({ type: Date, required: false })
autoCloseTriggerTime?: Date;
@Prop({ type: Boolean, default: false })
isHandledStatsUpdated?: boolean;
/** Latest field expert profile when expert requested party resend (V1 `sendAgainRequest`). */
@Prop({ type: Object })
expertResendByExpertProfileSnapshot?: Record<string, string | undefined>;
/** Field expert profile when expert marked in-person visit (V1 `inPersonVisit`). */
@Prop({ type: Object })
fieldExpertInPersonVisitProfileSnapshot?: Record<string, string | undefined>;
// Expert-initiated file tracking
@Prop({ type: Boolean, default: false })
expertInitiated?: boolean;
@Prop({ type: Types.ObjectId })
initiatedBy?: Types.ObjectId; // Expert who started the process
@Prop({ type: String, enum: FilledBy })
filledBy?: FilledBy; // Who filled the information
@Prop({ type: String, enum: CreationMethod, default: CreationMethod.NORMAL })
creationMethod?: CreationMethod; // How the file was created
@Prop({ type: ExpertLinkInfo })
expertLink?: ExpertLinkInfo; // Link information if created via link
@Prop({ type: [FileHistoryEvent], default: [] })
history?: FileHistoryEvent[]; // Timeline of events
@Prop({ type: Types.ObjectId })
assignedExpertId?: Types.ObjectId; // Expert assigned for evaluation (should be same as initiatedBy for expert-initiated files)
}
export const RequestManagementSchema = SchemaFactory.createForClass(
RequestManagementModel,
);