731 lines
26 KiB
TypeScript
731 lines
26 KiB
TypeScript
import { EmployeesAdapterMongoose } from "./Employees.Adapter.Mongoose";
|
|
import {
|
|
CreateEmployeeParams,
|
|
IEmployee,
|
|
IEmployeesManager,
|
|
EmployeeRoles,
|
|
ValidateEmployeeParams,
|
|
PaginateEmployeesParams,
|
|
PaginateEmployeesResults,
|
|
CompanyEmployeesView,
|
|
FindEmployeesParams,
|
|
FindEmployeesByIdParams,
|
|
UpdateEmployeeRolesParams,
|
|
UpdateEmployeeParams,
|
|
DeleteEmployeesByCompany,
|
|
DeleteEmployeeParams,
|
|
RemoveCollaboratorParams,
|
|
} from "./Employees.Interface";
|
|
import UsersManager from "../Users/Users";
|
|
import { ICompanyDocument } from "../Companies/Companies.Adapter.Mongoose";
|
|
import CompaniesManager from "../Companies/Companies";
|
|
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";
|
|
import { joinStrings } from "../../helpers/String";
|
|
import { isNull } from "../../helpers/IsNull";
|
|
import getAvatar from "../../helpers/getAvatar";
|
|
import { TextObjectFilterResult } from "../../Models/TextObjectFilter.model";
|
|
import MetricsList from "../../Models/Metrics/Metrics";
|
|
import { NotificationsManager } from "../Notifications/Notifications";
|
|
import EmployeesServicesList from "../EmployeesServices/EmployeesServices";
|
|
import HeatMapList from "../HeatMap/HeatMap";
|
|
import SchedulesList from "../Schedules/Schedules";
|
|
import AppointmentList from "../Appointments/Appointments";
|
|
import { IncompleteCollaboratorView } from "./Employees.Interface";
|
|
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";
|
|
|
|
class EmployeeManager implements IEmployeesManager {
|
|
employees: EmployeesAdapterMongoose;
|
|
|
|
constructor() {
|
|
this.employees = new EmployeesAdapterMongoose();
|
|
}
|
|
|
|
public async deleteEmployee(data: DeleteEmployeeParams): Promise<void> {
|
|
await this.employees.employeeList.deleteOne({ _id: data.employeeId });
|
|
}
|
|
|
|
public async deleteEmployeesByCompany(data: DeleteEmployeesByCompany): Promise<void> {
|
|
await this.employees.employeeList.deleteMany({ companyId: data.companyId });
|
|
}
|
|
|
|
public async checkRoleById(
|
|
companyId: string,
|
|
employeeId: string,
|
|
role: EmployeeRoles
|
|
): Promise<boolean> {
|
|
//Check if role exist in roles array.
|
|
const check = await this.employees.findOne({
|
|
companyId: companyId,
|
|
userId: employeeId,
|
|
removed: { $ne: true },
|
|
} as any);
|
|
|
|
if (!check) {
|
|
return false;
|
|
}
|
|
|
|
if (check && check.roles) {
|
|
return check.roles.includes(role);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async checkPermission(
|
|
company: ICompanyDocument,
|
|
employeeId: string,
|
|
role: EmployeeRoles
|
|
): Promise<boolean> {
|
|
//Si el data.sessionUser es el owner de la empresa tiene permiso.
|
|
if (String(company.ownerId) == String(employeeId)) {
|
|
return true;
|
|
}
|
|
|
|
//Si el data.sessionUser tiene el rol admin de la empresa tiene permiso.
|
|
return await this.checkRoleById(String(company._id), employeeId, role);
|
|
}
|
|
|
|
public async createEmployee(data: CreateEmployeeParams): Promise<IEmployee> {
|
|
//Primero tengo que verificar que el usuario que intenta registrar un empleado
|
|
//tenga permisos para hacerlo y que sea un usuario valido. Este dato viene de
|
|
//el campo data.sessionUser.
|
|
|
|
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 this.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
const userCheck = await UsersManager.users.findOne({ _id: data.userId });
|
|
|
|
if (!userCheck) {
|
|
throw new Error("El usuario no existe");
|
|
}
|
|
|
|
const employeeCheck = await this.employees.findOne({
|
|
companyId: data.companyId,
|
|
userId: data.userId,
|
|
});
|
|
|
|
// Reactivation: if employee exists and is removed, reactivate instead of creating duplicate
|
|
if (employeeCheck && employeeCheck.removed === true) {
|
|
// Check quota before reactivation
|
|
const canAdd = await MetricsList.canAddEmployee(companyCheck.ownerId);
|
|
if (!canAdd) {
|
|
throw new Error(
|
|
"Ha alcanzado el limite de colaboradores permitidos de acuerdo a su plan."
|
|
);
|
|
}
|
|
|
|
// Clear removal metadata
|
|
employeeCheck.removed = false;
|
|
employeeCheck.removedAt = undefined;
|
|
employeeCheck.removedBy = undefined;
|
|
|
|
// Refresh profileSnapshot from current User
|
|
employeeCheck.profileSnapshot = {
|
|
firstName: userCheck.firstName || undefined,
|
|
lastName: userCheck.lastName || undefined,
|
|
email: userCheck.email || undefined,
|
|
avatar: userCheck.avatar || undefined,
|
|
};
|
|
|
|
// Reset invitation state for full re-acceptance flow
|
|
employeeCheck.hostOk = false;
|
|
employeeCheck.guestOk = false;
|
|
|
|
await employeeCheck.save();
|
|
|
|
// Increment metrics
|
|
await MetricsList.addEmployee({
|
|
userId: companyCheck.ownerId,
|
|
quantity: 1,
|
|
});
|
|
|
|
// Send invitation notification
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.userId),
|
|
subject: "Su usuario ha sido vinculado a una organización",
|
|
message: `La compañía ${companyCheck.name} lo ha vinculado como colaborador. Es necesario que acepte la invitación para poder formar parte de esta organización.`,
|
|
type: NotificationType.EMPLOYEE,
|
|
code: String(employeeCheck._id),
|
|
});
|
|
|
|
return employeeCheck;
|
|
}
|
|
|
|
if (employeeCheck) {
|
|
if (!employeeCheck.guestOk) {
|
|
throw new Error("La invitación para este usuario ya está pendiente");
|
|
}
|
|
|
|
throw new Error("El usuario ya pertenece a la compañía");
|
|
}
|
|
|
|
const canAdd = await MetricsList.canAddEmployee(companyCheck.ownerId);
|
|
|
|
if (!canAdd) {
|
|
throw new Error(
|
|
"Ha alcanzado el limite de colaboradores permitidos de acuerdo a su plan."
|
|
);
|
|
}
|
|
|
|
const newEmplyee = await this.employees.create(data);
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.userId),
|
|
subject: "Su usuario ha sido vinculado a una organización",
|
|
message: `La compañía ${companyCheck.name} lo ha vinculado como colaborador. Es necesario que acepte la invitación para poder formar parte de esta organización.`,
|
|
type: NotificationType.EMPLOYEE,
|
|
code: String((newEmplyee as any)._id)
|
|
});
|
|
|
|
await MetricsList.addEmployee({
|
|
userId: companyCheck.ownerId,
|
|
quantity: 1,
|
|
});
|
|
|
|
return newEmplyee;
|
|
}
|
|
|
|
public async updateEmployeeRoles(data: UpdateEmployeeRolesParams): 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 checkPermission = await this.checkPermission(
|
|
companyCheck,
|
|
data.sessionUser,
|
|
EmployeeRoles.ADMIN
|
|
);
|
|
|
|
if (!checkPermission) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
const employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
if (String(employee.userId) == data.sessionUser) {
|
|
if (String(companyCheck.ownerId) != data.sessionUser) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
}
|
|
|
|
employee.roles = data.roles;
|
|
|
|
await employee.save();
|
|
}
|
|
|
|
//No exponer este metodo.
|
|
public async validateFull(data: ValidateEmployeeParams): Promise<void> {
|
|
const employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
employee.hostOk = true;
|
|
employee.guestOk = true;
|
|
|
|
await employee.save();
|
|
}
|
|
|
|
public async validateGuest(data: ValidateEmployeeParams): 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 employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
if (String(sessionUser.id) != employee.userId) {
|
|
throw new Error("Solo el destinatario puede aceptar la vinculación.");
|
|
}
|
|
|
|
employee.guestOk = true;
|
|
|
|
const checkCompany = await CompaniesManager.companies.findOne({
|
|
_id: employee.companyId,
|
|
});
|
|
|
|
if (!checkCompany) {
|
|
throw new Error("La organización no existe");
|
|
}
|
|
|
|
await employee.save();
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(employee.userId),
|
|
subject: "Has aceptado la invitación!",
|
|
message: `Ahora que haz aceptado la invitación ya estas habilitado para trabajar con ${checkCompany.name}.`,
|
|
type: NotificationType.EMPLOYEE,
|
|
code: String(employee._id)
|
|
});
|
|
}
|
|
|
|
public async rejectGuest(data: ValidateEmployeeParams): 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 employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
if (String(sessionUser.id) != employee.userId) {
|
|
throw new Error("Solo el destinatario puede rechazar la vinculación.");
|
|
}
|
|
|
|
const checkCompany = await CompaniesManager.companies.findOne({
|
|
_id: employee.companyId,
|
|
});
|
|
|
|
if (!checkCompany) {
|
|
throw new Error("La organización no existe");
|
|
}
|
|
|
|
await this.employees.employeeList.deleteOne({ _id: data.employeeId });
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(checkCompany.ownerId),
|
|
subject: "Invitación rechazada",
|
|
message: `El usuario ${UsersManager.getUserFullName(sessionUser)} ha rechazado la invitación para unirse a ${checkCompany.name}.`,
|
|
type: NotificationType.EMPLOYEE,
|
|
code: String(employee._id)
|
|
});
|
|
}
|
|
|
|
public async validateHost(data: ValidateEmployeeParams): 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 employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
//Si el data.sessionUser tiene el rol admin de la empresa tiene permiso.
|
|
const checkPermission = await this.checkRoleById(
|
|
employee.companyId,
|
|
data.sessionUser,
|
|
EmployeeRoles.ADMIN
|
|
);
|
|
|
|
if (!checkPermission) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
const employUserCheck = await UsersManager.users.findOne({
|
|
_id: employee.userId,
|
|
});
|
|
if (!employUserCheck) {
|
|
throw new Error("El usuario no existe");
|
|
}
|
|
|
|
const companyCheck = await CompaniesManager.companies.findOne({
|
|
_id: employee.companyId,
|
|
});
|
|
if (!companyCheck) {
|
|
throw new Error("La organización no existe");
|
|
}
|
|
|
|
employee.hostOk = true;
|
|
|
|
await employee.save();
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.sessionUser),
|
|
subject: "Has habilitado a un colaborador!",
|
|
message: `Se ha enviado una notificación a ${UsersManager.getUserFullName(
|
|
employUserCheck
|
|
)} para formar parte de ${companyCheck.name}.`,
|
|
type: NotificationType.EMPLOYEE,
|
|
code: String(employee._id)
|
|
});
|
|
}
|
|
|
|
public async paginate(filters: PaginateEmployeesParams): Promise<PaginateEmployeesResults> {
|
|
return await this.employees.paginate(filters);
|
|
}
|
|
|
|
public async findByCompanyId(data: FindEmployeesParams): Promise<CompanyEmployeesView[]> {
|
|
const employees = await this.employees.find({ ...data, removed: { $ne: true } } as any);
|
|
|
|
const view = await Promise.all(
|
|
employees.map(async (employee) => {
|
|
const userData = await UsersManager.users.findOne({ _id: employee.userId });
|
|
|
|
const strFirstName = userData ? isNull<string>(userData.firstName, "") : "";
|
|
const strLastName = userData ? isNull<string>(userData.lastName, "") : "";
|
|
const strFullName = joinStrings([strFirstName, strLastName], " ");
|
|
const strAvatar = userData
|
|
? getAvatar(userData.id, userData.avatar, strFullName)
|
|
: strFullName;
|
|
|
|
const boolGuestOk = employee.guestOk || false;
|
|
const boolHostOk = employee.hostOk || false;
|
|
const boolFullOk = boolGuestOk && boolHostOk;
|
|
|
|
return {
|
|
id: isNull<string>(employee.id, ""),
|
|
companyId: employee.companyId,
|
|
userId: employee.userId,
|
|
firstName: strFirstName,
|
|
lastName: strLastName,
|
|
fullName: strFullName,
|
|
avatar: strAvatar,
|
|
email: userData ? userData.email : "",
|
|
roles: employee.roles ? employee.roles : [],
|
|
guestOk: boolGuestOk,
|
|
hostOk: boolHostOk,
|
|
fullOk: boolFullOk,
|
|
calendarColor: isNull<string>(employee.calendarColor, "#ff9900"),
|
|
};
|
|
})
|
|
);
|
|
|
|
return view;
|
|
}
|
|
|
|
public async findById(data: FindEmployeesByIdParams): Promise<CompanyEmployeesView> {
|
|
const employee = await this.employees.findOne({ _id: data.id });
|
|
|
|
if (!employee) {
|
|
throw new Error("El colaborador no existe");
|
|
}
|
|
|
|
const userData = await UsersManager.users.findOne({ _id: employee.userId });
|
|
|
|
const strFirstName = userData ? isNull<string>(userData.firstName, "") : "";
|
|
const strLastName = userData ? isNull<string>(userData.lastName, "") : "";
|
|
const strFullName = joinStrings([strFirstName, strLastName], " ");
|
|
const strAvatar = userData
|
|
? getAvatar(userData.id, userData.avatar, strFullName)
|
|
: strFullName;
|
|
|
|
const boolGuestOk = employee.guestOk || false;
|
|
const boolHostOk = employee.hostOk || false;
|
|
const boolFullOk = boolGuestOk && boolHostOk;
|
|
|
|
return {
|
|
id: isNull<string>(employee.id, ""),
|
|
companyId: employee.companyId,
|
|
userId: employee.userId,
|
|
firstName: strFirstName,
|
|
lastName: strLastName,
|
|
fullName: strFullName,
|
|
avatar: strAvatar,
|
|
email: userData ? userData.email : "",
|
|
roles: employee.roles ? employee.roles : [],
|
|
guestOk: boolGuestOk,
|
|
hostOk: boolHostOk,
|
|
fullOk: boolFullOk,
|
|
calendarColor: isNull<string>(employee.calendarColor, "#ff9900"),
|
|
};
|
|
}
|
|
|
|
public async textObjectFilter(data: FindEmployeesParams): Promise<TextObjectFilterResult[]> {
|
|
const employees = await this.employees.find({ ...data, removed: { $ne: true } } as any);
|
|
const formattedResult: TextObjectFilterResult[] = [];
|
|
|
|
for (const colaborador of employees) {
|
|
const userInfo = await UsersManager.users.findOne({ _id: colaborador.userId });
|
|
|
|
if (!userInfo) {
|
|
continue;
|
|
}
|
|
|
|
const userFullName = joinStrings([userInfo.firstName, userInfo.lastName], " ");
|
|
const strAvatar = userInfo
|
|
? getAvatar(userInfo.id, userInfo.avatar, userFullName)
|
|
: userFullName;
|
|
|
|
formattedResult.push({
|
|
id: isNull<string>(colaborador.id, ""),
|
|
name: userFullName,
|
|
image: strAvatar,
|
|
});
|
|
}
|
|
|
|
return formattedResult;
|
|
}
|
|
|
|
public async update(data: UpdateEmployeeParams): 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 employee = await this.employees.findOne({
|
|
_id: data.id,
|
|
});
|
|
|
|
if (!employee) {
|
|
throw new Error("Ha ocurrido un error al validar el empleado.");
|
|
}
|
|
|
|
const companyCheck = await CompaniesManager.companies.findOne({ _id: employee.companyId });
|
|
if (!companyCheck) {
|
|
throw new Error("La organización no existe");
|
|
}
|
|
|
|
//Si el data.sessionUser tiene el rol admin de la empresa o es owner tiene permiso.
|
|
const checkPermission = await this.checkPermission(
|
|
companyCheck,
|
|
data.sessionUser,
|
|
EmployeeRoles.ADMIN
|
|
);
|
|
|
|
if (!checkPermission) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
if (data.calendarColor) {
|
|
employee.calendarColor = data.calendarColor;
|
|
}
|
|
|
|
if (data.limit) {
|
|
employee.limit = data.limit;
|
|
}
|
|
|
|
await employee.save();
|
|
}
|
|
|
|
public async getIncompleteSetup(sessionUser: string): Promise<IncompleteCollaboratorView[]> {
|
|
const incompleteCollaborators: IncompleteCollaboratorView[] = [];
|
|
const userCompanies = await CompaniesManager.getByUserId({ sessionUser });
|
|
|
|
for (const company of userCompanies) {
|
|
if (company.isAdmin || company.isOwner) {
|
|
const employees = await this.findByCompanyId({ companyId: company.id } as any);
|
|
for (const employee of employees) {
|
|
if (employee.hostOk && employee.guestOk) {
|
|
const services = await EmployeesServicesList.find({ employeeId: employee.id } as any);
|
|
const schedules = await SchedulesList.find({ employeeId: employee.id, companyId: company.id } as any);
|
|
|
|
const missingServices = services.length === 0;
|
|
const missingSchedules = schedules.length === 0;
|
|
|
|
if (missingServices || missingSchedules) {
|
|
incompleteCollaborators.push({
|
|
employeeId: employee.id,
|
|
companyId: company.id,
|
|
companyName: company.name,
|
|
name: employee.fullName,
|
|
missingServices,
|
|
missingSchedules
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return incompleteCollaborators;
|
|
}
|
|
|
|
public async removeCollaboratorFromCompany(data: RemoveCollaboratorParams): Promise<void> {
|
|
// Validate session user
|
|
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"
|
|
);
|
|
}
|
|
|
|
// Validate company exists
|
|
const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });
|
|
if (!companyCheck) {
|
|
throw new Error("La compañia no existe");
|
|
}
|
|
|
|
// Validate target employee exists
|
|
const employee = await this.employees.findOne({
|
|
_id: data.employeeId,
|
|
});
|
|
if (!employee) {
|
|
throw new Error("El colaborador no existe");
|
|
}
|
|
|
|
// Check if already removed
|
|
if (employee.removed) {
|
|
throw new Error("El colaborador ya fue eliminado de la organización");
|
|
}
|
|
|
|
// Validate permission (admin or owner)
|
|
if (
|
|
!(await this.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))
|
|
) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
// Self-removal prevention
|
|
if (String(employee.userId) === data.sessionUser) {
|
|
throw new Error("No puedes eliminarte a ti mismo de la organización");
|
|
}
|
|
|
|
// Owner protection
|
|
if (employee.roles && employee.roles.includes(EmployeeRoles.OWNER)) {
|
|
throw new Error("No se puede eliminar al propietario de la organización");
|
|
}
|
|
|
|
// Query future appointments for this employee
|
|
const futureAppointments = await AppointmentList.Appointments.AppointmentList.find({
|
|
companyId: data.companyId,
|
|
employeeId: data.employeeId,
|
|
start: { $gt: new Date() },
|
|
});
|
|
|
|
// If there are future appointments, require a replacement
|
|
if (futureAppointments && futureAppointments.length > 0) {
|
|
if (!data.replacementEmployeeId) {
|
|
throw new Error(
|
|
"El colaborador tiene turnos futuros asignados. Se requiere un empleado de reemplazo."
|
|
);
|
|
}
|
|
|
|
// Validate replacement employee
|
|
const replacementEmployee = await this.employees.findOne({
|
|
_id: data.replacementEmployeeId,
|
|
});
|
|
|
|
if (!replacementEmployee) {
|
|
throw new Error("El empleado de reemplazo no existe");
|
|
}
|
|
|
|
if (String(replacementEmployee.companyId) !== data.companyId) {
|
|
throw new Error("El empleado de reemplazo no pertenece a esta organización");
|
|
}
|
|
|
|
if (replacementEmployee.removed) {
|
|
throw new Error("El empleado de reemplazo fue eliminado de la organización");
|
|
}
|
|
|
|
if (String(replacementEmployee.userId) === data.sessionUser) {
|
|
throw new Error(
|
|
"No puedes designarte como reemplazo de ti mismo"
|
|
);
|
|
}
|
|
|
|
// Reassign future appointments
|
|
for (const appointment of futureAppointments) {
|
|
appointment.employeeId = data.replacementEmployeeId;
|
|
await appointment.save();
|
|
}
|
|
|
|
// Reassign active repeats
|
|
const RepeatsList = (await import("../Repeats/Repeats")).default;
|
|
const activeRepeats = await RepeatsList.repeats.find({
|
|
companyId: data.companyId,
|
|
employeeId: data.employeeId,
|
|
});
|
|
|
|
for (const repeat of activeRepeats) {
|
|
// Access the underlying Mongoose model to get save() capability
|
|
await RepeatsList.repeats.findOne({ _id: repeat.id }).then(async (doc) => {
|
|
if (doc) {
|
|
doc.employeeId = data.replacementEmployeeId!;
|
|
await doc.save();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Snapshot user profile
|
|
const targetUser = await UsersManager.users.findOne({ _id: employee.userId });
|
|
if (targetUser) {
|
|
employee.profileSnapshot = {
|
|
firstName: targetUser.firstName || undefined,
|
|
lastName: targetUser.lastName || undefined,
|
|
email: targetUser.email || undefined,
|
|
avatar: targetUser.avatar || undefined,
|
|
};
|
|
}
|
|
|
|
// Mark employee as removed
|
|
employee.removed = true;
|
|
employee.removedAt = new Date();
|
|
employee.removedBy = data.sessionUser;
|
|
|
|
await employee.save();
|
|
|
|
// Cleanup: delete employee services and heatmap data
|
|
await EmployeesServicesList.deleteEmployeeServiceByEmployee({
|
|
employeeId: data.employeeId,
|
|
});
|
|
|
|
await HeatMapList.deleteHeatMapByEmployee({
|
|
employeeId: data.employeeId,
|
|
});
|
|
|
|
// Decrement metrics
|
|
await MetricsList.addEmployee({
|
|
userId: companyCheck.ownerId,
|
|
quantity: -1,
|
|
});
|
|
}
|
|
}
|
|
|
|
export { EmployeeManager };
|
|
|
|
const EmployeesList = new EmployeeManager();
|
|
|
|
export default EmployeesList;
|