forked from Yara724/api
Initial commit after migration to gitea
This commit is contained in:
211
src/request-management/dto/create-request-management.dto.ts
Normal file
211
src/request-management/dto/create-request-management.dto.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { Types } from "mongoose";
|
||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
|
||||
import {
|
||||
WeatherCondition,
|
||||
RoadCondition,
|
||||
LightCondition,
|
||||
} from "src/Types&Enums/blame-request-management/accident-conditions.enum";
|
||||
|
||||
export class InitialFormDto {
|
||||
@ApiProperty({ required: false, default: false })
|
||||
expertOpinion?: boolean;
|
||||
|
||||
@ApiProperty({ required: false, default: false })
|
||||
imDamaged?: boolean;
|
||||
|
||||
@ApiProperty({ required: true, default: false })
|
||||
imGuilty?: boolean;
|
||||
}
|
||||
|
||||
export class CarBodyFormDto {
|
||||
@ApiProperty({ required: false, default: false })
|
||||
car?: boolean;
|
||||
|
||||
@ApiProperty({ required: false, default: false })
|
||||
object?: boolean;
|
||||
}
|
||||
|
||||
export class CarBodySecondForm {
|
||||
@ApiProperty({ required: false, default: false })
|
||||
imDamaged?: boolean;
|
||||
|
||||
@ApiProperty({ required: true, default: false })
|
||||
imGuilty?: boolean;
|
||||
}
|
||||
|
||||
export class FirstPartyFileDto {
|
||||
@ApiProperty()
|
||||
descriptions?: string;
|
||||
|
||||
@ApiProperty()
|
||||
voices?: string;
|
||||
|
||||
@ApiPropertyOptional({ type: String })
|
||||
firstPartyVideoId?: string;
|
||||
}
|
||||
|
||||
export class DescriptionDto {
|
||||
@ApiProperty()
|
||||
desc: string;
|
||||
|
||||
// CAR_BODY specific fields
|
||||
@ApiPropertyOptional({
|
||||
description: "Date of accident (for CAR_BODY type only)",
|
||||
example: "2025-12-08"
|
||||
})
|
||||
accidentDate?: Date;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Time of accident (for CAR_BODY type only)",
|
||||
example: "14:30"
|
||||
})
|
||||
accidentTime?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: WeatherCondition,
|
||||
description: "Weather condition at time of accident (for CAR_BODY type only)",
|
||||
example: WeatherCondition.CLEAR
|
||||
})
|
||||
weatherCondition?: WeatherCondition;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: RoadCondition,
|
||||
description: "Road condition at time of accident (for CAR_BODY type only)",
|
||||
example: RoadCondition.DRY
|
||||
})
|
||||
roadCondition?: RoadCondition;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: LightCondition,
|
||||
description: "Light condition at time of accident (for CAR_BODY type only)",
|
||||
example: LightCondition.DAYLIGHT
|
||||
})
|
||||
lightCondition?: LightCondition;
|
||||
}
|
||||
|
||||
export class FirstPartyDetail {
|
||||
// @ApiProperty({ type: String })
|
||||
firstPartyId?: Types.ObjectId;
|
||||
// @ApiProperty({ type: String })
|
||||
firstPartyPhoneNumber?: string;
|
||||
// @ApiProperty({ type: String })
|
||||
firstPartyClientId?: string;
|
||||
@ApiProperty({
|
||||
type: AddPlateDto,
|
||||
description: "first add plate and send plate id to this field",
|
||||
})
|
||||
firstPartyPlate?: AddPlateDto;
|
||||
|
||||
@ApiProperty()
|
||||
firstPartyFile?: FirstPartyFileDto;
|
||||
}
|
||||
|
||||
export class SecondPartyDetail {
|
||||
@ApiProperty({ type: String })
|
||||
secondPartyId?: Types.ObjectId;
|
||||
|
||||
@ApiProperty({ type: String })
|
||||
secondPartyPhoneNumber?: string;
|
||||
|
||||
@ApiProperty({ type: String })
|
||||
secondPartyClientId?: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
description: "first add plate and send plate id to this field",
|
||||
})
|
||||
secondPartyPlateId?: Types.ObjectId;
|
||||
|
||||
@ApiProperty({ type: String })
|
||||
secondPartyVideoId?: string;
|
||||
}
|
||||
|
||||
export class LocationDto {
|
||||
@ApiProperty({ type: Number, required: true })
|
||||
lat: number;
|
||||
|
||||
@ApiProperty({ type: Number, required: true })
|
||||
lon: number;
|
||||
}
|
||||
|
||||
export class CreateRequestDto {
|
||||
@ApiProperty({ type: String, required: false })
|
||||
type?: "THIRD_PARTY" | "CAR_BODY";
|
||||
}
|
||||
|
||||
// export class DocsOfThisFile {
|
||||
// @ApiProperty()
|
||||
// firstPartyFile?: FirstPartyFileDto;
|
||||
// }
|
||||
export class CreateRequestManagementDto {
|
||||
@ApiPropertyOptional({ required: false, description: "first step" })
|
||||
initialForm?: InitialFormDto;
|
||||
|
||||
@ApiPropertyOptional({ required: false, description: "first step" })
|
||||
firstPartyDetails?: FirstPartyDetail;
|
||||
|
||||
// @ApiPropertyOptional({ required: false, description: "second step call" })
|
||||
// docsOFirstParty?: DocsOfThisFile;
|
||||
@ApiPropertyOptional({ required: false })
|
||||
secondPartyDetails?: SecondPartyDetail;
|
||||
|
||||
@ApiPropertyOptional({ required: false, description: "third step call" })
|
||||
locationForm?: LocationDto;
|
||||
|
||||
@ApiProperty({ type: "string", format: "binary", required: false })
|
||||
firstPartyVideoId: Express.Multer.File;
|
||||
}
|
||||
// type: "object",
|
||||
// properties: {
|
||||
// file: {
|
||||
// type: "string",
|
||||
// format: "binary",
|
||||
// },
|
||||
// },
|
||||
|
||||
export class CreateRequestManagementInitialFormStep {
|
||||
step?: StepsEnum;
|
||||
userId?: Types.ObjectId;
|
||||
nextStep?: StepsEnum;
|
||||
message?: string;
|
||||
requestId?: Types.ObjectId;
|
||||
@ApiPropertyOptional({ required: false, description: "first step" })
|
||||
initialForm?: InitialFormDto;
|
||||
|
||||
constructor(userId, reqId, step, nextStep, message?) {
|
||||
this.requestId = reqId;
|
||||
this.userId = userId;
|
||||
this.step = step;
|
||||
this.nextStep = nextStep;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestManagementDtoRs {
|
||||
step?: StepsEnum;
|
||||
firstPartyUserId?: Types.ObjectId;
|
||||
secondPartyUserId?: Types.ObjectId | null;
|
||||
nextStep?: StepsEnum;
|
||||
message?: string;
|
||||
requestId?: Types.ObjectId;
|
||||
claimFile?: Types.ObjectId | null;
|
||||
constructor(
|
||||
firstPartyUser,
|
||||
secondPartyUserId,
|
||||
step,
|
||||
nextStep,
|
||||
message?,
|
||||
requestId?,
|
||||
claimFile = null,
|
||||
) {
|
||||
this.requestId = requestId;
|
||||
this.firstPartyUserId = firstPartyUser;
|
||||
this.secondPartyUserId = secondPartyUserId;
|
||||
this.step = step;
|
||||
this.nextStep = nextStep;
|
||||
this.message = message;
|
||||
this.claimFile = claimFile;
|
||||
}
|
||||
}
|
||||
26
src/request-management/dto/expert-accident-fields.dto.ts
Normal file
26
src/request-management/dto/expert-accident-fields.dto.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { AccidentWayIF, AccidentReasonIF } from "src/expert-blame/dto/reply.dto";
|
||||
|
||||
/**
|
||||
* DTO for expert to add accident fields to an expert-initiated file
|
||||
*/
|
||||
export class ExpertAccidentFieldsDto {
|
||||
@ApiProperty({
|
||||
type: AccidentWayIF,
|
||||
description: "Accident way information (id and label)",
|
||||
})
|
||||
accidentWay: AccidentWayIF;
|
||||
|
||||
@ApiProperty({
|
||||
type: AccidentReasonIF,
|
||||
description: "Accident reason information (id, label, and fanavaran)",
|
||||
})
|
||||
accidentReason: AccidentReasonIF;
|
||||
|
||||
@ApiProperty({
|
||||
type: AccidentWayIF,
|
||||
description: "Accident type information (id and label)",
|
||||
})
|
||||
accidentType: AccidentWayIF;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||
import { LocationDto } from "./create-request-management.dto";
|
||||
import { DescriptionDto } from "./create-request-management.dto";
|
||||
import { InitialFormDto, CarBodyFormDto } from "./create-request-management.dto";
|
||||
|
||||
/**
|
||||
* Comprehensive form for experts to fill all information at once for IN_PERSON CAR_BODY files
|
||||
*/
|
||||
export class ExpertCompleteCarBodyFormDto {
|
||||
// First Party Information
|
||||
@ApiProperty({
|
||||
description: "First party phone number",
|
||||
example: "09123456789",
|
||||
})
|
||||
firstPartyPhoneNumber: string;
|
||||
|
||||
@ApiProperty({ type: InitialFormDto })
|
||||
firstPartyInitialForm: InitialFormDto;
|
||||
|
||||
@ApiProperty({ type: CarBodyFormDto })
|
||||
carBodyForm: CarBodyFormDto;
|
||||
|
||||
@ApiProperty({ type: AddPlateDto })
|
||||
firstPartyPlate: AddPlateDto;
|
||||
|
||||
@ApiProperty({ type: LocationDto })
|
||||
firstPartyLocation: LocationDto;
|
||||
|
||||
@ApiProperty({ type: DescriptionDto })
|
||||
firstPartyDescription: DescriptionDto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||
import { LocationDto } from "./create-request-management.dto";
|
||||
import { DescriptionDto } from "./create-request-management.dto";
|
||||
import { InitialFormDto } from "./create-request-management.dto";
|
||||
|
||||
/**
|
||||
* Second party information for expert-completed THIRD_PARTY forms
|
||||
*/
|
||||
export class ExpertSecondPartyInfoDto {
|
||||
@ApiProperty({
|
||||
description: "Second party phone number",
|
||||
example: "09123456789",
|
||||
})
|
||||
phoneNumber: string;
|
||||
|
||||
@ApiProperty({ type: InitialFormDto })
|
||||
initialForm: InitialFormDto;
|
||||
|
||||
@ApiProperty({ type: AddPlateDto })
|
||||
plate: AddPlateDto;
|
||||
|
||||
@ApiProperty({ type: LocationDto })
|
||||
location: LocationDto;
|
||||
|
||||
@ApiProperty({ type: DescriptionDto })
|
||||
description: DescriptionDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive form for experts to fill all information at once for IN_PERSON THIRD_PARTY files
|
||||
*/
|
||||
export class ExpertCompleteThirdPartyFormDto {
|
||||
// First Party Information
|
||||
@ApiProperty({
|
||||
description: "First party phone number",
|
||||
example: "09123456789",
|
||||
})
|
||||
firstPartyPhoneNumber: string;
|
||||
|
||||
@ApiProperty({ type: InitialFormDto })
|
||||
firstPartyInitialForm: InitialFormDto;
|
||||
|
||||
@ApiProperty({ type: AddPlateDto })
|
||||
firstPartyPlate: AddPlateDto;
|
||||
|
||||
@ApiProperty({ type: LocationDto })
|
||||
firstPartyLocation: LocationDto;
|
||||
|
||||
@ApiProperty({ type: DescriptionDto })
|
||||
firstPartyDescription: DescriptionDto;
|
||||
|
||||
// Second Party Information
|
||||
@ApiProperty({ type: ExpertSecondPartyInfoDto })
|
||||
secondParty: ExpertSecondPartyInfoDto;
|
||||
|
||||
// Guilty Party
|
||||
@ApiProperty({
|
||||
description: "Phone number of the guilty party. Must match either first or second party phone number.",
|
||||
example: "09123456789",
|
||||
})
|
||||
guiltyPartyPhoneNumber: string;
|
||||
}
|
||||
|
||||
39
src/request-management/dto/expert-initiated.dto.ts
Normal file
39
src/request-management/dto/expert-initiated.dto.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsEnum, IsOptional, IsString } from "class-validator";
|
||||
import { CreationMethod } from "../entities/schema/request-management.schema";
|
||||
|
||||
export class CreateExpertInitiatedFileDto {
|
||||
@ApiProperty({
|
||||
description: "Type of blame file",
|
||||
enum: ["THIRD_PARTY", "CAR_BODY"],
|
||||
example: "THIRD_PARTY",
|
||||
})
|
||||
@IsEnum(["THIRD_PARTY", "CAR_BODY"])
|
||||
type: "THIRD_PARTY" | "CAR_BODY";
|
||||
|
||||
@ApiProperty({
|
||||
description: "Method of creation",
|
||||
enum: CreationMethod,
|
||||
example: CreationMethod.IN_PERSON,
|
||||
})
|
||||
@IsEnum(CreationMethod)
|
||||
creationMethod: CreationMethod;
|
||||
|
||||
// Phone numbers for LINK method - users will access files via normal login
|
||||
@ApiPropertyOptional({
|
||||
description: "First party phone number. Required for LINK method.",
|
||||
example: "09123456789",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
firstPartyPhoneNumber?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "Second party phone number. Required for LINK method when type is THIRD_PARTY.",
|
||||
example: "09187654321",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
secondPartyPhoneNumber?: string;
|
||||
}
|
||||
|
||||
52
src/request-management/dto/user-reply.dto.ts
Normal file
52
src/request-management/dto/user-reply.dto.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { RequestManagementModel } from "../entities/schema/request-management.schema";
|
||||
|
||||
export class UserReplyDto {
|
||||
public _id: string;
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
isAccept: boolean;
|
||||
|
||||
@ApiProperty({ required: true })
|
||||
signId: string;
|
||||
|
||||
constructor(requestId: string) {
|
||||
this._id = requestId;
|
||||
}
|
||||
}
|
||||
|
||||
export class DetailsReplyDtoRs {
|
||||
public fields: Object;
|
||||
public actorFullName: string;
|
||||
public description: string;
|
||||
public dateOfReply: string;
|
||||
public timeOfReply: string;
|
||||
public isGuiltyUser: boolean;
|
||||
public firstPartyAccept: boolean;
|
||||
public secondPartyAccept: boolean;
|
||||
public status: string;
|
||||
public expertResendReply: any;
|
||||
public expertSubmitReply: any;
|
||||
public expertSubmitReplyFinal: any;
|
||||
public theParties: any;
|
||||
public type: string;
|
||||
|
||||
constructor(request: RequestManagementModel, details: any) {
|
||||
const finalReply = request.expertSubmitReply;
|
||||
|
||||
this.fields = finalReply.fields;
|
||||
this.description = finalReply.description;
|
||||
this.firstPartyAccept = finalReply?.firstPartyComment?.isAccept;
|
||||
this.secondPartyAccept = finalReply?.secondPartyComment?.isAccept;
|
||||
this.actorFullName = details.actorDetail.CheckedRequest.fullName;
|
||||
this.dateOfReply = details.actorDetail.Date.toLocaleDateString("fa-IR");
|
||||
this.timeOfReply = details.actorDetail.Date.toLocaleTimeString("fa-IR");
|
||||
this.isGuiltyUser = details.isGuilty;
|
||||
this.status = request.blameStatus;
|
||||
this.expertResendReply = request.expertResendReply;
|
||||
this.expertSubmitReply = request.expertSubmitReply;
|
||||
this.expertSubmitReplyFinal = request.expertSubmitReplyFinal;
|
||||
this.theParties = details.theParties;
|
||||
this.type = request.type || "THIRD_PARTY"; // Include type field
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { FilterQuery, Model, UpdateQuery } from 'mongoose';
|
||||
import { BlameDocumentModel } from '../schema/blame-document.schema';
|
||||
|
||||
@Injectable()
|
||||
export class BlameDocumentDbService {
|
||||
constructor(
|
||||
@InjectModel(BlameDocumentModel.name)
|
||||
private readonly blameDocumentModel: Model<BlameDocumentModel>,
|
||||
) {}
|
||||
|
||||
async create(createDto: Partial<BlameDocumentModel>): Promise<BlameDocumentModel> {
|
||||
const newDocument = new this.blameDocumentModel(createDto);
|
||||
return newDocument.save();
|
||||
}
|
||||
|
||||
async findOne(filter: FilterQuery<BlameDocumentModel>): Promise<BlameDocumentModel | null> {
|
||||
return this.blameDocumentModel.findOne(filter).lean();
|
||||
}
|
||||
|
||||
async findAll(filter: FilterQuery<BlameDocumentModel>): Promise<BlameDocumentModel[]> {
|
||||
return this.blameDocumentModel.find(filter).lean();
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<BlameDocumentModel | null> {
|
||||
return this.blameDocumentModel.findById(id).lean();
|
||||
}
|
||||
|
||||
async findByIdAndUpdate(
|
||||
id: string,
|
||||
updateDto: UpdateQuery<BlameDocumentModel>,
|
||||
): Promise<BlameDocumentModel | null> {
|
||||
return this.blameDocumentModel.findByIdAndUpdate(id, updateDto, { new: true }).lean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { Model, Types } from "mongoose";
|
||||
import { BlameVideosModel } from "src/request-management/entities/schema/blame-video.schema";
|
||||
|
||||
export class BlameVideoDbService {
|
||||
constructor(
|
||||
@InjectModel(BlameVideosModel.name)
|
||||
private readonly blameVideoModel: Model<BlameVideosModel>,
|
||||
) {}
|
||||
|
||||
async create(video: BlameVideosModel): Promise<BlameVideosModel> {
|
||||
return await this.blameVideoModel.create(video);
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<BlameVideosModel> {
|
||||
return await this.blameVideoModel.findOne(new Types.ObjectId(id));
|
||||
}
|
||||
|
||||
async findOneByRequestId(id: string): Promise<BlameVideosModel> {
|
||||
return await this.blameVideoModel.findById(id);
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<BlameVideosModel | null> {
|
||||
return this.blameVideoModel.findById(id).lean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { Model, Types } from "mongoose";
|
||||
import { BlameVoicesModel } from "../schema/blame-voice.schema";
|
||||
|
||||
export class BlameVoiceDbService {
|
||||
constructor(
|
||||
@InjectModel(BlameVoicesModel.name)
|
||||
private readonly blameVoiceModel: Model<BlameVoicesModel>,
|
||||
) {}
|
||||
|
||||
async create(Voice: BlameVoicesModel): Promise<BlameVoicesModel> {
|
||||
return await this.blameVoiceModel.create(Voice);
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<BlameVoicesModel> {
|
||||
return await this.blameVoiceModel.findOne(new Types.ObjectId(id));
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<BlameVoicesModel | null> {
|
||||
return this.blameVoiceModel.findById(id).lean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { FilterQuery, Model, Types, UpdateQuery } from "mongoose";
|
||||
import { RequestManagementModel } from "../schema/request-management.schema";
|
||||
|
||||
export class RequestManagementDbService {
|
||||
constructor(
|
||||
@InjectModel(RequestManagementModel.name)
|
||||
private readonly requestModel: Model<RequestManagementModel>,
|
||||
) {}
|
||||
|
||||
async create(
|
||||
request: RequestManagementModel,
|
||||
): Promise<RequestManagementModel> {
|
||||
return await this.requestModel.create(request);
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
return await this.requestModel.findOne(new Types.ObjectId(id));
|
||||
}
|
||||
|
||||
async findAll(filter?: FilterQuery<RequestManagementModel>) {
|
||||
return await this.requestModel.find({ ...filter });
|
||||
}
|
||||
|
||||
async findAllAndPagination(query, currentPage, resPerPage?) {
|
||||
const responsePerPage = resPerPage | 1;
|
||||
const skipPge = responsePerPage * (Number(currentPage) - 1);
|
||||
return await this.requestModel
|
||||
.find(query)
|
||||
.limit(responsePerPage)
|
||||
.skip(skipPge);
|
||||
}
|
||||
|
||||
async countByFilter(
|
||||
filter: FilterQuery<RequestManagementModel>,
|
||||
): Promise<number> {
|
||||
return await this.requestModel.countDocuments(filter);
|
||||
}
|
||||
|
||||
async findAndUpdate(
|
||||
filter: FilterQuery<RequestManagementModel>,
|
||||
update: UpdateQuery<RequestManagementModel>,
|
||||
) {
|
||||
return await this.requestModel.findByIdAndUpdate(filter, update);
|
||||
}
|
||||
|
||||
async findOneAndUpdate(
|
||||
filter: FilterQuery<RequestManagementModel>,
|
||||
update: UpdateQuery<RequestManagementModel>,
|
||||
options: {
|
||||
new?: boolean;
|
||||
upsert?: boolean;
|
||||
runValidators?: boolean;
|
||||
} = {},
|
||||
): Promise<RequestManagementModel | null> {
|
||||
try {
|
||||
const updatedDoc = await this.requestModel.findOneAndUpdate(
|
||||
filter,
|
||||
update,
|
||||
{
|
||||
new: options.new ?? true, // Return updated doc by default
|
||||
upsert: options.upsert ?? false, // Don’t create new if not found
|
||||
runValidators: options.runValidators ?? true,
|
||||
},
|
||||
);
|
||||
|
||||
return updatedDoc;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`❌ [RequestManagementDbService] findOneAndUpdate failed`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findByIdAndUpdate(
|
||||
id: string,
|
||||
update: UpdateQuery<RequestManagementModel>,
|
||||
) {
|
||||
return await this.requestModel.findByIdAndUpdate(
|
||||
{ _id: new Types.ObjectId(id) },
|
||||
update,
|
||||
);
|
||||
}
|
||||
|
||||
async aggregate(filter?) {
|
||||
return await this.requestModel.aggregate(filter);
|
||||
}
|
||||
|
||||
async updateMany(filter?, update?) {
|
||||
return await this.requestModel.updateMany(filter, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { Model, Types } from "mongoose";
|
||||
import { UserSignModel } from "../schema/sign.schema";
|
||||
|
||||
export class UserSignDbService {
|
||||
constructor(
|
||||
@InjectModel(UserSignModel.name)
|
||||
private readonly blameVoiceModel: Model<UserSignModel>,
|
||||
) {}
|
||||
|
||||
async create(sign: UserSignModel): Promise<UserSignModel> {
|
||||
return await this.blameVoiceModel.create(sign);
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<UserSignModel> {
|
||||
return await this.blameVoiceModel.findOne(new Types.ObjectId(id));
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<UserSignModel | null> {
|
||||
// Use your correct Model type
|
||||
return this.blameVoiceModel.findById(id).lean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export enum BlameDocumentType {
|
||||
NationalCertificate = "nationalCertificate",
|
||||
CarCertificate = "carCertificate",
|
||||
DrivingLicence = "drivingLicense",
|
||||
CarGreenCard = "carGreenCard",
|
||||
}
|
||||
|
||||
@Schema({ collection: "blame-documents", versionKey: false, timestamps: true })
|
||||
export class BlameDocumentModel {
|
||||
@Prop({ required: true })
|
||||
path: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
requestId: Types.ObjectId;
|
||||
|
||||
@Prop({ required: true })
|
||||
fileName: string;
|
||||
|
||||
@Prop({ required: true, type: String, enum: BlameDocumentType })
|
||||
documentType: BlameDocumentType;
|
||||
}
|
||||
export const BlameDocumentSchema =
|
||||
SchemaFactory.createForClass(BlameDocumentModel);
|
||||
15
src/request-management/entities/schema/blame-video.schema.ts
Normal file
15
src/request-management/entities/schema/blame-video.schema.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
@Schema({ collection: "blame-videos", versionKey: false })
|
||||
export class BlameVideosModel {
|
||||
@Prop()
|
||||
path: string;
|
||||
|
||||
@Prop()
|
||||
requestId: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
fileName: string;
|
||||
}
|
||||
export const BlameVideoSchema = SchemaFactory.createForClass(BlameVideosModel);
|
||||
24
src/request-management/entities/schema/blame-voice.schema.ts
Normal file
24
src/request-management/entities/schema/blame-voice.schema.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export enum UploadContext {
|
||||
INITIAL = 'INITIAL_UPLOAD',
|
||||
EXPERT_RESEND = 'EXPERT_RESEND',
|
||||
}
|
||||
|
||||
@Schema({ collection: "blame-voice", versionKey: false })
|
||||
export class BlameVoicesModel {
|
||||
@Prop()
|
||||
path: string;
|
||||
|
||||
@Prop()
|
||||
requestId: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
fileName: string;
|
||||
|
||||
@Prop({ type: String, enum: UploadContext, default: UploadContext.INITIAL })
|
||||
context?: UploadContext;
|
||||
}
|
||||
|
||||
export const BlameVoiceSchema = SchemaFactory.createForClass(BlameVoicesModel);
|
||||
@@ -0,0 +1,465 @@
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
@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,
|
||||
);
|
||||
19
src/request-management/entities/schema/sign.schema.ts
Normal file
19
src/request-management/entities/schema/sign.schema.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
@Schema({ collection: "blame-sign", versionKey: false })
|
||||
export class UserSignModel {
|
||||
@Prop()
|
||||
path: string;
|
||||
|
||||
@Prop()
|
||||
userId: string;
|
||||
|
||||
@Prop()
|
||||
requestId: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export const UserSignSchema = SchemaFactory.createForClass(UserSignModel);
|
||||
275
src/request-management/expert-initiated.controller.ts
Normal file
275
src/request-management/expert-initiated.controller.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import { extname } from "node:path";
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
Get,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
FileInterceptor,
|
||||
} from "@nestjs/platform-express";
|
||||
import { diskStorage } from "multer";
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiParam,
|
||||
ApiQuery,
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiConsumes,
|
||||
} from "@nestjs/swagger";
|
||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||
import { CurrentUser } from "src/decorators/user.decorator";
|
||||
import { Roles } from "src/decorators/roles.decorator";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
import { CreateExpertInitiatedFileDto } from "./dto/expert-initiated.dto";
|
||||
import { ExpertCompleteThirdPartyFormDto } from "./dto/expert-complete-third-party-form.dto";
|
||||
import { ExpertCompleteCarBodyFormDto } from "./dto/expert-complete-car-body-form.dto";
|
||||
import { ExpertAccidentFieldsDto } from "./dto/expert-accident-fields.dto";
|
||||
|
||||
@ApiTags("expert-initiated-blame")
|
||||
@Controller("expert-initiated-blame")
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(LocalActorAuthGuard, RolesGuard)
|
||||
@Roles(RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT)
|
||||
export class ExpertInitiatedController {
|
||||
constructor(
|
||||
private readonly requestManagementService: RequestManagementService,
|
||||
) {}
|
||||
|
||||
@Post("create")
|
||||
@ApiOperation({
|
||||
summary: "Create expert-initiated blame file",
|
||||
description:
|
||||
"Expert creates an initial incomplete blame file. Can be IN_PERSON (expert fills all info) or LINK (expert sends link to users).",
|
||||
})
|
||||
@ApiBody({ type: CreateExpertInitiatedFileDto })
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: "File created successfully",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
requestId: { type: "string" },
|
||||
},
|
||||
},
|
||||
})
|
||||
async createExpertInitiatedFile(
|
||||
@CurrentUser() expert: any,
|
||||
@Body() dto: CreateExpertInitiatedFileDto,
|
||||
) {
|
||||
return await this.requestManagementService.createExpertInitiatedFile(
|
||||
expert,
|
||||
dto,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("complete-form-third-party/:requestId")
|
||||
@ApiOperation({
|
||||
summary: "Expert completes all information for IN_PERSON THIRD_PARTY file",
|
||||
description:
|
||||
"Expert fills all information at once for IN_PERSON THIRD_PARTY files. " +
|
||||
"Includes first party, second party, and guilty party selection. " +
|
||||
"Video and voice uploads are handled separately.",
|
||||
})
|
||||
@ApiParam({
|
||||
name: "requestId",
|
||||
description: "ID of the expert-initiated IN_PERSON THIRD_PARTY file",
|
||||
})
|
||||
@ApiBody({ type: ExpertCompleteThirdPartyFormDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: "Form completed successfully. File is now ready for user signatures.",
|
||||
})
|
||||
async completeThirdPartyForm(
|
||||
@CurrentUser() expert: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() formData: ExpertCompleteThirdPartyFormDto,
|
||||
) {
|
||||
return await this.requestManagementService.expertCompleteThirdPartyForm(
|
||||
expert,
|
||||
requestId,
|
||||
formData,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("complete-form-car-body/:requestId")
|
||||
@ApiOperation({
|
||||
summary: "Expert completes all information for IN_PERSON CAR_BODY file",
|
||||
description:
|
||||
"Expert fills all information at once for IN_PERSON CAR_BODY files. " +
|
||||
"Includes first party data and carBodyForm. " +
|
||||
"Video and voice uploads are handled separately.",
|
||||
})
|
||||
@ApiParam({
|
||||
name: "requestId",
|
||||
description: "ID of the expert-initiated IN_PERSON CAR_BODY file",
|
||||
})
|
||||
@ApiBody({ type: ExpertCompleteCarBodyFormDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: "Form completed successfully. File is now ready for user signature.",
|
||||
})
|
||||
async completeCarBodyForm(
|
||||
@CurrentUser() expert: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() formData: ExpertCompleteCarBodyFormDto,
|
||||
) {
|
||||
return await this.requestManagementService.expertCompleteCarBodyForm(
|
||||
expert,
|
||||
requestId,
|
||||
formData,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("upload-video/:requestId")
|
||||
@ApiOperation({
|
||||
summary: "Expert uploads video for expert-initiated file",
|
||||
description:
|
||||
"Expert uploads a single video file for the entire blame file. " +
|
||||
"Only one video is needed per file (not per party). " +
|
||||
"Only works for expert-initiated files.",
|
||||
})
|
||||
@ApiParam({
|
||||
name: "requestId",
|
||||
description: "ID of the expert-initiated file",
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: {
|
||||
fileSize: 20 * 1024 * 1024, // 20MB
|
||||
},
|
||||
storage: diskStorage({
|
||||
destination: "./files/video",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const filename = `expert-${file.originalname}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: "Video uploaded successfully",
|
||||
})
|
||||
async uploadVideo(
|
||||
@CurrentUser() expert: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@UploadedFile() file?: Express.Multer.File,
|
||||
) {
|
||||
return await this.requestManagementService.expertUploadVideo(
|
||||
expert,
|
||||
requestId,
|
||||
file,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("upload-voice/:requestId")
|
||||
@ApiOperation({
|
||||
summary: "Expert uploads voice for expert-initiated file",
|
||||
description:
|
||||
"Expert uploads a single voice file for the entire blame file. " +
|
||||
"Only one voice is needed per file (not per party). " +
|
||||
"Only works for expert-initiated files.",
|
||||
})
|
||||
@ApiParam({
|
||||
name: "requestId",
|
||||
description: "ID of the expert-initiated file",
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
voice: {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@UseInterceptors(
|
||||
FileInterceptor("voice", {
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024, // 10MB
|
||||
},
|
||||
storage: diskStorage({
|
||||
destination: "./files/voice",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const flname = file.originalname.split(".")[0];
|
||||
const filename = `expert-${flname}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: "Voice uploaded successfully",
|
||||
})
|
||||
async uploadVoice(
|
||||
@CurrentUser() expert: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@UploadedFile() voice?: Express.Multer.File,
|
||||
) {
|
||||
return await this.requestManagementService.expertUploadVoice(
|
||||
expert,
|
||||
requestId,
|
||||
voice,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("add-accident-fields/:requestId")
|
||||
@ApiOperation({
|
||||
summary: "Expert adds accident fields to expert-initiated file",
|
||||
description:
|
||||
"Expert adds accident way, reason, and type fields to an expert-initiated file. " +
|
||||
"Can be used for both IN_PERSON and LINK-based files. " +
|
||||
"If expertSubmitReply already exists, updates the fields. Otherwise, creates a new expertSubmitReply.",
|
||||
})
|
||||
@ApiParam({
|
||||
name: "requestId",
|
||||
description: "ID of the expert-initiated file",
|
||||
})
|
||||
@ApiBody({ type: ExpertAccidentFieldsDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: "Accident fields added successfully",
|
||||
})
|
||||
async addAccidentFields(
|
||||
@CurrentUser() expert: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() fields: ExpertAccidentFieldsDto,
|
||||
) {
|
||||
return await this.requestManagementService.expertAddAccidentFields(
|
||||
expert,
|
||||
requestId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
22
src/request-management/request-management.controller.spec.ts
Normal file
22
src/request-management/request-management.controller.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { RequestManagementController } from "./request-management.controller";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
|
||||
describe("RequestManagementController", () => {
|
||||
let controller: RequestManagementController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [RequestManagementController],
|
||||
providers: [RequestManagementService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<RequestManagementController>(
|
||||
RequestManagementController,
|
||||
);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
403
src/request-management/request-management.controller.ts
Normal file
403
src/request-management/request-management.controller.ts
Normal file
@@ -0,0 +1,403 @@
|
||||
import { extname } from "node:path";
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
Req,
|
||||
Put,
|
||||
UploadedFiles,
|
||||
Patch,
|
||||
Query,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
FileFieldsInterceptor,
|
||||
FileInterceptor,
|
||||
} from "@nestjs/platform-express";
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiConsumes,
|
||||
ApiParam,
|
||||
ApiQuery,
|
||||
ApiTags,
|
||||
} from "@nestjs/swagger";
|
||||
import { Request } from "express";
|
||||
import { diskStorage } from "multer";
|
||||
import { GlobalGuard } from "src/auth/guards/global.guard";
|
||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||
import { CurrentUser } from "src/decorators/user.decorator";
|
||||
import { Roles } from "src/decorators/roles.decorator";
|
||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { BlameDocumentType } from "./entities/schema/blame-document.schema";
|
||||
import {
|
||||
CarBodyFormDto,
|
||||
CarBodySecondForm,
|
||||
CreateRequestDto,
|
||||
DescriptionDto,
|
||||
InitialFormDto,
|
||||
LocationDto,
|
||||
} from "./dto/create-request-management.dto";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
import { InPersonVisitDto } from "src/claim-request-management/dto/in-person-visit.dto";
|
||||
|
||||
@Controller("blame-request-management")
|
||||
@ApiTags("blame-request-management")
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(GlobalGuard, RolesGuard)
|
||||
@Roles(RoleEnum.USER)
|
||||
export class RequestManagementController {
|
||||
constructor(
|
||||
private readonly requestManagementService: RequestManagementService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
@UseGuards(GlobalGuard)
|
||||
async createRequest(
|
||||
@CurrentUser() user,
|
||||
@Body() createRequestDto: CreateRequestDto,
|
||||
) {
|
||||
return await this.requestManagementService.createRequest(
|
||||
user,
|
||||
createRequestDto.type,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/initial-form/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: InitialFormDto })
|
||||
@UseGuards(GlobalGuard)
|
||||
async initialFrom(
|
||||
@Body()
|
||||
body: InitialFormDto,
|
||||
@CurrentUser() user,
|
||||
@Param("requestId") requestId,
|
||||
) {
|
||||
return await this.requestManagementService.initialFormStep(
|
||||
body,
|
||||
user,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: {
|
||||
fileSize: 20 * 1024 * 1024,
|
||||
},
|
||||
storage: diskStorage({
|
||||
destination: "./files/video",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const filename = `${file.originalname}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@UseGuards(GlobalGuard)
|
||||
@ApiParam({ name: "requestId" })
|
||||
@Post("upload-video/:requestId")
|
||||
@UseGuards(GlobalGuard)
|
||||
async videoUploader(
|
||||
@Param("requestId") requestId,
|
||||
@CurrentUser() user,
|
||||
@UploadedFile() file?: Express.Multer.File,
|
||||
) {
|
||||
return await this.requestManagementService.videoUploadStep(
|
||||
file,
|
||||
user,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/add-plate/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: AddPlateDto })
|
||||
@UseGuards(GlobalGuard)
|
||||
async addPlate(
|
||||
@Body()
|
||||
body: AddPlateDto,
|
||||
@CurrentUser() user,
|
||||
@Param("requestId") requestId,
|
||||
) {
|
||||
return await this.requestManagementService.addPlateToRequest(
|
||||
user,
|
||||
body,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/carbody-form/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: CarBodyFormDto })
|
||||
@UseGuards(GlobalGuard)
|
||||
async carBodyForm(
|
||||
@Body()
|
||||
body: CarBodyFormDto,
|
||||
@CurrentUser() user,
|
||||
@Param("requestId") requestId,
|
||||
) {
|
||||
return await this.requestManagementService.carBodyFormStep(
|
||||
body,
|
||||
user,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/carbody-secondform/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: CarBodySecondForm })
|
||||
@UseGuards(GlobalGuard)
|
||||
async carBodySecondForm(
|
||||
@Body()
|
||||
body: CarBodySecondForm,
|
||||
@CurrentUser() user,
|
||||
@Param("requestId") requestId,
|
||||
) {
|
||||
return await this.requestManagementService.carBodySecondForm(
|
||||
body,
|
||||
user,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/add-detail-location/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: LocationDto })
|
||||
@UseGuards(GlobalGuard)
|
||||
async addLocation(
|
||||
@Body()
|
||||
body: LocationDto,
|
||||
@CurrentUser() user,
|
||||
@Param("requestId") requestId,
|
||||
) {
|
||||
return await this.requestManagementService.addDetailToRequestLocation(
|
||||
user.sub,
|
||||
body,
|
||||
requestId,
|
||||
user, // Pass user object for expert verification
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024,
|
||||
},
|
||||
storage: diskStorage({
|
||||
destination: "./files/voice",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const flname = file.originalname.split(".")[0];
|
||||
const filename = `${flname}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiParam({ name: "requestId" })
|
||||
@Post("upload-voice/:requestId")
|
||||
@UseGuards(GlobalGuard)
|
||||
async voiceUploader(
|
||||
@Param("requestId") requestId,
|
||||
@CurrentUser() user,
|
||||
@UploadedFile() voice?: Express.Multer.File,
|
||||
) {
|
||||
return await this.requestManagementService.voiceUploadStep(
|
||||
user,
|
||||
voice,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("/add-detail-description/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: DescriptionDto })
|
||||
@UseGuards(GlobalGuard)
|
||||
async addDescription(
|
||||
@Body()
|
||||
body: DescriptionDto,
|
||||
@Param("requestId") requestId,
|
||||
@CurrentUser() user,
|
||||
) {
|
||||
return await this.requestManagementService.addDescriptionToRequest(
|
||||
user,
|
||||
body,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("add-second-party-details/:phoneNumber/:requestId/:frontendRoutes")
|
||||
@ApiParam({ name: "phoneNumber" })
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiParam({ name: "frontendRoutes" })
|
||||
@UseGuards(GlobalGuard)
|
||||
async addSecondPartyDetailsToRequest(
|
||||
@Req() req: Request,
|
||||
@Param("phoneNumber") phoneNumber,
|
||||
@Param("requestId") requestId,
|
||||
@Param("frontendRoutes") frontendRoutes,
|
||||
@CurrentUser() firstPartyUser,
|
||||
) {
|
||||
return await this.requestManagementService.addSecondPartyDetailsToRequest(
|
||||
phoneNumber,
|
||||
requestId,
|
||||
firstPartyUser,
|
||||
frontendRoutes,
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
@Get("requests")
|
||||
async requests(@CurrentUser() user) {
|
||||
return await this.requestManagementService.myRequests(user.username);
|
||||
}
|
||||
|
||||
@Get("creatable-claims")
|
||||
@UseGuards(GlobalGuard)
|
||||
async getCreatableClaims(@CurrentUser() user) {
|
||||
return await this.requestManagementService.getCreatableClaims(user);
|
||||
}
|
||||
|
||||
@Get("request/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@Roles(RoleEnum.USER)
|
||||
async checkParties(@CurrentUser() user, @Param("requestId") requestId) {
|
||||
return await this.requestManagementService.requestDetails(user, requestId);
|
||||
}
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
sign: {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
},
|
||||
isAccept: {
|
||||
type: "Boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("sign", {
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024,
|
||||
},
|
||||
storage: diskStorage({
|
||||
destination: "./files/signs",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const filename = `${file.originalname.split(/[.,\s-]/)[0]}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@Put("request/reply/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@UseGuards(GlobalGuard)
|
||||
async userReply(
|
||||
@Body("isAccept") isAccept: boolean,
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() user,
|
||||
@UploadedFile() sign?: Express.Multer.File,
|
||||
) {
|
||||
const props = { requestId, user };
|
||||
return await this.requestManagementService.userReply(
|
||||
props,
|
||||
Boolean(isAccept),
|
||||
sign,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
nationalCertificate: { type: "string", format: "binary" },
|
||||
carCertificate: { type: "string", format: "binary" },
|
||||
drivingLicense: { type: "string", format: "binary" },
|
||||
carGreenCard: { type: "string", format: "binary" },
|
||||
voice: { type: "string", format: "binary" },
|
||||
description: { type: "string" },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileFieldsInterceptor(
|
||||
[
|
||||
{ name: "nationalCertificate", maxCount: 1 },
|
||||
{ name: "carCertificate", maxCount: 1 },
|
||||
{ name: "drivingLicense", maxCount: 1 },
|
||||
{ name: "carGreenCard", maxCount: 1 },
|
||||
{ name: "voice", maxCount: 1 },
|
||||
],
|
||||
{
|
||||
limits: { fileSize: 10 * 1024 * 1024 },
|
||||
storage: diskStorage({
|
||||
destination: "./files/blame-resend-docs",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
const filename = `${file.fieldname}-${unique}${ex}`;
|
||||
callback(null, filename);
|
||||
},
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
@UseGuards(GlobalGuard)
|
||||
@ApiParam({ name: "requestId" })
|
||||
@Put("request/resend/:requestId")
|
||||
userResendReply(
|
||||
@Body("description") description: string,
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() user: any,
|
||||
@UploadedFiles()
|
||||
files: { [key in BlameDocumentType | "voice"]?: Express.Multer.File[] },
|
||||
) {
|
||||
return this.requestManagementService.userResend(
|
||||
files,
|
||||
user,
|
||||
requestId,
|
||||
description,
|
||||
);
|
||||
}
|
||||
}
|
||||
72
src/request-management/request-management.module.ts
Normal file
72
src/request-management/request-management.module.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { forwardRef, Module } from "@nestjs/common";
|
||||
import { MongooseModule } from "@nestjs/mongoose";
|
||||
import { MulterModule } from "@nestjs/platform-express";
|
||||
import { ClaimRequestManagementModule } from "src/claim-request-management/claim-request-management.module";
|
||||
import { ClientModule } from "src/client/client.module";
|
||||
import { PlatesModule } from "src/plates/plates.module";
|
||||
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
|
||||
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
|
||||
import { SandHubModule } from "src/sand-hub/sand-hub.module";
|
||||
import { UsersModule } from "src/users/users.module";
|
||||
import { CronModule } from "src/utils/cron/cron.module";
|
||||
import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module";
|
||||
import { BlameDocumentDbService } from "./entities/db-service/blame-document.db.service";
|
||||
import { BlameVoiceDbService } from "./entities/db-service/blame.voice.db.service";
|
||||
import { UserSignDbService } from "./entities/db-service/sign.db.service";
|
||||
import { UserSignModel, UserSignSchema } from "./entities/schema/sign.schema";
|
||||
import {
|
||||
BlameVideosModel,
|
||||
BlameVideoSchema,
|
||||
} from "./entities/schema/blame-video.schema";
|
||||
import {
|
||||
BlameVoiceSchema,
|
||||
BlameVoicesModel,
|
||||
} from "./entities/schema/blame-voice.schema";
|
||||
import { BlameDocumentModel, BlameDocumentSchema } from "./entities/schema/blame-document.schema";
|
||||
import {
|
||||
RequestManagementModel,
|
||||
RequestManagementSchema,
|
||||
} from "./entities/schema/request-management.schema";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
import { RequestManagementController } from "./request-management.controller";
|
||||
import { ExpertInitiatedController } from "./expert-initiated.controller";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UsersModule,
|
||||
ClientModule,
|
||||
SandHubModule,
|
||||
SmsManagerModule,
|
||||
PlatesModule,
|
||||
MulterModule.register({
|
||||
dest: "./files/video",
|
||||
}),
|
||||
MongooseModule.forFeature([
|
||||
{ name: RequestManagementModel.name, schema: RequestManagementSchema },
|
||||
{ name: BlameVideosModel.name, schema: BlameVideoSchema },
|
||||
{ name: BlameVoicesModel.name, schema: BlameVoiceSchema },
|
||||
{ name: UserSignModel.name, schema: UserSignSchema },
|
||||
{ name: BlameDocumentModel.name, schema: BlameDocumentSchema },
|
||||
]),
|
||||
forwardRef(() => ClaimRequestManagementModule),
|
||||
CronModule,
|
||||
],
|
||||
controllers: [RequestManagementController, ExpertInitiatedController],
|
||||
providers: [
|
||||
RequestManagementService,
|
||||
BlameVideoDbService,
|
||||
BlameVoiceDbService,
|
||||
UserSignDbService,
|
||||
RequestManagementDbService,
|
||||
BlameDocumentDbService,
|
||||
],
|
||||
exports: [
|
||||
RequestManagementService,
|
||||
RequestManagementDbService,
|
||||
BlameVideoDbService,
|
||||
BlameVoiceDbService,
|
||||
BlameDocumentDbService,
|
||||
UserSignDbService,
|
||||
],
|
||||
})
|
||||
export class RequestManagementModule {}
|
||||
18
src/request-management/request-management.service.spec.ts
Normal file
18
src/request-management/request-management.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
|
||||
describe("RequestManagementService", () => {
|
||||
let service: RequestManagementService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [RequestManagementService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<RequestManagementService>(RequestManagementService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
3146
src/request-management/request-management.service.ts
Normal file
3146
src/request-management/request-management.service.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user