Merge pull request 'update the blame service' (#9) from s.hajizadeh/yara724api:main into main

Reviewed-on: Yara724/api#9
This commit is contained in:
2026-03-18 15:53:40 +03:30
2 changed files with 67 additions and 42 deletions

View File

@@ -269,8 +269,7 @@ export class RequestManagementService {
const step = stepByNumber ?? stepByKey; const step = stepByNumber ?? stepByKey;
if (!step) { if (!step) {
throw new InternalServerErrorException( throw new InternalServerErrorException(
`Workflow step not found (${stepNumber ? `stepNumber=${stepNumber}` : ""}${ `Workflow step not found (${stepNumber ? `stepNumber=${stepNumber}` : ""}${stepNumber && stepKey ? ", " : ""
stepNumber && stepKey ? ", " : ""
}${stepKey ? `stepKey=${stepKey}` : ""})`, }${stepKey ? `stepKey=${stepKey}` : ""})`,
); );
} }
@@ -304,7 +303,7 @@ export class RequestManagementService {
private readonly workflowStepDbService: WorkflowStepDbService, private readonly workflowStepDbService: WorkflowStepDbService,
private readonly hashService: HashService, private readonly hashService: HashService,
private readonly userAuthService: UserAuthService, private readonly userAuthService: UserAuthService,
) {} ) { }
async createRequestV2(user: any, type: BlameRequestType) { async createRequestV2(user: any, type: BlameRequestType) {
const firstStep = await this.getWorkflowStep({ stepNumber: 1 }); const firstStep = await this.getWorkflowStep({ stepNumber: 1 });
@@ -768,8 +767,7 @@ export class RequestManagementService {
); );
if (err?.response) { if (err?.response) {
this.logger.error( this.logger.error(
`[TEJARAT] block inquiry response for request=${req._id}: status=${ `[TEJARAT] block inquiry response for request=${req._id}: status=${err.response.status
err.response.status
}, data=${JSON.stringify(err.response.data)}`, }, data=${JSON.stringify(err.response.data)}`,
); );
} }
@@ -1310,10 +1308,10 @@ export class RequestManagementService {
if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) { if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) {
// User should already be set in firstPartyDetails or secondPartyDetails // User should already be set in firstPartyDetails or secondPartyDetails
const isFirstParty = foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub || const isFirstParty = foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub ||
foundRequest.firstPartyDetails?.firstPartyPhoneNumber === user.username; foundRequest.firstPartyDetails?.firstPartyPhoneNumber === user.username;
const isSecondParty = foundRequest.secondPartyDetails?.secondPartyId?.toString() === user.sub || const isSecondParty = foundRequest.secondPartyDetails?.secondPartyId?.toString() === user.sub ||
foundRequest.secondPartyDetails?.secondPartyPhoneNumber === user.username; foundRequest.secondPartyDetails?.secondPartyPhoneNumber === user.username;
if (!isFirstParty && !isSecondParty) { if (!isFirstParty && !isSecondParty) {
throw new ForbiddenException( throw new ForbiddenException(
"You are not authorized to complete this file. This file was created by an expert for specific parties.", "You are not authorized to complete this file. This file was created by an expert for specific parties.",
@@ -1357,9 +1355,9 @@ export class RequestManagementService {
); );
} else if ( } else if (
foundRequest?.firstPartyDetails?.firstPartyPhoneNumber == user.username || foundRequest?.firstPartyDetails?.firstPartyPhoneNumber == user.username ||
(foundRequest.expertInitiated && (foundRequest.expertInitiated &&
foundRequest.creationMethod === CreationMethod.LINK && foundRequest.creationMethod === CreationMethod.LINK &&
foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub) foundRequest.firstPartyDetails?.firstPartyId?.toString() === user.sub)
) { ) {
// Normal flow or link-based - set full firstPartyDetails // Normal flow or link-based - set full firstPartyDetails
const updatePayload: any = { const updatePayload: any = {
@@ -1373,7 +1371,7 @@ export class RequestManagementService {
nextStep: StepsEnum.F_videoUpload, nextStep: StepsEnum.F_videoUpload,
$push: { steps: StepsEnum.F_InitialForm }, $push: { steps: StepsEnum.F_InitialForm },
}; };
// For link-based expert-initiated files, ensure filledBy is set // For link-based expert-initiated files, ensure filledBy is set
if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) { if (foundRequest.expertInitiated && foundRequest.creationMethod === CreationMethod.LINK) {
updatePayload.$set = { filledBy: FilledBy.CUSTOMER }; updatePayload.$set = { filledBy: FilledBy.CUSTOMER };
@@ -1492,7 +1490,7 @@ export class RequestManagementService {
partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate; partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate;
const currentStep = const currentStep =
partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate; partyType === "firstParty" ? StepsEnum.F_addPlate : StepsEnum.S_addPlate;
// Determine next step based on request type // Determine next step based on request type
let nextStep: StepsEnum; let nextStep: StepsEnum;
if (request.type === "CAR_BODY") { if (request.type === "CAR_BODY") {
@@ -1635,8 +1633,8 @@ export class RequestManagementService {
// Determine if the current user is the first party // Determine if the current user is the first party
// Check if nextStep is firstParty-addPlate OR if user is the first party phone number // Check if nextStep is firstParty-addPlate OR if user is the first party phone number
const isFirstParty = const isFirstParty =
(request.nextStep === StepsEnum.F_addPlate || (request.nextStep === StepsEnum.F_addPlate ||
request.currentStep === StepsEnum.F_addPlate) && request.currentStep === StepsEnum.F_addPlate) &&
request.firstPartyDetails.firstPartyPhoneNumber === user.username; request.firstPartyDetails.firstPartyPhoneNumber === user.username;
if (!isFirstParty) { if (!isFirstParty) {
@@ -1787,7 +1785,7 @@ export class RequestManagementService {
const findRequest = const findRequest =
await this.requestManagementDbService.findOne(requestId); await this.requestManagementDbService.findOne(requestId);
if (!findRequest) throw new NotFoundException("requestId not found"); if (!findRequest) throw new NotFoundException("requestId not found");
// Allow first party only // Allow first party only
if (findRequest.firstPartyDetails.firstPartyId == new Types.ObjectId(userId)) { if (findRequest.firstPartyDetails.firstPartyId == new Types.ObjectId(userId)) {
await this.requestManagementDbService.findAndUpdate( await this.requestManagementDbService.findAndUpdate(
@@ -1799,7 +1797,7 @@ export class RequestManagementService {
$push: { steps: StepsEnum.F_addLocation }, // Ensure step is saved $push: { steps: StepsEnum.F_addLocation }, // Ensure step is saved
}, },
); );
return new RequestManagementDtoRs( return new RequestManagementDtoRs(
findRequest.firstPartyDetails.firstPartyId, findRequest.firstPartyDetails.firstPartyId,
findRequest?.secondPartyDetails?.secondPartyId || null, findRequest?.secondPartyDetails?.secondPartyId || null,
@@ -1840,7 +1838,7 @@ export class RequestManagementService {
if (!request) { if (!request) {
throw new NotFoundException("request is not found"); throw new NotFoundException("request is not found");
} }
const voiceDoc = await this.blameVoiceDbService.create({ const voiceDoc = await this.blameVoiceDbService.create({
fileName: voice.filename, fileName: voice.filename,
path: voice.path, path: voice.path,
@@ -1990,7 +1988,7 @@ export class RequestManagementService {
); );
} }
const nextStepMessage = request.type === "CAR_BODY" const nextStepMessage = request.type === "CAR_BODY"
? "Please sign the document to complete your request" ? "Please sign the document to complete your request"
: "add secondParty to request"; : "add secondParty to request";
@@ -2798,7 +2796,7 @@ export class RequestManagementService {
const replyField = isObjection const replyField = isObjection
? "expertSubmitReplyFinal" ? "expertSubmitReplyFinal"
: "expertSubmitReply"; : "expertSubmitReply";
// Handle cases where firstPartyId might be undefined (e.g., IN_PERSON expert-filled files) // Handle cases where firstPartyId might be undefined (e.g., IN_PERSON expert-filled files)
const firstPartyId = request.firstPartyDetails?.firstPartyId; const firstPartyId = request.firstPartyDetails?.firstPartyId;
if (!firstPartyId) { if (!firstPartyId) {
@@ -2806,7 +2804,7 @@ export class RequestManagementService {
"First party ID is missing. Please ensure the file has been properly initialized.", "First party ID is missing. Please ensure the file has been properly initialized.",
); );
} }
const isFirstParty = userId === firstPartyId.toString(); const isFirstParty = userId === firstPartyId.toString();
const currentPartyKey = isFirstParty const currentPartyKey = isFirstParty
@@ -2859,13 +2857,13 @@ export class RequestManagementService {
// (CAR_BODY requests might not have actorLocked set) // (CAR_BODY requests might not have actorLocked set)
if (request.actorLocked?.actorId) { if (request.actorLocked?.actorId) {
// Convert ObjectIds to strings safely // Convert ObjectIds to strings safely
const actorIdStr = request.actorLocked.actorId.toString ? const actorIdStr = request.actorLocked.actorId.toString ?
request.actorLocked.actorId.toString() : request.actorLocked.actorId.toString() :
String(request.actorLocked.actorId); String(request.actorLocked.actorId);
const requestIdStr = request._id.toString ? const requestIdStr = request._id.toString ?
request._id.toString() : request._id.toString() :
String(request._id); String(request._id);
await this.updateDamageExpertStats( await this.updateDamageExpertStats(
actorIdStr, actorIdStr,
requestIdStr, requestIdStr,
@@ -3777,6 +3775,25 @@ export class RequestManagementService {
})); }));
} }
async getAllBlameRequestsV2(user: any): Promise<any> {
console.log(user)
try {
const requests = await this.blameRequestDbService.find({
parties: {
$elemMatch: {
'person.userId': new Types.ObjectId(user.sub)
}
}
}, { select: "requestNo type status blameStatus createdAt updatedAt" });
return requests;
} catch (err) {
this.logger.error("Error: ", err);
throw new InternalServerErrorException(
"Something Went Wrong",
);
}
}
/** /**
* V2: Get one blame request by id. Access allowed if current user is a party (by userId or phone) * V2: Get one blame request by id. Access allowed if current user is a party (by userId or phone)
* or the initiating field expert. For expert-initiated LINK, party access by phone is sufficient. * or the initiating field expert. For expert-initiated LINK, party access by phone is sufficient.
@@ -4562,7 +4579,7 @@ export class RequestManagementService {
const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
const companyCode = sandHubReport?.CompanyCode; const companyCode = sandHubReport?.CompanyCode;
// Try to find client by company code first (more reliable) // Try to find client by company code first (more reliable)
const client = companyCode const client = companyCode
? await this.clientService.findClientWithCompanyCode(+companyCode) ? await this.clientService.findClientWithCompanyCode(+companyCode)
@@ -4619,7 +4636,7 @@ export class RequestManagementService {
const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
const companyCode = sandHubReport?.CompanyCode; const companyCode = sandHubReport?.CompanyCode;
// Try to find client by company code first (more reliable) // Try to find client by company code first (more reliable)
const client = companyCode const client = companyCode
? await this.clientService.findClientWithCompanyCode(+companyCode) ? await this.clientService.findClientWithCompanyCode(+companyCode)
@@ -4877,7 +4894,7 @@ export class RequestManagementService {
const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName; const clientName = sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
const companyCode = sandHubReport?.CompanyCode; const companyCode = sandHubReport?.CompanyCode;
// Try to find client by company code first (more reliable) // Try to find client by company code first (more reliable)
const client = companyCode const client = companyCode
? await this.clientService.findClientWithCompanyCode(+companyCode) ? await this.clientService.findClientWithCompanyCode(+companyCode)
@@ -4941,7 +4958,7 @@ export class RequestManagementService {
// For CAR_BODY: Create expertSubmitReply // For CAR_BODY: Create expertSubmitReply
// First party is guilty by default if accident was with another car // First party is guilty by default if accident was with another car
const guiltyUserId = firstPartyUserId; const guiltyUserId = firstPartyUserId;
// Accident fields can be added separately via add-accident-fields endpoint // Accident fields can be added separately via add-accident-fields endpoint
updatePayload.$set.expertSubmitReply = { updatePayload.$set.expertSubmitReply = {
description: "Expert completed all information on-site. Awaiting signature.", description: "Expert completed all information on-site. Awaiting signature.",
@@ -5170,11 +5187,11 @@ export class RequestManagementService {
// If description is already added, next step is different // If description is already added, next step is different
const hasDescription = request.steps?.includes(StepsEnum.F_addDescription) || const hasDescription = request.steps?.includes(StepsEnum.F_addDescription) ||
request.steps?.includes(StepsEnum.S_addDescription); request.steps?.includes(StepsEnum.S_addDescription);
const currentStep = request.type === "THIRD_PARTY" const currentStep = request.type === "THIRD_PARTY"
? StepsEnum.F_addVoice ? StepsEnum.F_addVoice
: StepsEnum.F_addVoice; : StepsEnum.F_addVoice;
const nextStep = hasDescription const nextStep = hasDescription
? (request.type === "THIRD_PARTY" ? StepsEnum.AddSignatures : StepsEnum.AddSignatures) ? (request.type === "THIRD_PARTY" ? StepsEnum.AddSignatures : StepsEnum.AddSignatures)
: (request.type === "THIRD_PARTY" ? StepsEnum.F_addDescription : StepsEnum.F_addDescription); : (request.type === "THIRD_PARTY" ? StepsEnum.F_addDescription : StepsEnum.F_addDescription);
@@ -5277,25 +5294,25 @@ export class RequestManagementService {
} else { } else {
newReplyObject.description = "Expert on-site assessment"; newReplyObject.description = "Expert on-site assessment";
} }
if ('guiltyUserId' in existingReply) { if ('guiltyUserId' in existingReply) {
newReplyObject.guiltyUserId = existingReply.guiltyUserId; newReplyObject.guiltyUserId = existingReply.guiltyUserId;
} else { } else {
newReplyObject.guiltyUserId = request.firstPartyDetails?.firstPartyId; newReplyObject.guiltyUserId = request.firstPartyDetails?.firstPartyId;
} }
if ('submitTime' in existingReply) { if ('submitTime' in existingReply) {
newReplyObject.submitTime = existingReply.submitTime; newReplyObject.submitTime = existingReply.submitTime;
} else { } else {
newReplyObject.submitTime = new Date(); newReplyObject.submitTime = new Date();
} }
if ('firstPartyComment' in existingReply) { if ('firstPartyComment' in existingReply) {
newReplyObject.firstPartyComment = existingReply.firstPartyComment; newReplyObject.firstPartyComment = existingReply.firstPartyComment;
} else { } else {
newReplyObject.firstPartyComment = null; newReplyObject.firstPartyComment = null;
} }
if ('secondPartyComment' in existingReply) { if ('secondPartyComment' in existingReply) {
newReplyObject.secondPartyComment = existingReply.secondPartyComment; newReplyObject.secondPartyComment = existingReply.secondPartyComment;
} else { } else {

View File

@@ -51,7 +51,7 @@ import { RequestManagementService } from "./request-management.service";
export class RequestManagementV2Controller { export class RequestManagementV2Controller {
constructor( constructor(
private readonly requestManagementService: RequestManagementService, private readonly requestManagementService: RequestManagementService,
) {} ) { }
@Post() @Post()
@UseGuards(GlobalGuard) @UseGuards(GlobalGuard)
@@ -65,6 +65,14 @@ export class RequestManagementV2Controller {
); );
} }
@Get()
@Roles(RoleEnum.USER, RoleEnum.FIELD_EXPERT)
async getAllBlameRequestsV2(
@CurrentUser() user: any,
) {
return this.requestManagementService.getAllBlameRequestsV2(user);
}
/** /**
* Get one blame request by id. Allowed for the request owner (party) or the initiating field expert. * Get one blame request by id. Allowed for the request owner (party) or the initiating field expert.
*/ */
@@ -324,10 +332,10 @@ export class RequestManagementV2Controller {
@UploadedFile() sign: Express.Multer.File, @UploadedFile() sign: Express.Multer.File,
) { ) {
// Convert string "true"/"false" to boolean // Convert string "true"/"false" to boolean
const acceptDecision = typeof isAccept === "string" const acceptDecision = typeof isAccept === "string"
? isAccept === "true" ? isAccept === "true"
: Boolean(isAccept); : Boolean(isAccept);
return this.requestManagementService.userSignDecisionV2( return this.requestManagementService.userSignDecisionV2(
requestId, requestId,
user.sub, user.sub,