first commit

This commit is contained in:
2026-07-16 20:48:43 -03:00
commit 5696cee264
1111 changed files with 322270 additions and 0 deletions
+536
View File
@@ -0,0 +1,536 @@
import fs from "fs";
import path from "path";
import { rm } from "fs/promises";
import { ServicesAdapterMongoose } from "./Service.Adapter.Mongoose";
import {
CompanyServiceView,
CreateServiceParams,
UpdateServiceParams,
FindServicesByCompanyParams,
FindServicesByIdParams,
IService,
IServicesManager,
PaginateServicesParams,
PaginateServicesResults,
SetServiceFileParams,
PublicServicesResults,
PublicServiceView,
DeleteServicesByCompany,
SetPublishedStatusParams,
SERVICE_PUBLISHED_STATUS,
FindServicesParams,
ApplyDiscountParams,
DeleteDiscountParams,
SysAdminPaginateServicesParams,
SysAdminSetServiceBannedParams,
} from "./Service.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 { isNull } from "../../helpers/IsNull";
import getServiceImage from "../../helpers/getServiceImage";
import sharp from "sharp";
import MetricsList from "../../Models/Metrics/Metrics";
import { resizeAndCrop } from "../../helpers/resizeAndCrop";
import { TextObjectFilterResult } from "../TextObjectFilter.model";
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
import Discounts from "../Discounts/Discounts";
import { DiscountType } from "../Discounts/Discounts.Interface";
class ServiceManager implements IServicesManager {
services: ServicesAdapterMongoose;
constructor() {
this.services = new ServicesAdapterMongoose();
}
public async setPublishedStatus(data: SetPublishedStatusParams): 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 service = await this.services.findOne({ _id: data.serviceId });
if (!service) {
throw new Error("El servicio no existe");
}
const company = await CompaniesManager.companies.findOne({
_id: service.companyId,
});
if (!company) {
throw new Error("La compañia no existe");
}
if (!(await EmployeesList.checkPermission(company, data.sessionUser, EmployeeRoles.ADMIN))) {
throw new Error(NoPermissionMessage());
}
service.published = data.published;
await service.save();
}
public async deleteServicesByCompany(data: DeleteServicesByCompany): Promise<void> {
const services = await this.services.find({
companyId: data.companyId,
});
const rootDir =
process.env.NODE_ENV !== "production"
? process.env.UPLOAD_SERVICE_FILES_ROOT_DEV
: process.env.UPLOAD_SERVICE_FILES_ROOT_PRO;
if (!rootDir) {
throw new Error("No se ha encontrado la ruta de los archivos");
}
for (const service of services) {
if (!service.id) {
continue;
}
const serviceDir = path.join(__dirname, rootDir, service.id);
try {
await rm(serviceDir, { recursive: true, force: true });
} catch (err) {
console.error(`Error al eliminar el directorio ${serviceDir}:`, err);
}
}
await this.services.serviceList.deleteMany({
companyId: data.companyId,
});
}
public async createService(data: CreateServiceParams): Promise<IService> {
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 checkService = await this.services.findOne({
name: data.name,
companyId: data.companyId,
});
if (checkService) {
throw new Error("Ya existe un servicio con ese nombre");
}
const canAdd = await MetricsList.canAddService(companyCheck.ownerId);
if (!canAdd) {
throw new Error("Ha alcanzado el limite de servicios permitidos de acuerdo a su plan.");
}
const newService = await this.services.create({
...data,
...{
categoryId: companyCheck.categoryId,
block: companyCheck.block,
city: companyCheck.city,
state: companyCheck.state,
country: companyCheck.country,
zipCode: companyCheck.zipCode,
latitude: companyCheck.latitude,
longitude: companyCheck.longitude,
originalPrice: data.originalPrice,
price: data.originalPrice,
},
});
await MetricsList.addService({
userId: companyCheck.ownerId,
quantity: 1,
});
return newService;
}
public async updateService(data: UpdateServiceParams): 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());
}
await this.services.update(data);
}
public async applyDiscount(data: ApplyDiscountParams): Promise<void> {
await validateSessionUser({ sessionUser: data.sessionUser });
await validatePermissionsByCompany({
companyId: data.companyId,
sessionUser: data.sessionUser,
});
const service = await this.services.findOne({
_id: data.serviceId,
companyId: data.companyId,
});
if (!service) {
throw new Error("El servicio no existe");
}
const discount = await Discounts.discounts.discountsList.findOne({
_id: data.discountId,
companyId: data.companyId,
});
if (!discount) {
throw new Error("El descuento no existe");
}
if (discount.type == DiscountType.FIXED) {
service.price = service.originalPrice - discount.value;
} else if (discount.type == DiscountType.PERCENTAGE) {
service.price = service.originalPrice - service.originalPrice * (discount.value / 100);
}
service.discountId = data.discountId;
await service.save();
}
public async deleteDiscount(data: DeleteDiscountParams): Promise<void> {
await validateSessionUser({ sessionUser: data.sessionUser });
await validatePermissionsByCompany({
companyId: data.companyId,
sessionUser: data.sessionUser,
});
const service = await this.services.findOne({
_id: data.serviceId,
companyId: data.companyId,
});
if (!service) {
throw new Error("El servicio no existe");
}
service.discountId = undefined;
service.price = service.originalPrice;
await service.save();
}
public async paginate(filters: PaginateServicesParams): Promise<PaginateServicesResults> {
return await this.services.paginate(filters);
}
public async findByCompanyId(data: FindServicesByCompanyParams): Promise<CompanyServiceView[]> {
const returnValue: CompanyServiceView[] = [];
const companyServices = await this.services.find({
companyId: data.companyId,
});
const defColor = "#" + String(process.env.DEFAULT_SERVICE_COLOR);
const defFontColor = "#" + String(process.env.DEFAULT_SERVICE_FONT_COLOR);
const defFontShadowColor = "#" + String(process.env.DEFAULT_SERVICE_FONT_SHADOW_COLOR);
for (const service of companyServices) {
let discountId = "";
let discountName = "";
let discountCode = "";
let discountType = "";
let discountValue = 0;
if (service.discountId) {
const discount = await Discounts.discounts.discountsList.findOne({
_id: String(service.discountId),
companyId: data.companyId,
});
if (discount) {
discountId = discount.id;
discountName = discount.name;
discountCode = discount.code;
discountType = discount.type;
discountValue = discount.value;
}
}
returnValue.push({
id: isNull<string>(service.id, ""),
name: service.name,
description: service.description,
length: service.length,
price: service.price,
originalPrice: service.originalPrice,
discountId: discountId,
discountName: discountName,
discountCode: discountCode,
discountType: discountType,
discountValue: discountValue,
limit: service.limit,
image: getServiceImage(isNull<string>(service.id, ""), isNull<string>(service.image, ""), ""),
color: isNull<string>(service.color, defColor),
fontColor: isNull<string>(service.fontColor, defFontColor),
fontShadowColor: isNull<string>(service.fontShadowColor, defFontShadowColor),
published: isNull<SERVICE_PUBLISHED_STATUS>(
service.published,
SERVICE_PUBLISHED_STATUS.PRIVATE
),
});
}
return returnValue;
}
public async findById(data: FindServicesByIdParams): Promise<CompanyServiceView> {
const service = await this.services.findOne({ _id: data.serviceId });
if (!service) {
throw new Error("El servicio no existe");
}
const defColor = "#" + String(process.env.DEFAULT_SERVICE_COLOR);
const defFontColor = "#" + String(process.env.DEFAULT_SERVICE_FONT_COLOR);
const defFontShadowColor = "#" + String(process.env.DEFAULT_SERVICE_FONT_SHADOW_COLOR);
let discountId = "";
let discountName = "";
let discountCode = "";
let discountType = "";
let discountValue = 0;
if (service.discountId) {
const discount = await Discounts.discounts.discountsList.findOne({
_id: String(service.discountId),
companyId: String(service.companyId),
});
if (discount) {
discountId = discount.id;
discountName = discount.name;
discountCode = discount.code;
discountType = discount.type;
discountValue = discount.value;
}
}
return {
id: isNull<string>(service.id, ""),
name: service.name,
description: service.description,
length: service.length,
price: service.price,
originalPrice: service.originalPrice,
discountId: discountId,
discountName: discountName,
discountCode: discountCode,
discountType: discountType,
discountValue: discountValue,
limit: service.limit,
image: getServiceImage(isNull<string>(service.id, ""), isNull<string>(service.image, ""), ""),
color: isNull<string>(service.color, defColor),
fontColor: isNull<string>(service.fontColor, defFontColor),
fontShadowColor: isNull<string>(service.fontShadowColor, defFontShadowColor),
published: isNull<SERVICE_PUBLISHED_STATUS>(service.published, SERVICE_PUBLISHED_STATUS.PRIVATE),
};
}
public async setServiceImage(data: SetServiceFileParams): 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 serviceCheck = await this.services.findOne({
_id: data.serviceId,
});
if (!serviceCheck) {
throw new Error("El servicio no existe");
}
if (!data.file) {
throw new Error("No se ha enviado el archivo");
}
const rootDir =
process.env.NODE_ENV !== "production"
? process.env.UPLOAD_SERVICE_FILES_ROOT_DEV
: process.env.UPLOAD_SERVICE_FILES_ROOT_PRO;
const uploadPath = path.join(__dirname, rootDir + data.serviceId);
if (!fs.existsSync(uploadPath)) {
fs.mkdirSync(uploadPath, { recursive: true });
}
const fileExtension = data.file.originalname.split(".").pop();
const newFileName = "image." + fileExtension;
const filePath = path.join(uploadPath, newFileName);
const bufferImage = await resizeAndCrop(sharp(data.file.buffer), 378, 378);
bufferImage.webp().toFile(filePath);
//await sharp(data.file.buffer).resize({ height: 150 }).webp().toFile(filePath);
serviceCheck.image = newFileName;
await serviceCheck.save();
}
public async publicServices(data: PaginateServicesParams): Promise<PublicServicesResults> {
// Only fetch services that are marked as published
data.published = SERVICE_PUBLISHED_STATUS.PUBLISHED;
const services = await this.services.paginateFiltered(data);
const formattedServices: PublicServiceView[] = [];
for (const service of services.data) {
const organization = await CompaniesManager.companies.findOne({
_id: service.companyId,
});
// Only include services whose organization is also published
if (!organization || organization.published !== "published" || organization.banned || service.banned) {
continue;
}
const defColor = "#" + String(process.env.DEFAULT_HEADER_COLOR);
const defFontColor = "#" + String(process.env.DEFAULT_HEADER_FONT_COLOR);
const defFontShadowColor = "#" + String(process.env.DEFAULT_HEADER_FONT_SHADOW_COLOR);
let discountId = "";
let discountName = "";
let discountCode = "";
let discountType = "";
let discountValue = 0;
if (service.discountId) {
const discount = await Discounts.discounts.discountsList.findOne({
_id: String(service.discountId),
companyId: String(service.companyId),
});
if (discount) {
discountId = discount.id;
discountName = discount.name;
discountCode = discount.code;
discountType = discount.type;
discountValue = discount.value;
}
}
formattedServices.push({
id: isNull<string>(service.id, ""),
name: service.name,
description: service.description,
length: service.length,
price: service.price,
originalPrice: service.originalPrice,
discountId: discountId,
discountName: discountName,
discountCode: discountCode,
discountType: discountType,
discountValue: discountValue,
limit: service.limit,
image: getServiceImage(isNull<string>(service.id, ""), isNull<string>(service.image, ""), ""),
organizationId: isNull<string>(service.companyId, ""),
organizationName: isNull<string>(organization.name, ""),
categoryId: service.categoryId,
color: isNull<string>(service.color, defColor),
fontColor: isNull<string>(service.fontColor, defFontColor),
fontShadowColor: isNull<string>(service.fontShadowColor, defFontShadowColor),
});
}
const results: PublicServicesResults = {
data: formattedServices,
page: services.page,
pages: services.pages,
};
return results;
}
public async textObjectFilter(data: FindServicesParams): Promise<TextObjectFilterResult[]> {
const services = await this.services.find(data);
const formattedResult: TextObjectFilterResult[] = [];
for (const service of services) {
formattedResult.push({
id: isNull<string>(service.id, ""),
name: service.name,
image: "",
});
}
return formattedResult;
}
public async sysAdminPaginate(data: SysAdminPaginateServicesParams): Promise<PaginateServicesResults> {
return await this.services.sysAdminPaginate(data);
}
public async sysAdminSetBanned(data: SysAdminSetServiceBannedParams): Promise<void> {
const serviceCheck = await this.services.findOne({ _id: data.id });
if (!serviceCheck) {
throw new Error("El servicio no existe");
}
await this.services.update({
id: data.id,
companyId: serviceCheck.companyId, // needed by UpdateServiceParams
banned: data.banned,
sessionUser: "" // sysadmin override
});
}
}
const ServiceList = new ServiceManager();
export default ServiceList;