forked from Yara724/api
244 lines
7.6 KiB
TypeScript
244 lines
7.6 KiB
TypeScript
import { BadRequestException, Injectable } from "@nestjs/common";
|
|
import { Types } from "mongoose";
|
|
import { ClaimCaseDbService } from "src/claim-request-management/entites/db-service/claim-case.db.service";
|
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
|
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
|
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
|
import { requireActorClientKey } from "src/helpers/tenant-scope";
|
|
import {
|
|
BlameCaseStatusCountReportDtoRs,
|
|
ClaimCaseStatusCountReportDtoRs,
|
|
CompanyAllRequestsCountReportDtoRs,
|
|
} from "./dto/reports.dto";
|
|
|
|
@Injectable()
|
|
export class ReportsService {
|
|
constructor(
|
|
private readonly blameRequestDbService: BlameRequestDbService,
|
|
private readonly claimCaseDbService: ClaimCaseDbService,
|
|
) {}
|
|
|
|
private clientObjectId(client: string | undefined): Types.ObjectId {
|
|
const ck = requireActorClientKey({ clientKey: client });
|
|
return new Types.ObjectId(ck);
|
|
}
|
|
|
|
private isBlameForClient(r: any, client: Types.ObjectId): boolean {
|
|
const idStr = String(client);
|
|
return (r?.parties || []).some(
|
|
(p) => String(p?.person?.clientId || "") === idStr,
|
|
);
|
|
}
|
|
|
|
private isClaimForClient(r: any, client: Types.ObjectId): boolean {
|
|
return String(r?.owner?.userClientKey || "") === String(client);
|
|
}
|
|
|
|
private countBlameByCaseStatus(
|
|
blames: any[],
|
|
clientId: Types.ObjectId,
|
|
): Record<string, number> {
|
|
const out: Record<string, number> = { all: 0 };
|
|
for (const s of Object.values(CaseStatus)) {
|
|
out[s] = 0;
|
|
}
|
|
for (const r of blames) {
|
|
if (!this.isBlameForClient(r, clientId)) continue;
|
|
const st = r?.status as string;
|
|
if (st in out) out[st]++;
|
|
else out[st] = (out[st] ?? 0) + 1;
|
|
out.all++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
private countClaimByCaseStatus(
|
|
claims: any[],
|
|
clientId: Types.ObjectId,
|
|
): Record<string, number> {
|
|
const out: Record<string, number> = { all: 0 };
|
|
for (const s of Object.values(ClaimCaseStatus)) {
|
|
out[s] = 0;
|
|
}
|
|
for (const r of claims) {
|
|
if (!this.isClaimForClient(r, clientId)) continue;
|
|
const st = r?.status as string;
|
|
if (st in out) out[st]++;
|
|
else out[st] = (out[st] ?? 0) + 1;
|
|
out.all++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
async getAllRequestsCountByRole(role: string, client: string | undefined) {
|
|
const clientId = this.clientObjectId(client);
|
|
const [blames, claims] = await Promise.all([
|
|
this.blameRequestDbService.find({}, { lean: true }),
|
|
this.claimCaseDbService.find({}, { lean: true }),
|
|
]);
|
|
|
|
if (role === "expert") {
|
|
return this.countBlameByCaseStatus(blames as any[], clientId);
|
|
}
|
|
|
|
if (role === "damage_expert") {
|
|
return this.countClaimByCaseStatus(claims as any[], clientId);
|
|
}
|
|
|
|
if (role === "company") {
|
|
return {
|
|
blame: this.countBlameByCaseStatus(blames as any[], clientId),
|
|
claim: this.countClaimByCaseStatus(claims as any[], clientId),
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Per calendar month, counts by native `CaseStatus` / `ClaimCaseStatus`.
|
|
* Company gets both blame and claim maps; expert / damage_expert get one map.
|
|
*/
|
|
async getDateFilteredRequestByStatus(
|
|
role: string,
|
|
client: string | undefined,
|
|
start: Date,
|
|
end: Date,
|
|
) {
|
|
const clientId = this.clientObjectId(client);
|
|
const [blames, claims] = await Promise.all([
|
|
this.blameRequestDbService.find({}, { lean: true }),
|
|
this.claimCaseDbService.find({}, { lean: true }),
|
|
]);
|
|
|
|
const inRange = (r: any) => {
|
|
const createdAt = new Date(r.createdAt);
|
|
return createdAt >= start && createdAt <= end;
|
|
};
|
|
|
|
if (role === "company") {
|
|
const blameOut: Record<string, number> = { all: 0 };
|
|
const claimOut: Record<string, number> = { all: 0 };
|
|
for (const s of Object.values(CaseStatus)) blameOut[s] = 0;
|
|
for (const s of Object.values(ClaimCaseStatus)) claimOut[s] = 0;
|
|
|
|
for (const r of blames as any[]) {
|
|
if (!this.isBlameForClient(r, clientId) || !inRange(r)) continue;
|
|
const st = r?.status as string;
|
|
if (st in blameOut) blameOut[st]++;
|
|
else blameOut[st] = (blameOut[st] ?? 0) + 1;
|
|
blameOut.all++;
|
|
}
|
|
for (const r of claims as any[]) {
|
|
if (!this.isClaimForClient(r, clientId) || !inRange(r)) continue;
|
|
const st = r?.status as string;
|
|
if (st in claimOut) claimOut[st]++;
|
|
else claimOut[st] = (claimOut[st] ?? 0) + 1;
|
|
claimOut.all++;
|
|
}
|
|
return { blame: blameOut, claim: claimOut };
|
|
}
|
|
|
|
if (role === "expert") {
|
|
const out: Record<string, number> = { all: 0 };
|
|
for (const s of Object.values(CaseStatus)) out[s] = 0;
|
|
for (const r of blames as any[]) {
|
|
if (!this.isBlameForClient(r, clientId) || !inRange(r)) continue;
|
|
const st = r?.status as string;
|
|
if (st in out) out[st]++;
|
|
else out[st] = (out[st] ?? 0) + 1;
|
|
out.all++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
if (role === "damage_expert") {
|
|
const out: Record<string, number> = { all: 0 };
|
|
for (const s of Object.values(ClaimCaseStatus)) out[s] = 0;
|
|
for (const r of claims as any[]) {
|
|
if (!this.isClaimForClient(r, clientId) || !inRange(r)) continue;
|
|
const st = r?.status as string;
|
|
if (st in out) out[st]++;
|
|
else out[st] = (out[st] ?? 0) + 1;
|
|
out.all++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
throw new BadRequestException("Unsupported role for reports");
|
|
}
|
|
|
|
async getAllRequestsReportCount(actor: any, client: string | undefined) {
|
|
requireActorClientKey(actor);
|
|
|
|
if (actor.role === "damage_expert") {
|
|
const data = await this.getAllRequestsCountByRole(actor.role, client);
|
|
return new ClaimCaseStatusCountReportDtoRs(data as Record<string, number>);
|
|
}
|
|
if (actor.role === "expert") {
|
|
const data = await this.getAllRequestsCountByRole(actor.role, client);
|
|
return new BlameCaseStatusCountReportDtoRs(data as Record<string, number>);
|
|
}
|
|
const companyPayload = await this.getAllRequestsCountByRole(
|
|
"company",
|
|
client,
|
|
);
|
|
return new CompanyAllRequestsCountReportDtoRs(
|
|
(companyPayload as { blame: Record<string, number> }).blame,
|
|
(companyPayload as { claim: Record<string, number> }).claim,
|
|
);
|
|
}
|
|
|
|
async getAllRequestsReportPerMonth(actor: any, client: string | undefined) {
|
|
requireActorClientKey(actor);
|
|
const result: any[] = [];
|
|
const now = new Date();
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
const monthStart = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
|
const monthEnd = new Date(
|
|
now.getFullYear(),
|
|
now.getMonth() - i + 1,
|
|
0,
|
|
23,
|
|
59,
|
|
59,
|
|
);
|
|
const label = monthStart.toLocaleDateString("fa-IR", {
|
|
month: "long",
|
|
year: "numeric",
|
|
});
|
|
|
|
let data: any;
|
|
if (actor.role === "company") {
|
|
data = await this.getDateFilteredRequestByStatus(
|
|
"company",
|
|
client,
|
|
monthStart,
|
|
monthEnd,
|
|
);
|
|
} else {
|
|
data = await this.getDateFilteredRequestByStatus(
|
|
actor.role,
|
|
client,
|
|
monthStart,
|
|
monthEnd,
|
|
);
|
|
}
|
|
|
|
result.unshift({
|
|
stDate: monthStart,
|
|
enDate: monthEnd,
|
|
faLabel: label,
|
|
data,
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** Same aggregates as `GET /reports/report/requests` (native status keys). */
|
|
async getAllCheckedRequestsCount(actor: any, client: string | undefined) {
|
|
return this.getAllRequestsReportCount(actor, client);
|
|
}
|
|
}
|