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 { await this.schedules.schedulesList.deleteMany({ employeeId: data.employeeId }); } public async deleteSchedulesByCompany(data: DeleteScheulesByCompanyParams): Promise { await this.schedules.schedulesList.deleteMany({ companyId: data.companyId }); } public async createSchedule(data: CreateScheduleParams): Promise { 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 { 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 { return await this.schedules.paginate(filters); } public async find(filters: FindSchedulesParams): Promise { 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 { 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 { // 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 { 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(filters.companyId, ""), employeeId: isNull(filters.employeeId, ""), weekDay: isNull(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 { 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;