import { connect } from "mongoose"; import EmployeesList from "../../Models/Employees/Employee"; import { EmployeeRoles } from "../../Models/Employees/Employees.Interface"; import AppointmentList from "../../Models/Appointments/Appointments"; import { DashboardFinancialsResult, DashboardSummaryResult, GetDashboardFinancialsParams, IncomeDataPoint } from "./Dashboard.Interface"; import dayjs from "dayjs"; import { AppointmentEventByClient } from "../../Models/Appointments/Appointments.Interface"; import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check"; import CashFlowList from "../../Models/CashFlow/CashFlow"; import CompaniesList from "../../Models/Companies/Companies"; import PlanSubscriptionsList from "../../Models/PlanSubscriptions/PlanSubscriptons"; import MetricsList from "../../Models/Metrics/Metrics"; import PlanUsageCycleList from "../../Models/PlanUsageCycle/PlanUsageCycle"; export class DashboardService { public async getDashboardSummary(sessionUser: string): Promise { await connect(`${process.env.DATABASE_CONNECTION}`); await validateSessionUser({ sessionUser }); // 1. Encontrar todas las relaciones de empleado para este usuario const employeeRecords = await EmployeesList.employees.find({ userId: sessionUser }); let finalRole: "ADMIN" | "EMPLOYEE" | "NONE" = "NONE"; let allPersonalAgenda: AppointmentEventByClient[] = []; for (const emp of employeeRecords) { const isAdmin = emp.roles?.includes(EmployeeRoles.ADMIN) || false; if (isAdmin && finalRole !== "ADMIN") { finalRole = "ADMIN"; } else if (!isAdmin && finalRole === "NONE") { finalRole = "EMPLOYEE"; } const todayStart = dayjs().startOf("day").toDate(); const todayEnd = dayjs().endOf("day").toDate(); if (isAdmin) { // If ADMIN, fetch all appointments for this company for today const apps = await AppointmentList.Appointments.AppointmentList.find({ companyId: emp.companyId, start: { $gte: todayStart, $lte: todayEnd } }); for (const a of apps) { try { const evt = await AppointmentList.getAppointmentEvent({ appointmentId: String(a._id), sessionUser: sessionUser }); allPersonalAgenda.push(evt); } catch (e) { console.error(e); } } } else { // If EMPLOYEE, fetch only their appointments for today const apps = await AppointmentList.Appointments.AppointmentList.find({ companyId: emp.companyId, employeeId: String(emp.id), start: { $gte: todayStart, $lte: todayEnd } }); for (const a of apps) { try { const evt = await AppointmentList.getAppointmentEvent({ appointmentId: String(a._id), sessionUser: sessionUser }); allPersonalAgenda.push(evt); } catch (e) { console.error(e); } } } } // Sort agenda by start time allPersonalAgenda.sort((a, b) => a.startTime - b.startTime); // Note: globalMetrics is handled by the frontend useSessionStore // we just return the role and the personalAgenda return { role: finalRole, personalAgenda: allPersonalAgenda }; } public async getDashboardFinancials(params: GetDashboardFinancialsParams): Promise { await connect(`${process.env.DATABASE_CONNECTION}`); await validateSessionUser({ sessionUser: params.sessionUser }); await validatePermissionsByCompany({ companyId: params.companyId, sessionUser: params.sessionUser }); const now = dayjs(); let startDate: Date; let prevStartDate: Date; let endDate = now.toDate(); let prevEndDate: Date; if (params.timeframe === "week") { startDate = now.subtract(7, "day").startOf("day").toDate(); prevStartDate = now.subtract(14, "day").startOf("day").toDate(); prevEndDate = now.subtract(7, "day").endOf("day").toDate(); } else if (params.timeframe === "month") { startDate = now.subtract(1, "month").startOf("day").toDate(); prevStartDate = now.subtract(2, "month").startOf("day").toDate(); prevEndDate = now.subtract(1, "month").endOf("day").toDate(); } else { // year startDate = now.subtract(1, "year").startOf("month").toDate(); prevStartDate = now.subtract(2, "year").startOf("month").toDate(); prevEndDate = now.subtract(1, "year").endOf("month").toDate(); } // Fetch current period cashflow const currentMatch: any = { companyId: params.companyId, type: "deposit", createdAt: { $gte: startDate, $lte: endDate } }; if (params.employeeId) currentMatch.employeeId = params.employeeId; const currentMovements = await CashFlowList.movements.movementList.find(currentMatch); // Fetch previous period cashflow const prevMatch: any = { companyId: params.companyId, type: "deposit", createdAt: { $gte: prevStartDate, $lte: prevEndDate } }; if (params.employeeId) prevMatch.employeeId = params.employeeId; const prevMovements = await CashFlowList.movements.movementList.find(prevMatch); const currentPeriodTotal = currentMovements.reduce((sum, m) => sum + m.amount, 0); const previousPeriodTotal = prevMovements.reduce((sum, m) => sum + m.amount, 0); // Group data points const dataPointsMap = new Map(); currentMovements.forEach(m => { let key = ""; if (params.timeframe === "year") { key = dayjs(m.createdAt).format("YYYY-MM"); } else { key = dayjs(m.createdAt).format("YYYY-MM-DD"); } dataPointsMap.set(key, (dataPointsMap.get(key) || 0) + m.amount); }); // Fill empty dates for the chart const currentPeriodData: IncomeDataPoint[] = []; if (params.timeframe === "year") { for (let i = 0; i < 12; i++) { const k = dayjs(startDate).add(i, "month").format("YYYY-MM"); currentPeriodData.push({ date: k, amount: dataPointsMap.get(k) || 0 }); } } else { const days = params.timeframe === "week" ? 7 : 30; // Approx for (let i = 0; i <= days; i++) { const k = dayjs(startDate).add(i, "day").format("YYYY-MM-DD"); if (dayjs(k).isAfter(now)) break; currentPeriodData.push({ date: k, amount: dataPointsMap.get(k) || 0 }); } } // Calculate unpaid today const todayStart = now.startOf("day").toDate(); const todayEnd = now.endOf("day").toDate(); const unpaidMatch: any = { companyId: params.companyId, payment: false, start: { $gte: todayStart, $lte: todayEnd } }; if (params.employeeId) unpaidMatch.employeeId = params.employeeId; const unpaidApps = await AppointmentList.Appointments.AppointmentList.find(unpaidMatch); const unpaidToday = unpaidApps.reduce((sum, a) => sum + (a.price || 0), 0); return { currentPeriodTotal, previousPeriodTotal, currentPeriodData, unpaidToday }; } public async getCompanyMetrics(params: { sessionUser: string; companyId: string }): Promise { await connect(`${process.env.DATABASE_CONNECTION}`); await validateSessionUser({ sessionUser: params.sessionUser }); await validatePermissionsByCompany({ companyId: params.companyId, sessionUser: params.sessionUser }); const company = await CompaniesList.companies.findOne({ _id: params.companyId }); if (!company) { throw new Error("No se encontro la compaƱia."); } const ownerId = company.ownerId; const isOwner = String(ownerId) === String(params.sessionUser); // Fetch metrics for owner const metricsDocument = await MetricsList.getMetrics(String(ownerId)); const metrics = metricsDocument.toObject ? metricsDocument.toObject() : { ...metricsDocument }; // Fetch subscription for owner const subscription = await PlanSubscriptionsList.getSubscriptionByUser({ sessionUser: String(ownerId) }); if (subscription) { metrics.appointmentsCount = await PlanUsageCycleList.getAppointmentsCount({ userId: String(ownerId), }); } return { metrics, subscription, isOwner }; } }