Files
dh-inspeccion-v2/api-v3/src/dashboard/dashboard.service.ts
T

168 lines
6.6 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
export interface DashboardAuditItem {
id: string;
occurredAt: Date;
actorUsername: string | null;
action: string;
entityType: string | null;
entityId: string | null;
}
interface DashboardCountsRow {
totalAssets: number | string;
assetsNeedValidation: number | string;
assetsWithoutGeometry: number | string;
plannedInspections: number | string;
activeUsers: number | string;
inactiveUsers: number | string;
activeSessions: number | string;
openFindings: number | string;
awaitingCompanyResponse: number | string;
overdueCompanyResponses: number | string;
companyResponsesDueNext7Days: number | string;
awaitingVerificationSchedule: number | string;
overdueControls: number | string;
controlsNext30Days: number | string;
}
export interface DashboardUpcomingControl {
id: string;
code: string;
title: string;
nextControlOn: string;
actId: string;
actCode: string;
visitId: string;
visitCode: string;
assetId: string;
assetCode: string;
assetName: string;
}
@Injectable()
export class DashboardService {
constructor(private readonly dataSource: DataSource) {}
async summary() {
return this.dataSource.transaction('REPEATABLE READ', async (manager) => {
const [counts] = (await manager.query(`
SELECT
(SELECT COUNT(*) FROM assets WHERE information_status <> 'INACTIVE') AS "totalAssets",
(SELECT COUNT(*) FROM assets WHERE information_status NOT IN ('VALIDATED','INACTIVE')) AS "assetsNeedValidation",
(SELECT COUNT(*) FROM assets asset WHERE asset.information_status <> 'INACTIVE' AND NOT EXISTS (SELECT 1 FROM asset_geometries geometry WHERE geometry.asset_id=asset.id)) AS "assetsWithoutGeometry",
(SELECT COUNT(*) FROM inspection_visits WHERE status='PLANNED') AS "plannedInspections",
(SELECT COUNT(*) FROM users WHERE status = 'ACTIVE') AS "activeUsers",
(SELECT COUNT(*) FROM users WHERE status = 'INACTIVE') AS "inactiveUsers",
(
SELECT COUNT(*)
FROM auth_sessions
WHERE revoked_at IS NULL
AND expires_at > CURRENT_TIMESTAMP
) AS "activeSessions",
(
SELECT COUNT(*) FROM inspection_findings WHERE status = 'OPEN'
) AS "openFindings",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN' AND company_response_received_on IS NULL
) AS "awaitingCompanyResponse",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN'
AND company_response_received_on IS NULL
AND correction_due_on IS NOT NULL
AND correction_due_on < (CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date
) AS "overdueCompanyResponses",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN'
AND company_response_received_on IS NULL
AND correction_due_on BETWEEN
(CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date
AND (CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date + 7
) AS "companyResponsesDueNext7Days",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN'
AND company_response_received_on IS NOT NULL
AND next_control_on IS NULL
) AS "awaitingVerificationSchedule",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN'
AND company_response_received_on IS NOT NULL
AND next_control_on < (CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date
) AS "overdueControls",
(
SELECT COUNT(*) FROM inspection_findings
WHERE status = 'OPEN'
AND next_control_on BETWEEN
(CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date
AND (CURRENT_TIMESTAMP AT TIME ZONE 'America/Argentina/Mendoza')::date + 30
) AS "controlsNext30Days"
`)) as DashboardCountsRow[];
const recentAudit = (await manager.query(`
SELECT
event.id,
event.occurred_at AS "occurredAt",
event.actor_username AS "actorUsername",
event.action,
event.entity_type AS "entityType",
event.entity_id AS "entityId"
FROM audit_events event
ORDER BY event.occurred_at DESC, event.id DESC
LIMIT 6
`)) as DashboardAuditItem[];
const upcomingControls = (await manager.query(`
SELECT
finding.id,
finding.code,
finding.title,
finding.next_control_on AS "nextControlOn",
act.id AS "actId",
act.code AS "actCode",
visit.id AS "visitId",
visit.code AS "visitCode",
asset.id AS "assetId",
asset.code AS "assetCode",
asset.name AS "assetName"
FROM inspection_findings finding
INNER JOIN inspection_acts act ON act.id = finding.act_id
INNER JOIN inspection_visits visit ON visit.id = act.visit_id
INNER JOIN assets asset ON asset.id = finding.asset_id
WHERE finding.status = 'OPEN'
AND finding.company_response_received_on IS NOT NULL
AND finding.next_control_on IS NOT NULL
ORDER BY finding.next_control_on, finding.code
LIMIT 8
`)) as DashboardUpcomingControl[];
return {
counts: {
totalAssets: Number(counts?.totalAssets ?? 0),
assetsNeedValidation: Number(counts?.assetsNeedValidation ?? 0),
assetsWithoutGeometry: Number(counts?.assetsWithoutGeometry ?? 0),
plannedInspections: Number(counts?.plannedInspections ?? 0),
activeUsers: Number(counts?.activeUsers ?? 0),
inactiveUsers: Number(counts?.inactiveUsers ?? 0),
activeSessions: Number(counts?.activeSessions ?? 0),
openFindings: Number(counts?.openFindings ?? 0),
awaitingCompanyResponse: Number(counts?.awaitingCompanyResponse ?? 0),
overdueCompanyResponses: Number(counts?.overdueCompanyResponses ?? 0),
companyResponsesDueNext7Days: Number(counts?.companyResponsesDueNext7Days ?? 0),
awaitingVerificationSchedule: Number(counts?.awaitingVerificationSchedule ?? 0),
overdueControls: Number(counts?.overdueControls ?? 0),
controlsNext30Days: Number(counts?.controlsNext30Days ?? 0),
},
recentAudit,
upcomingControls,
generatedAt: new Date().toISOString(),
};
});
}
}