forked from Yara724/api
Added registrar + fixed sign bug in car-body flow
This commit is contained in:
@@ -103,7 +103,7 @@ export class RequestManagementService {
|
||||
}
|
||||
|
||||
private async advanceWorkflowToNext(req: any, submittedStepKey: WorkflowStep) {
|
||||
// CAR_BODY: after first-party description there is no second party or expert; go to COMPLETED
|
||||
// CAR_BODY: after first-party description, move to signature step.
|
||||
if (
|
||||
req.type === BlameRequestType.CAR_BODY &&
|
||||
submittedStepKey === WorkflowStep.FIRST_DESCRIPTION
|
||||
@@ -113,11 +113,11 @@ export class RequestManagementService {
|
||||
: [];
|
||||
if (!completed.includes(submittedStepKey)) completed.push(submittedStepKey);
|
||||
req.workflow.completedSteps = completed;
|
||||
req.workflow.currentStep = WorkflowStep.COMPLETED;
|
||||
req.workflow.currentStep = WorkflowStep.WAITING_FOR_SIGNATURES;
|
||||
req.workflow.nextStep = undefined;
|
||||
req.status = CaseStatus.COMPLETED;
|
||||
req.status = CaseStatus.WAITING_FOR_SIGNATURES;
|
||||
this.logger.debug(
|
||||
"[WORKFLOW] CAR_BODY: advanced to COMPLETED after FIRST_DESCRIPTION",
|
||||
"[WORKFLOW] CAR_BODY: advanced to WAITING_FOR_SIGNATURES after FIRST_DESCRIPTION",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -3234,16 +3234,86 @@ export class RequestManagementService {
|
||||
* Verify the current user (field expert) can access this BlameRequest (v2).
|
||||
*/
|
||||
private verifyExpertAccessForBlameV2(req: any, expert: any): void {
|
||||
if (!req?.expertInitiated || !req?.initiatedByFieldExpertId) {
|
||||
throw new ForbiddenException(
|
||||
"This file is not expert-initiated or has no initiating expert",
|
||||
if (req?.expertInitiated && req?.initiatedByFieldExpertId) {
|
||||
if (String(req.initiatedByFieldExpertId) !== String(expert?.sub)) {
|
||||
throw new ForbiddenException(
|
||||
"You can only access files that you have initiated",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (req?.registrarInitiated && req?.initiatedByRegistrarId) {
|
||||
if (String(req.initiatedByRegistrarId) !== String(expert?.sub)) {
|
||||
throw new ForbiddenException(
|
||||
"You can only access files that you have initiated",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new ForbiddenException(
|
||||
"This file is not initiator-scoped or has no initiating actor",
|
||||
);
|
||||
}
|
||||
|
||||
async createRegistrarInitiatedBlame(
|
||||
registrar: any,
|
||||
dto: { type: "THIRD_PARTY" | "CAR_BODY" },
|
||||
): Promise<{ requestId: string; publicId: string }> {
|
||||
const registrarId = new Types.ObjectId(registrar.sub);
|
||||
const type =
|
||||
dto.type === "CAR_BODY"
|
||||
? BlameRequestType.CAR_BODY
|
||||
: BlameRequestType.THIRD_PARTY;
|
||||
const firstStep = await this.getWorkflowStep({ stepNumber: 1 });
|
||||
if (!firstStep?.stepKey) {
|
||||
throw new InternalServerErrorException(
|
||||
"Workflow stepNumber=1 not configured in step manager",
|
||||
);
|
||||
}
|
||||
if (String(req.initiatedByFieldExpertId) !== String(expert?.sub)) {
|
||||
throw new ForbiddenException(
|
||||
"You can only access files that you have initiated",
|
||||
const nextStepFromManager = firstStep.nextPossibleSteps?.[0];
|
||||
const nextStep =
|
||||
type === BlameRequestType.CAR_BODY
|
||||
? (WorkflowStep.CAR_BODY_ACCIDENT_TYPE as WorkflowStep)
|
||||
: (nextStepFromManager as WorkflowStep);
|
||||
if (!nextStep) {
|
||||
throw new InternalServerErrorException(
|
||||
"Workflow stepNumber=1 has no nextPossibleSteps configured",
|
||||
);
|
||||
}
|
||||
const publicId = await this.publicIdService.generateRequestPublicId();
|
||||
const parties: any[] = [{ role: PartyRole.FIRST, person: {} }];
|
||||
if (type === BlameRequestType.THIRD_PARTY) {
|
||||
parties.push({ role: PartyRole.SECOND, person: {} });
|
||||
}
|
||||
const created = await this.blameRequestDbService.create({
|
||||
publicId,
|
||||
requestNo: publicId,
|
||||
type,
|
||||
parties,
|
||||
workflow: {
|
||||
currentStep: firstStep.stepKey as WorkflowStep,
|
||||
nextStep,
|
||||
completedSteps: [firstStep.stepKey as WorkflowStep],
|
||||
locked: false,
|
||||
},
|
||||
history: [],
|
||||
registrarInitiated: true,
|
||||
initiatedByRegistrarId: registrarId,
|
||||
creationMethod: CreationMethod.IN_PERSON,
|
||||
filledBy: FilledBy.REGISTRAR,
|
||||
} as any);
|
||||
if (!Array.isArray((created as any).history)) (created as any).history = [];
|
||||
(created as any).history.push({
|
||||
type: "FILE_CREATED_BY_REGISTRAR",
|
||||
actor: {
|
||||
actorId: registrarId,
|
||||
actorName: `${registrar.firstName || ""} ${registrar.lastName || ""}`.trim(),
|
||||
actorType: "registrar",
|
||||
},
|
||||
metadata: { creationMethod: CreationMethod.IN_PERSON, type: dto.type },
|
||||
});
|
||||
await (created as any).save();
|
||||
return { requestId: String((created as any)._id), publicId: (created as any).publicId };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3596,7 +3666,10 @@ export class RequestManagementService {
|
||||
): Promise<{ sent: boolean; message: string }> {
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
if (!req.expertInitiated || req.creationMethod !== CreationMethod.IN_PERSON) {
|
||||
if (
|
||||
(!req.expertInitiated && !req.registrarInitiated) ||
|
||||
req.creationMethod !== CreationMethod.IN_PERSON
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"This endpoint is only for expert-initiated IN_PERSON blame files.",
|
||||
);
|
||||
@@ -3657,7 +3730,10 @@ export class RequestManagementService {
|
||||
): Promise<{ verified: boolean; message: string }> {
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
if (!req.expertInitiated || req.creationMethod !== CreationMethod.IN_PERSON) {
|
||||
if (
|
||||
(!req.expertInitiated && !req.registrarInitiated) ||
|
||||
req.creationMethod !== CreationMethod.IN_PERSON
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"This endpoint is only for expert-initiated IN_PERSON blame files.",
|
||||
);
|
||||
@@ -3712,7 +3788,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: expert?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
},
|
||||
metadata: {
|
||||
firstPartyVerified: true,
|
||||
@@ -3759,8 +3835,10 @@ export class RequestManagementService {
|
||||
async getMyExpertInitiatedFilesV2(expert: any): Promise<any[]> {
|
||||
const expertId = new Types.ObjectId(expert.sub);
|
||||
const files = await this.blameRequestDbService.find({
|
||||
expertInitiated: true,
|
||||
initiatedByFieldExpertId: expertId,
|
||||
$or: [
|
||||
{ expertInitiated: true, initiatedByFieldExpertId: expertId },
|
||||
{ registrarInitiated: true, initiatedByRegistrarId: expertId },
|
||||
],
|
||||
});
|
||||
return (files || []).map((f: any) => ({
|
||||
_id: f._id,
|
||||
@@ -3790,16 +3868,20 @@ export class RequestManagementService {
|
||||
req.expertInitiated &&
|
||||
req.initiatedByFieldExpertId &&
|
||||
String(req.initiatedByFieldExpertId) === String(user?.sub);
|
||||
const isRegistrarOwner =
|
||||
req.registrarInitiated &&
|
||||
req.initiatedByRegistrarId &&
|
||||
String(req.initiatedByRegistrarId) === String(user?.sub);
|
||||
const isParty = Array.isArray(req.parties) && req.parties.some((p: any) => {
|
||||
const pid = p?.person?.userId ? String(p.person.userId) : null;
|
||||
const phone = p?.person?.phoneNumber;
|
||||
return (pid && pid === String(user?.sub)) || (phone && phone === user?.username);
|
||||
});
|
||||
if (!isFieldExpertOwner && !isParty) {
|
||||
if (!isFieldExpertOwner && !isRegistrarOwner && !isParty) {
|
||||
throw new ForbiddenException("You do not have access to this request");
|
||||
}
|
||||
// Optionally bind userId to party when user opens via LINK (phone match, no userId yet)
|
||||
if (!isFieldExpertOwner && user?.sub && Types.ObjectId.isValid(user.sub)) {
|
||||
if (!isFieldExpertOwner && !isRegistrarOwner && user?.sub && Types.ObjectId.isValid(user.sub)) {
|
||||
let updated = false;
|
||||
for (const p of req.parties || []) {
|
||||
if (
|
||||
@@ -3862,7 +3944,10 @@ export class RequestManagementService {
|
||||
if (!req) {
|
||||
throw new NotFoundException("Request not found");
|
||||
}
|
||||
if (!req.expertInitiated || req.creationMethod !== CreationMethod.IN_PERSON) {
|
||||
if (
|
||||
(!req.expertInitiated && !req.registrarInitiated) ||
|
||||
req.creationMethod !== CreationMethod.IN_PERSON
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"This endpoint is only for expert-initiated IN_PERSON BlameRequest files.",
|
||||
);
|
||||
@@ -3890,7 +3975,10 @@ export class RequestManagementService {
|
||||
): Promise<any> {
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
if (!req.expertInitiated || req.creationMethod !== CreationMethod.IN_PERSON) {
|
||||
if (
|
||||
(!req.expertInitiated && !req.registrarInitiated) ||
|
||||
req.creationMethod !== CreationMethod.IN_PERSON
|
||||
) {
|
||||
throw new BadRequestException("Only expert-initiated IN_PERSON CAR_BODY files.");
|
||||
}
|
||||
if (req.type !== BlameRequestType.CAR_BODY) {
|
||||
@@ -4193,7 +4281,7 @@ export class RequestManagementService {
|
||||
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
if (!req.expertInitiated) {
|
||||
if (!req.expertInitiated && !req.registrarInitiated) {
|
||||
throw new BadRequestException("This endpoint is only for expert-initiated files.");
|
||||
}
|
||||
this.verifyExpertAccessForBlameV2(req, expert);
|
||||
|
||||
Reference in New Issue
Block a user