Files
turnosxpress/server/src/Models/Schedules/Schedules.ts
T

367 lines
12 KiB
TypeScript

import { SchedulesAdapterMongoose } from "./Schedules.Adapter.Mongoose";
import {
CreateScheduleParams,
UpdateScheduleParams,
DeleteScheduleParams,
FindSchedulesParams,
ISchedule,
ISchedulesManager,
PaginateSchedulesParams,
PaginateSchedulesResults,
CollaboratorSchedulesView,
AvailableSchedulesParams,
DeleteScheulesByCompanyParams,
DeleteSchedulesByEmployeeParams,
ScheduleItem,
} from "./Schedules.Interface";
import { EmployeeRoles } from "../Employees/Employees.Interface";
import UsersManager from "../Users/Users";
import CompaniesManager from "../Companies/Companies";
import EmployeesList from "../Employees/Employee";
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";
import { joinStrings } from "../../helpers/String";
import { isNull } from "../../helpers/IsNull";
import getAvatar from "../../helpers/getAvatar";
import { NotificationsManager } from "../Notifications/Notifications";
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";
class SchedulesManager implements ISchedulesManager {
schedules: SchedulesAdapterMongoose;
constructor() {
this.schedules = new SchedulesAdapterMongoose();
}
public async deleteSchedulesByEmployee(data: DeleteSchedulesByEmployeeParams): Promise<void> {
await this.schedules.schedulesList.deleteMany({ employeeId: data.employeeId });
}
public async deleteSchedulesByCompany(data: DeleteScheulesByCompanyParams): Promise<void> {
await this.schedules.schedulesList.deleteMany({ companyId: data.companyId });
}
public async createSchedule(data: CreateScheduleParams): Promise<ISchedule> {
const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });
if (!sessionUser) {
throw new Error(
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
);
}
const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });
if (!companyCheck) {
throw new Error("La compañia no existe");
}
if (
!(await EmployeesList.checkPermission(
companyCheck,
data.sessionUser,
EmployeeRoles.ADMIN
))
) {
throw new Error(NoPermissionMessage());
}
const employeeCheck = await EmployeesList.employees.findOne({
companyId: data.companyId,
_id: data.employeeId,
});
if (!employeeCheck) {
throw new Error("El empleado no existe o no pertenece a la compañia.");
}
const ScheduleCheck = await this.schedules.findOne({
employeeId: data.employeeId,
companyId: data.companyId,
weekDay: data.weekDay,
});
if (ScheduleCheck) {
throw new Error("El servicio ya fue asignado para este empleado");
}
const newEmplyeeService = await this.schedules.create(data);
const strHorarios = data.schedules.map((horario) => {
return `De ${horario.from} a ${horario.to}`;
});
await NotificationsManager.sendSystemNotification({
userId: String(employeeCheck.userId),
subject: "Se han agregado horarios de trabajo",
message: `Se han registrado nuevos horarios en la organización ${companyCheck.name
}. Horarios: ${strHorarios.join(", ")}`,
type: NotificationType.SCHEDULE,
code: String(employeeCheck._id)
});
return newEmplyeeService;
}
public async updateSchedule(data: UpdateScheduleParams): Promise<void> {
const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });
if (!sessionUser) {
throw new Error(
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
);
}
const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });
if (!companyCheck) {
throw new Error("La compañia no existe");
}
const employeeCheck = await EmployeesList.employees.findOne({
companyId: data.companyId,
_id: data.employeeId,
});
if (!employeeCheck) {
throw new Error("El empleado no existe o no pertenece a la compañia.");
}
if (
!(await EmployeesList.checkPermission(
companyCheck,
data.sessionUser,
EmployeeRoles.ADMIN
))
) {
throw new Error(NoPermissionMessage());
}
const scheduleUpd = await this.schedules.findOne({
employeeId: data.employeeId,
companyId: data.companyId,
weekDay: data.weekDay,
});
if (!scheduleUpd) {
this.createSchedule(data);
return;
}
scheduleUpd.schedules = data.schedules;
await scheduleUpd.save();
const strHorarios = data.schedules.map((horario) => {
return `De ${horario.from} a ${horario.to}`;
});
await NotificationsManager.sendSystemNotification({
userId: String(employeeCheck.userId),
subject: "Se han agregado horarios de trabajo",
message: `Se han registrado nuevos horarios en la organización ${companyCheck.name
}. Horarios: ${strHorarios.join(", ")}`,
type: NotificationType.SCHEDULE,
code: String(employeeCheck._id)
});
}
public async paginate(filters: PaginateSchedulesParams): Promise<PaginateSchedulesResults> {
return await this.schedules.paginate(filters);
}
public async find(filters: FindSchedulesParams): Promise<ISchedule[]> {
return await this.schedules.find(filters);
}
private isScheduleAvailableForService(schedule: ScheduleItem, serviceId?: string): boolean {
if (!serviceId || !schedule.serviceScope || schedule.serviceScope === "all") {
return true;
}
return schedule.serviceScope === "specific" && (schedule.serviceIds || []).includes(serviceId);
}
private async getScheduleByFrom(data: AvailableSchedulesParams): Promise<ScheduleItem | null> {
const schedule = await this.schedules.findOne({
employeeId: data.employeeId,
companyId: data.companyId,
weekDay: data.weekDay,
});
if (schedule) {
for (const horario of schedule.schedules) {
if (horario.disabled) {
continue;
}
if (!this.isScheduleAvailableForService(horario, data.serviceId)) {
continue;
}
if (horario.from == data.from) {
return horario;
}
if (horario.from < data.from && horario.to > data.from) {
return horario;
}
}
}
return null;
}
private checkScheduleTo(schedule: ScheduleItem, to: string): boolean {
const toSchedule = schedule.to.split(":");
const toHoras = parseInt(toSchedule[0]);
const toMinutos = parseInt(toSchedule[1]);
const toTime = toHoras + toMinutos / 60;
const dataTo = to.split(":");
const dataToHoras = parseInt(dataTo[0]);
const dataToMinutos = parseInt(dataTo[1]);
const dataToTime = dataToHoras + dataToMinutos / 60;
// Si este rango cubre el from to del potencial turno retorna true.
if (dataToTime <= toTime) {
return true;
}
return false;
}
public async isAvailable(data: AvailableSchedulesParams): Promise<boolean> {
// Buscar un rango horario que coincida con el from.
const scheduleFrom = await this.getScheduleByFrom(data);
if (scheduleFrom) {
// Si este rango cubre el from to del potencial turno retorna true.
if (this.checkScheduleTo(scheduleFrom, data.to)) {
return true;
}
// En caso de que no lo cubra, lo que hacemos es chequear si en ese rango que comienza con el from
// es posible convinarlo con otro rango chequeando que exista un rango que comience donde termina el primero.
// Entonces podemos crear un nuevo rango que convine los dos encontrados para formar uno mas grande.
const scheduleTo = await this.getScheduleByFrom({
employeeId: data.employeeId,
companyId: data.companyId,
weekDay: data.weekDay,
from: scheduleFrom.to,
to: data.to,
serviceId: data.serviceId,
});
if (!scheduleTo) {
return false;
}
if (this.checkScheduleTo(scheduleTo, data.to)) {
return true;
}
}
return false;
}
public async findAllSchedulesByCollaborator(
filters: FindSchedulesParams
): Promise<CollaboratorSchedulesView> {
const employeeCheck = await EmployeesList.employees.findOne({
_id: filters.employeeId,
companyId: filters.companyId,
});
if (!employeeCheck) {
throw new Error("El colaborador no existe o no pertenece a la compañia.");
}
const userCheck = await UsersManager.users.findOne({
_id: employeeCheck.userId,
});
if (!userCheck) {
throw new Error("El colaborador no existe");
}
const schedule = await SchedulesList.schedules.findOne({
companyId: filters.companyId,
employeeId: filters.employeeId,
weekDay: filters.weekDay,
});
const fullName = joinStrings([userCheck.firstName, userCheck.lastName], " ");
return {
companyId: isNull<string>(filters.companyId, ""),
employeeId: isNull<string>(filters.employeeId, ""),
weekDay: isNull<number>(filters.weekDay, 1),
scheduleId: schedule ? schedule.id : "",
schedules: schedule ? schedule.schedules : [],
fullName: fullName,
avatar: getAvatar(userCheck.id, userCheck.avatar, fullName),
};
}
public async delete(data: DeleteScheduleParams): Promise<void> {
const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });
if (!sessionUser) {
throw new Error(
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
);
}
const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });
if (!companyCheck) {
throw new Error("La compañia no existe");
}
if (
!(await EmployeesList.checkPermission(
companyCheck,
data.sessionUser,
EmployeeRoles.ADMIN
))
) {
throw new Error(NoPermissionMessage());
}
const deleteItem = await this.schedules.findOne({
_id: data.id,
});
if (!deleteItem) {
throw new Error("Ha ocurrido un error al borrar el horario");
}
const emplyeeId = deleteItem.employeeId;
const employeeCheck = await EmployeesList.employees.findOne({
companyId: data.companyId,
_id: emplyeeId,
});
if (!employeeCheck) {
throw new Error("El empleado no existe o no pertenece a la compañia.");
}
const strHorarios = deleteItem.schedules.map((horario) => {
return `De ${horario.from} a ${horario.to}`;
});
await this.schedules.delete(String(deleteItem._id));
await NotificationsManager.sendSystemNotification({
userId: String(employeeCheck.userId),
subject: "Se han agregado horarios de trabajo",
message: `Se han registrado nuevos horarios en la organización ${companyCheck.name
}. Horarios: ${strHorarios.join(", ")}`,
type: NotificationType.SCHEDULE,
code: String(employeeCheck._id)
});
}
}
const SchedulesList = new SchedulesManager();
export default SchedulesList;