221 lines
9.2 KiB
TypeScript
221 lines
9.2 KiB
TypeScript
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<DashboardSummaryResult> {
|
|
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<DashboardFinancialsResult> {
|
|
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<string, number>();
|
|
|
|
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<any> {
|
|
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
|
|
};
|
|
}
|
|
}
|