forked from Yara724/api
Merge pull request 'daghi problem fixed , 2 apis for reporting counts added' (#32) from s.hajizadeh/yara724api:main into main
Reviewed-on: Yara724/api#32
This commit is contained in:
@@ -12,8 +12,12 @@ export class ClaimPartPricing {
|
|||||||
@Prop({ type: String })
|
@Prop({ type: String })
|
||||||
partId: string;
|
partId: string;
|
||||||
|
|
||||||
@Prop({ type: String })
|
/**
|
||||||
carPartDamage?: string;
|
* Legacy string or structured `{ part, side }` from expert reply DTOs (V1/V2).
|
||||||
|
* Must be Mixed — objects cannot cast to String.
|
||||||
|
*/
|
||||||
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
|
carPartDamage?: string | { part?: string; side?: string };
|
||||||
|
|
||||||
@Prop({ type: String })
|
@Prop({ type: String })
|
||||||
typeOfDamage?: string;
|
typeOfDamage?: string;
|
||||||
@@ -27,8 +31,9 @@ export class ClaimPartPricing {
|
|||||||
@Prop({ type: MongooseSchema.Types.Mixed })
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
totalPayment?: string | number;
|
totalPayment?: string | number;
|
||||||
|
|
||||||
|
/** V1/V2: string/number legacy, or `{ option, price?, branchId? }` after expert normalization. */
|
||||||
@Prop({ type: MongooseSchema.Types.Mixed })
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
daghi?: string | number;
|
daghi?: string | number | Record<string, unknown>;
|
||||||
|
|
||||||
@Prop({ type: Boolean, default: false })
|
@Prop({ type: Boolean, default: false })
|
||||||
factorNeeded?: boolean;
|
factorNeeded?: boolean;
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import {
|
|||||||
blameCaseAccessibleToExpert,
|
blameCaseAccessibleToExpert,
|
||||||
requireActorClientKey,
|
requireActorClientKey,
|
||||||
} from "src/helpers/tenant-scope";
|
} from "src/helpers/tenant-scope";
|
||||||
|
import {
|
||||||
|
blameCaseStatusToReportBucket,
|
||||||
|
initialBlameExpertReportBuckets,
|
||||||
|
} from "src/helpers/expert-panel-status-report";
|
||||||
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
|
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
|
||||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||||
import {
|
import {
|
||||||
@@ -205,6 +209,38 @@ export class ExpertBlameService {
|
|||||||
* 2. Requests where current expert made the decision
|
* 2. Requests where current expert made the decision
|
||||||
* Does NOT show requests decided by other experts.
|
* Does NOT show requests decided by other experts.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* V2: Count blame cases for this expert’s tenant, grouped for dashboard:
|
||||||
|
* `IN_PROGRESS` = OPEN + WAITING_FOR_SECOND_PARTY; other {@link CaseStatus} keys unchanged.
|
||||||
|
*/
|
||||||
|
async getStatusReportBucketsV2(actor: any): Promise<Record<string, number>> {
|
||||||
|
requireActorClientKey(actor);
|
||||||
|
const rows = await this.blameRequestDbService.find({}, { lean: true });
|
||||||
|
const buckets = initialBlameExpertReportBuckets();
|
||||||
|
for (const doc of rows as Record<string, unknown>[]) {
|
||||||
|
if (!this.blameDocIncludedInExpertTenantReport(doc, actor)) continue;
|
||||||
|
const st = String(doc.status ?? "");
|
||||||
|
const key = blameCaseStatusToReportBucket(st);
|
||||||
|
buckets.all++;
|
||||||
|
buckets[key] = (buckets[key] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
return buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tenant + expert-initiated visibility (same as list, without queue filters). */
|
||||||
|
private blameDocIncludedInExpertTenantReport(
|
||||||
|
doc: Record<string, unknown>,
|
||||||
|
actor: { sub: string; clientKey?: string },
|
||||||
|
): boolean {
|
||||||
|
if (!blameCaseAccessibleToExpert(doc, actor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (doc.expertInitiated && doc.initiatedByFieldExpertId) {
|
||||||
|
return String(doc.initiatedByFieldExpertId) === String(actor.sub);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async findAllV2(actor: any): Promise<AllRequestDtoRsV2> {
|
async findAllV2(actor: any): Promise<AllRequestDtoRsV2> {
|
||||||
try {
|
try {
|
||||||
requireActorClientKey(actor);
|
requireActorClientKey(actor);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
Put,
|
Put,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiBearerAuth, ApiBody, ApiParam, ApiTags } from "@nestjs/swagger";
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
|
||||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||||
import { Roles } from "src/decorators/roles.decorator";
|
import { Roles } from "src/decorators/roles.decorator";
|
||||||
@@ -38,6 +38,23 @@ export class ExpertBlameV2Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("report/status-counts")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Count blame cases by grouped status bucket (tenant)",
|
||||||
|
description:
|
||||||
|
"IN_PROGRESS groups OPEN and WAITING_FOR_SECOND_PARTY. WAITING_FOR_EXPERT, WAITING_FOR_DOCUMENT_RESEND, WAITING_FOR_SIGNATURES, and terminal statuses are counted separately. Expert-initiated files count only for the initiating expert.",
|
||||||
|
})
|
||||||
|
async getStatusReportBucketsV2(@CurrentUser() actor: any) {
|
||||||
|
try {
|
||||||
|
return await this.expertBlameService.getStatusReportBucketsV2(actor);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof HttpException) throw error;
|
||||||
|
throw new InternalServerErrorException(
|
||||||
|
error instanceof Error ? error.message : "Failed to load blame status report",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
@ApiParam({ name: "id", description: "Blame case request id" })
|
@ApiParam({ name: "id", description: "Blame case request id" })
|
||||||
async findOne(@Param("id") id: string, @CurrentUser() actor: any) {
|
async findOne(@Param("id") id: string, @CurrentUser() actor: any) {
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ import {
|
|||||||
claimCaseTouchesClient,
|
claimCaseTouchesClient,
|
||||||
requireActorClientKey,
|
requireActorClientKey,
|
||||||
} from "src/helpers/tenant-scope";
|
} from "src/helpers/tenant-scope";
|
||||||
|
import {
|
||||||
|
claimCaseStatusToReportBucket,
|
||||||
|
initialClaimExpertReportBuckets,
|
||||||
|
} from "src/helpers/expert-panel-status-report";
|
||||||
import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/claim-workflow-steps.enum";
|
import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/claim-workflow-steps.enum";
|
||||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||||
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||||
@@ -2448,6 +2452,25 @@ export class ExpertClaimService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2: Count claim cases for this damage expert’s tenant, grouped for dashboard:
|
||||||
|
* `IN_PROGRESS` = user flow before submission complete (CREATED … CAPTURING_PART_DAMAGES);
|
||||||
|
* other {@link ClaimCaseStatus} keys unchanged.
|
||||||
|
*/
|
||||||
|
async getStatusReportBucketsV2(actor: any): Promise<Record<string, number>> {
|
||||||
|
const clientKey = requireActorClientKey(actor);
|
||||||
|
const rows = await this.claimCaseDbService.find({}, { lean: true });
|
||||||
|
const buckets = initialClaimExpertReportBuckets();
|
||||||
|
for (const doc of rows as Record<string, unknown>[]) {
|
||||||
|
if (!claimCaseTouchesClient(doc, clientKey)) continue;
|
||||||
|
const st = String(doc.status ?? "");
|
||||||
|
const key = claimCaseStatusToReportBucket(st);
|
||||||
|
buckets.all++;
|
||||||
|
buckets[key] = (buckets[key] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
return buckets;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2: Get claim list for damage expert
|
* V2: Get claim list for damage expert
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ class InPersonVisitV2Dto {
|
|||||||
export class ExpertClaimV2Controller {
|
export class ExpertClaimV2Controller {
|
||||||
constructor(private readonly expertClaimService: ExpertClaimService) { }
|
constructor(private readonly expertClaimService: ExpertClaimService) { }
|
||||||
|
|
||||||
|
@Get("report/status-counts")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Count claim cases by grouped status bucket (tenant)",
|
||||||
|
description:
|
||||||
|
"IN_PROGRESS groups user-phase statuses before submission is complete (CREATED through CAPTURING_PART_DAMAGES). Other keys match ClaimCaseStatus. Scoped to the insurer in the JWT.",
|
||||||
|
})
|
||||||
|
async getStatusReportBucketsV2(@CurrentUser() actor: any) {
|
||||||
|
return await this.expertClaimService.getStatusReportBucketsV2(actor);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("requests")
|
@Get("requests")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "List available claim requests for damage expert",
|
summary: "List available claim requests for damage expert",
|
||||||
|
|||||||
49
src/helpers/expert-panel-status-report.ts
Normal file
49
src/helpers/expert-panel-status-report.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
||||||
|
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
||||||
|
|
||||||
|
/** Blame: early workflow before expert queue / resend / signatures / terminals. */
|
||||||
|
const BLAME_IN_PROGRESS_STATUSES = new Set<string>([
|
||||||
|
CaseStatus.OPEN,
|
||||||
|
CaseStatus.WAITING_FOR_SECOND_PARTY,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claim: user flow before submission is complete (analog to `ClaimWorkflowStep.USER_SUBMISSION_COMPLETE`).
|
||||||
|
*/
|
||||||
|
const CLAIM_IN_PROGRESS_STATUSES = new Set<string>([
|
||||||
|
ClaimCaseStatus.CREATED,
|
||||||
|
ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
||||||
|
ClaimCaseStatus.SELECTING_OTHER_PARTS,
|
||||||
|
ClaimCaseStatus.UPLOADING_REQUIRED_DOCUMENTS,
|
||||||
|
ClaimCaseStatus.CAPTURING_PART_DAMAGES,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function blameCaseStatusToReportBucket(status: string): string {
|
||||||
|
if (BLAME_IN_PROGRESS_STATUSES.has(status)) return "IN_PROGRESS";
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function claimCaseStatusToReportBucket(status: string): string {
|
||||||
|
if (CLAIM_IN_PROGRESS_STATUSES.has(status)) return "IN_PROGRESS";
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Keys returned for blame V2 expert report (IN_PROGRESS + each native post-early status). */
|
||||||
|
export function initialBlameExpertReportBuckets(): Record<string, number> {
|
||||||
|
const out: Record<string, number> = { all: 0, IN_PROGRESS: 0 };
|
||||||
|
for (const s of Object.values(CaseStatus)) {
|
||||||
|
if (BLAME_IN_PROGRESS_STATUSES.has(s)) continue;
|
||||||
|
out[s] = 0;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Keys returned for claim V2 expert report. */
|
||||||
|
export function initialClaimExpertReportBuckets(): Record<string, number> {
|
||||||
|
const out: Record<string, number> = { all: 0, IN_PROGRESS: 0 };
|
||||||
|
for (const s of Object.values(ClaimCaseStatus)) {
|
||||||
|
if (CLAIM_IN_PROGRESS_STATUSES.has(s)) continue;
|
||||||
|
out[s] = 0;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user