first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
FindSchedulesParams,
|
||||
ISchedulesAdapter,
|
||||
ISchedule,
|
||||
CreateScheduleParams,
|
||||
UpdateScheduleParams,
|
||||
PaginateSchedulesParams,
|
||||
PaginateSchedulesResults,
|
||||
} from "./Schedules.Interface";
|
||||
import { Document, Model, Schema, model } from "mongoose";
|
||||
|
||||
export interface IScheduleDocument extends Omit<ISchedule, "id">, Document {}
|
||||
|
||||
export class SchedulesAdapterMongoose implements ISchedulesAdapter {
|
||||
schema: Schema;
|
||||
schedulesList: Model<IScheduleDocument>;
|
||||
|
||||
constructor() {
|
||||
this.schema = new Schema({
|
||||
employeeId: { type: Schema.Types.ObjectId, required: true, ref: "Employee" },
|
||||
companyId: { type: Schema.Types.ObjectId, required: true, ref: "Companie" },
|
||||
schedules: { type: Array, required: true },
|
||||
weekDay: { type: Number, required: true },
|
||||
creationDate: { type: Date, required: true, default: Date.now },
|
||||
});
|
||||
|
||||
this.schedulesList = model<IScheduleDocument>("Schedule", this.schema);
|
||||
}
|
||||
|
||||
public async create(data: CreateScheduleParams): Promise<ISchedule> {
|
||||
return await this.schedulesList.create(data);
|
||||
}
|
||||
|
||||
public async update(data: UpdateScheduleParams): Promise<void> {
|
||||
const updateSchedule = await this.schedulesList.findOne({
|
||||
companyId: data.companyId,
|
||||
employeeId: data.employeeId,
|
||||
weekDay: data.weekDay,
|
||||
});
|
||||
|
||||
if (!updateSchedule) {
|
||||
throw new Error("Schedule not found");
|
||||
}
|
||||
|
||||
if (data.schedules) {
|
||||
updateSchedule.schedules = data.schedules;
|
||||
}
|
||||
|
||||
updateSchedule.save();
|
||||
}
|
||||
|
||||
public async delete(id: string): Promise<void> {
|
||||
await this.schedulesList.deleteOne({ _id: id }).exec();
|
||||
}
|
||||
|
||||
public async find(filters: FindSchedulesParams): Promise<IScheduleDocument[]> {
|
||||
return this.schedulesList.find(filters).exec();
|
||||
}
|
||||
|
||||
public async findOne(filters: FindSchedulesParams): Promise<IScheduleDocument | null> {
|
||||
return this.schedulesList.findOne(filters).exec();
|
||||
}
|
||||
|
||||
public async paginate(filters: PaginateSchedulesParams): Promise<PaginateSchedulesResults> {
|
||||
const { page, limit, ...findFilters } = filters; //Extract page and limit from filters
|
||||
const count = await this.schedulesList.countDocuments(findFilters).exec();
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const results = await this.schedulesList
|
||||
.find(findFilters)
|
||||
.skip(skip)
|
||||
.limit(filters.limit)
|
||||
.exec();
|
||||
|
||||
return {
|
||||
data: results,
|
||||
page: filters.page,
|
||||
pages: Math.ceil(count / filters.limit),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { IScheduleDocument } from "./Schedules.Adapter.Mongoose";
|
||||
|
||||
export type ScheduleItem = {
|
||||
from: string;
|
||||
to: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
export type FindSchedulesParams = {
|
||||
_id?: string;
|
||||
companyId?: string;
|
||||
employeeId?: string;
|
||||
weekDay?: number;
|
||||
};
|
||||
|
||||
export type AvailableSchedulesParams = {
|
||||
companyId: string;
|
||||
employeeId: string;
|
||||
weekDay: number;
|
||||
from: string;
|
||||
to: string;
|
||||
};
|
||||
|
||||
export type PaginateSchedulesParams = FindSchedulesParams & {
|
||||
page: number;
|
||||
limit: number;
|
||||
};
|
||||
|
||||
export type PaginateSchedulesResults = {
|
||||
data: ISchedule[];
|
||||
page: number;
|
||||
pages: number;
|
||||
};
|
||||
|
||||
export type CreateScheduleParams = {
|
||||
companyId: string;
|
||||
employeeId: string;
|
||||
schedules: Array<ScheduleItem>;
|
||||
weekDay: number;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type UpdateScheduleParams = {
|
||||
companyId: string;
|
||||
employeeId: string;
|
||||
schedules: Array<ScheduleItem>;
|
||||
weekDay: number;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type DeleteScheduleParams = {
|
||||
id: string;
|
||||
companyId: string;
|
||||
sessionUser: string;
|
||||
};
|
||||
|
||||
export type DeleteScheulesByCompanyParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export type DeleteSchedulesByEmployeeParams = {
|
||||
employeeId: string;
|
||||
};
|
||||
|
||||
export interface ISchedule {
|
||||
id?: string;
|
||||
companyId: string;
|
||||
employeeId: string;
|
||||
schedules: Array<ScheduleItem>;
|
||||
weekDay: number;
|
||||
}
|
||||
|
||||
export interface CollaboratorSchedulesView {
|
||||
companyId: string;
|
||||
employeeId: string;
|
||||
scheduleId: string;
|
||||
weekDay: number;
|
||||
fullName: string;
|
||||
avatar: string;
|
||||
schedules: ScheduleItem[];
|
||||
}
|
||||
|
||||
export interface ISchedulesAdapter {
|
||||
create(data: CreateScheduleParams): Promise<ISchedule>;
|
||||
update(data: UpdateScheduleParams): Promise<void>;
|
||||
delete(id: string): Promise<void>;
|
||||
find(filters: FindSchedulesParams): Promise<ISchedule[]>;
|
||||
findOne(filters: FindSchedulesParams): Promise<IScheduleDocument | null>;
|
||||
paginate(filters: PaginateSchedulesParams): Promise<PaginateSchedulesResults>;
|
||||
}
|
||||
|
||||
export interface ISchedulesManager {
|
||||
schedules: ISchedulesAdapter;
|
||||
createSchedule(data: CreateScheduleParams): Promise<ISchedule>;
|
||||
updateSchedule(data: UpdateScheduleParams): Promise<void>;
|
||||
find(data: FindSchedulesParams): Promise<ISchedule[]>;
|
||||
findAllSchedulesByCollaborator(data: FindSchedulesParams): Promise<CollaboratorSchedulesView>;
|
||||
delete(data: DeleteScheduleParams): Promise<void>;
|
||||
isAvailable(data: AvailableSchedulesParams): Promise<boolean>;
|
||||
deleteSchedulesByCompany(data: DeleteScheulesByCompanyParams): Promise<void>;
|
||||
deleteSchedulesByEmployee(data: DeleteSchedulesByEmployeeParams): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
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 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 (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,
|
||||
});
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user