first commit
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user