forked from Yara724/api
478 lines
10 KiB
TypeScript
478 lines
10 KiB
TypeScript
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
export enum CreationMethod {
|
||
NORMAL = "NORMAL",
|
||
LINK = "LINK",
|
||
IN_PERSON = "IN_PERSON",
|
||
}
|
||
|
||
export enum FilledBy {
|
||
CUSTOMER = "customer",
|
||
EXPERT = "expert",
|
||
}
|
||
|
||
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;
|
||
|
||
// 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,
|
||
);
|