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 { const out: Record = { 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 { const out: Record = { 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 = { all: 0 }; const claimOut: Record = { 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 = { 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 = { 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); } if (actor.role === "expert") { const data = await this.getAllRequestsCountByRole(actor.role, client); return new BlameCaseStatusCountReportDtoRs(data as Record); } const companyPayload = await this.getAllRequestsCountByRole( "company", client, ); return new CompanyAllRequestsCountReportDtoRs( (companyPayload as { blame: Record }).blame, (companyPayload as { claim: Record }).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); } }