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
@@ -0,0 +1,158 @@
import { Document, Model, Schema, model, FilterQuery } from "mongoose";
import {
CreatePaymentExtraParams,
FindPaymentsParams,
IPayment,
IPaymentsAdapter,
PaginatePaymentsParams,
PaginatePaymentsResults,
} from "./Payments.Interface";
import {
CLIENT_ACCOUNT_MOVEMENT_STATUS,
CLIENT_ACCOUNT_PAYMENT_METHODS,
} from "../ClientAccountMovements/ClientAccountMovements.Interface";
export interface IPaymentDocument extends Omit<IPayment, "id" | "_id">, Document {
_id: string;
}
export class PaymentsAdapterMongoose implements IPaymentsAdapter {
schema: Schema;
paymentsList: Model<IPaymentDocument>;
constructor() {
this.schema = new Schema({
clientId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Client",
},
companyId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Company",
},
employeeId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Employee",
},
serviceId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Service",
},
appointmentId: {
type: Schema.Types.ObjectId,
required: true,
ref: "Appointment",
},
amount: { type: Number, required: true, default: 0 },
status: {
type: String,
required: true,
default: CLIENT_ACCOUNT_MOVEMENT_STATUS.PENDING,
},
method: {
type: String,
required: true,
default: CLIENT_ACCOUNT_PAYMENT_METHODS.CASH,
},
description: { type: String, required: false, default: "" },
createdAt: { type: Date, required: true, default: Date.now },
updatedAt: { type: Date, required: true, default: Date.now },
});
this.paymentsList = model<IPaymentDocument>("Payment", this.schema);
}
public async create(data: CreatePaymentExtraParams): Promise<IPayment> {
return await this.paymentsList.create(data);
}
public async delete(id: string): Promise<void> {
await this.paymentsList.deleteOne({ _id: id });
}
public async find(filters: Omit<FindPaymentsParams, "sessionUser">): Promise<IPayment[]> {
const searchCriteria = this.buildSearchCriteria(filters);
return await this.paymentsList.find(searchCriteria).exec();
}
public async findOne(
filters: Omit<FindPaymentsParams, "sessionUser">
): Promise<IPaymentDocument | null> {
return await this.paymentsList.findOne(filters).exec();
}
private buildSearchCriteria(
filters: Omit<FindPaymentsParams, "sessionUser">
): FilterQuery<IPayment> {
const searchCriteria: FilterQuery<IPayment> = {};
if (filters.startDate) {
searchCriteria.createdAt = {
$gte: filters.startDate,
$lte: filters.endDate,
};
}
if (filters.status) {
searchCriteria.status = filters.status;
}
if (filters.clientIds && filters.clientIds.length > 0) {
searchCriteria.clientId = { $in: filters.clientIds };
} else if (filters.clientId) {
searchCriteria.clientId = filters.clientId;
}
if (filters.method) {
searchCriteria.method = filters.method;
}
if (filters.companyId) {
searchCriteria.companyId = filters.companyId;
}
if (filters.employeeId) {
searchCriteria.employeeId = filters.employeeId;
}
if (filters.serviceId) {
searchCriteria.serviceId = filters.serviceId;
}
if (filters.appointmentId) {
searchCriteria.appointmentId = filters.appointmentId;
}
return searchCriteria;
}
public async paginate(filters: PaginatePaymentsParams): Promise<PaginatePaymentsResults> {
const searchCriteria = this.buildSearchCriteria(filters);
const count = await this.paymentsList.countDocuments(searchCriteria).exec();
const skip = (filters.page - 1) * filters.limit;
const results = await this.paymentsList
.find(searchCriteria)
.skip(skip)
.limit(filters.limit)
.sort({ createdAt: -1 })
.exec();
let pages = Math.ceil(count / filters.limit);
if (pages < 1) {
pages = 1;
}
return {
data: results,
page: filters.page,
pages: pages,
};
}
}
@@ -0,0 +1,178 @@
import {
CLIENT_ACCOUNT_MOVEMENT_STATUS,
CLIENT_ACCOUNT_PAYMENT_METHODS,
} from "../ClientAccountMovements/ClientAccountMovements.Interface";
import { IPaymentDocument } from "./Payments.Adapter.Mongoose";
export enum PAYMENT_METHODS_CLIENT_ACCOUNT {
CLIENT_ACCOUNT = "ACCOUNT",
}
export type PAYMENT_METHODS = CLIENT_ACCOUNT_PAYMENT_METHODS | PAYMENT_METHODS_CLIENT_ACCOUNT;
export type FindPaymentsParams = {
id?: string;
clientId?: string;
clientIds?: string[];
companyId?: string;
appointmentId?: string;
employeeId?: string;
serviceId?: string;
status?: CLIENT_ACCOUNT_MOVEMENT_STATUS;
method?: PAYMENT_METHODS;
startDate?: Date;
endDate?: Date;
proccessDate?: PAYMENT_PROCCESS_DATE;
sessionUser?: string;
};
export type PaginatePaymentsParams = FindPaymentsParams & {
page: number;
limit: number;
};
export type PaginatePaymentsResults = {
data: IPayment[];
page: number;
pages: number;
};
export enum PAYMENT_PROCCESS_DATE {
NOW = "now",
APPOINTMENT = "appointment",
}
export type CreatePaymentParams = {
amount: number;
method: PAYMENT_METHODS;
status: CLIENT_ACCOUNT_MOVEMENT_STATUS;
appointmentId: string;
description?: string;
proccessDate: PAYMENT_PROCCESS_DATE;
sessionUser?: string;
};
export type CreatePaymentExtraParams = CreatePaymentParams & {
clientId: string;
companyId: string;
employeeId: string;
serviceId: string;
};
export type DeletePaymentParams = {
id: string;
sessionUser?: string;
};
export type GetAppointmentPaymentsParams = {
appointmentId: string;
sessionUser?: string;
};
export type MarkAsPayedParams = {
appointmentId: string;
method: PAYMENT_METHODS;
sessionUser?: string;
};
export interface PaginatePaymentsView {
id: string;
appointmentId: string;
appointmentDate: Date;
clientId: string;
clientName: string;
employeeId: string;
employeeName: string;
serviceId: string;
serviceName: string;
price: number;
status: CLIENT_ACCOUNT_MOVEMENT_STATUS;
method: PAYMENT_METHODS;
description: string;
createdAt: Date;
}
export type PaginateNotPayedAppointmentsParams = FindPaymentsParams & {
page: number;
limit: number;
};
export type PaginateNotPayedAppointmentsResult = {
data: PaginatePaymentsView[];
page: number;
pages: number;
};
export type PaginatePaymentsViewParams = FindPaymentsParams & {
page: number;
limit: number;
};
export type PaginatePaymentsViewResults = {
data: PaginatePaymentsView[];
page: number;
pages: number;
};
export type DeletePaymentsByCompanyParams = {
companyId: string;
};
export type DeletePaymentsByClientParams = {
clientId: string;
};
export interface IPayment {
id?: string;
_id?: string;
amount: number;
status: CLIENT_ACCOUNT_MOVEMENT_STATUS;
method: PAYMENT_METHODS;
clientId: string;
companyId: string;
employeeId: string;
serviceId: string;
appointmentId: string;
description: string;
createdAt: Date;
updatedAt: Date;
}
export interface IPaymentsAdapter {
create(data: CreatePaymentExtraParams): Promise<IPayment>;
delete(id: string): Promise<void>;
find(filters: FindPaymentsParams): Promise<IPayment[]>;
findOne(filters: FindPaymentsParams): Promise<IPaymentDocument | null>;
paginate(filters: PaginatePaymentsParams): Promise<PaginatePaymentsResults>;
}
export type NotPayedReportResult = {
items: PaginatePaymentsView[];
totalAmount: number;
};
export type PaymentsReportResult = {
items: PaginatePaymentsView[];
totalAmount: number;
};
export interface IPaymentsManager {
payments: IPaymentsAdapter;
createPayment(data: CreatePaymentParams): Promise<IPayment>;
getAppointmentPayments(datas: GetAppointmentPaymentsParams): Promise<IPayment[]>;
deletePayment(data: DeletePaymentParams): Promise<void>;
deletePaymentsByCompany(data: DeletePaymentsByCompanyParams): Promise<void>;
deletePaymentsByClient(data: DeletePaymentsByClientParams): Promise<void>;
markAsPayed(data: MarkAsPayedParams): Promise<void>;
paginatePaymentsView(data: PaginatePaymentsViewParams): Promise<PaginatePaymentsViewResults>;
paginatedPaymentsViewTotal(data: FindPaymentsParams): Promise<number>;
paginateNotPayedAppointments(
data: PaginateNotPayedAppointmentsParams
): Promise<PaginateNotPayedAppointmentsResult>;
paginatedNotPayedAppiontmentsTotal(data: FindPaymentsParams): Promise<number>;
payNotPayedAppointments(data: FindPaymentsParams): Promise<void>;
getNotPayedReport(data: FindPaymentsParams): Promise<NotPayedReportResult>;
getPaymentsReport(data: FindPaymentsParams): Promise<PaymentsReportResult>;
}
+749
View File
@@ -0,0 +1,749 @@
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";
import EmployeesList from "../Employees/Employee";
import { EmployeeRoles } from "../Employees/Employees.Interface";
import UserList from "../Users/Users";
import { IUserDocument } from "../Users/Users.Adapter.Mongoose";
import { PaymentsAdapterMongoose } from "./Payments.Adapter.Mongoose";
import {
CreatePaymentParams,
DeletePaymentParams,
DeletePaymentsByClientParams,
DeletePaymentsByCompanyParams,
FindPaymentsParams,
GetAppointmentPaymentsParams,
IPayment,
IPaymentsManager,
MarkAsPayedParams,
PaginateNotPayedAppointmentsParams,
PaginateNotPayedAppointmentsResult,
NotPayedReportResult,
PaginatePaymentsView,
PaginatePaymentsViewParams,
PaginatePaymentsViewResults,
PaymentsReportResult,
PAYMENT_METHODS_CLIENT_ACCOUNT,
PAYMENT_PROCCESS_DATE,
} from "./Payments.Interface";
import AppointmentList from "../Appointments/Appointments";
import {
CLIENT_ACCOUNT_MOVEMENT_STATUS,
CLIENT_ACCOUNT_MOVEMENT_TYPES,
CLIENT_ACCOUNT_PAYMENT_METHODS,
} from "../ClientAccountMovements/ClientAccountMovements.Interface";
import ClientsList from "../Clients/Clients";
import ServicesList from "../Services/Service";
import ClientAccount from "../ClientAccounts/ClientAccount";
import ClientAccountMovements from "../ClientAccountMovements/ClientAccountMovements";
import CashFlowBalance from "../CashFlowBalance/CashFlowBalance";
import dayjs from "dayjs";
import { FilterQuery } from "mongoose";
import { IAppointment } from "../Appointments/Appointments.Interface";
import PlanSubscriptionsList from "../PlanSubscriptions/PlanSubscriptons";
import CompaniesList from "../Companies/Companies";
import { PlanFeatures } from "../Plans/Plans.interface";
class PaymentsManager implements IPaymentsManager {
public async getPaymentsReport(
filters: FindPaymentsParams
): Promise<PaymentsReportResult> {
await this.validateSessionUser(((filters.sessionUser || "") || ""));
await this.validatePermissions(((filters.sessionUser || "") || ""), String(filters.companyId));
const results = await this.payments.find(filters);
// Sort descending by createdAt since find() in adapter doesn't sort by default
results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
const contentData: PaginatePaymentsView[] = [];
let totalAmount = 0;
for (const pago of results) {
const appointment = await AppointmentList.Appointments.findOne({
_id: pago.appointmentId,
});
if (!appointment) continue;
const client = await ClientsList.clients.findOne({
_id: appointment.clientId,
});
if (!client) continue;
const employee = await EmployeesList.employees.findOne({
_id: appointment.employeeId,
});
if (!employee) continue;
const employeeUser = await UserList.users.findOne({
_id: String(employee.userId),
});
if (!employeeUser) continue;
const service = await ServicesList.services.findOne({
_id: appointment.serviceId,
});
const clientName = await ClientsList.getClientFullName(client);
const employeeName = await UserList.getUserFullName(employeeUser);
totalAmount += pago.amount;
contentData.push({
id: pago.id || "",
appointmentId: appointment.id,
appointmentDate: appointment.start as Date,
clientId: String(client._id),
clientName: clientName || "",
employeeId: employee?.id || "",
employeeName: employeeName || "",
serviceId: service?.id || "",
serviceName: service?.name || "",
price: pago.amount,
method: pago.method || "",
status: pago.status as any,
description: pago.description || "",
createdAt: pago.createdAt as Date,
});
}
return {
items: contentData,
totalAmount
};
}
payments: PaymentsAdapterMongoose;
constructor() {
this.payments = new PaymentsAdapterMongoose();
}
public async deletePaymentsByCompany(data: DeletePaymentsByCompanyParams): Promise<void> {
await this.payments.paymentsList.deleteMany({
companyId: data.companyId,
});
}
public async deletePaymentsByClient(data: DeletePaymentsByClientParams): Promise<void> {
await this.payments.paymentsList.deleteMany({
clientId: data.clientId,
});
}
public async paginatedPaymentsViewTotal(data: FindPaymentsParams): Promise<number> {
const pagos = await this.payments.find(data);
const pagosPrice = pagos.map((pago) => pago.amount);
return pagosPrice.reduce((previous, current) => current + previous, 0);
}
public async paginatePaymentsView(
data: PaginatePaymentsViewParams
): Promise<PaginatePaymentsViewResults> {
const userSession = await this.validateSessionUser((data.sessionUser || ""));
await this.validatePermissions(String(userSession.id), String(data.companyId));
const results: Array<PaginatePaymentsView> = [];
const pagos = await this.payments.paginate(data);
for (const pago of pagos.data) {
const appointment = await AppointmentList.Appointments.findOne({
_id: pago.appointmentId,
});
if (!appointment) {
continue;
}
const client = await ClientsList.clients.findOne({
_id: appointment.clientId,
});
if (!client) {
continue;
}
const employee = await EmployeesList.employees.findOne({
_id: appointment.employeeId,
});
if (!employee) {
continue;
}
const employeeUser = await UserList.users.findOne({
_id: String(employee.userId),
});
if (!employeeUser) {
continue;
}
const service = await ServicesList.services.findOne({
_id: appointment.serviceId,
});
if (!service) {
continue;
}
if (!pago._id) {
continue;
}
results.push({
id: String(pago.id),
appointmentId: String(pago.appointmentId),
appointmentDate: appointment.dateDay,
clientId: String(pago.clientId),
clientName: ClientsList.getClientFullName(client),
employeeId: String(pago.employeeId),
employeeName: UserList.getUserFullName(employeeUser),
serviceId: String(pago.serviceId),
serviceName: service.name,
price: Number(pago.amount),
status: pago.status,
method: pago.method || "",
description: pago.description,
createdAt: pago.createdAt,
});
}
return {
data: results,
page: data.page,
pages: pagos.pages,
};
}
private async validateSessionUser(sessionUserId: string): Promise<IUserDocument> {
const sessionUser = await UserList.users.findOne({
_id: sessionUserId,
});
if (!sessionUser) {
throw new Error(
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
);
}
return sessionUser;
}
private async validatePermissions(sessionUserId: string, companyId: string): Promise<void> {
// Verificar si el usuario de sesión es un empleado con permisos de administrador
const hasAdminPermission = await EmployeesList.checkRoleById(
companyId,
sessionUserId,
EmployeeRoles.ADMIN
);
if (!hasAdminPermission) {
throw new Error(NoPermissionMessage());
}
const company = await CompaniesList.companies.findOne({
_id: companyId,
});
if (!company) {
throw new Error("No se ha encontrado la organización");
}
const canAccess = await PlanSubscriptionsList.checkFeature({
userId: String(company.ownerId),
feature: PlanFeatures.PAYMENTS,
});
if (!canAccess) {
throw new Error("La organizacion no tiene un plan que soporte el módulo de pagos");
}
}
private async getPayedAmount(appointmentId: string): Promise<number> {
const payments = await this.payments.find({
appointmentId: appointmentId,
});
const payedAmount = payments.reduce((acc, payment) => acc + payment.amount, 0);
return payedAmount;
}
public async createPayment(data: CreatePaymentParams): Promise<IPayment> {
const userSession = await this.validateSessionUser((data.sessionUser || ""));
const appointment = await AppointmentList.Appointments.findOne({
_id: data.appointmentId,
});
if (!appointment) {
throw new Error("No se encontró la reserva");
}
if (appointment.payment) {
throw new Error("La reserva ya fue pagada");
}
await this.validatePermissions(String(userSession.id), String(appointment.companyId));
const toPayAmount = appointment.price;
const payedAmount = await this.getPayedAmount(data.appointmentId);
const remainingAmount = toPayAmount - payedAmount;
if (remainingAmount <= 0) {
throw new Error("La reserva ya está pagada");
}
if (data.amount > remainingAmount) {
throw new Error("El monto del pago no puede ser mayor al monto restante");
}
if (data.amount <= 0) {
throw new Error("El monto del pago no puede ser menor o igual a 0");
}
let availableClientAccountAmount = 0;
if (data.method == PAYMENT_METHODS_CLIENT_ACCOUNT.CLIENT_ACCOUNT) {
availableClientAccountAmount = await ClientAccount.getAvailableAmount({
clientId: String(appointment.clientId),
sessionUser: (data.sessionUser || ""),
});
if (availableClientAccountAmount < data.amount) {
throw new Error("El saldo de la cuenta del cliente no es suficiente");
}
}
let proccessDate = new Date();
if (data.proccessDate == PAYMENT_PROCCESS_DATE.NOW) {
proccessDate = new Date();
} else if (data.proccessDate == PAYMENT_PROCCESS_DATE.APPOINTMENT) {
proccessDate = new Date(appointment.start);
}
const payment = await this.payments.create({
...data,
clientId: String(appointment.clientId),
companyId: String(appointment.companyId),
employeeId: String(appointment.employeeId),
serviceId: String(appointment.serviceId),
...{ createdAt: proccessDate },
});
const eventData = await AppointmentList.getAppointmentEvent({
appointmentId: data.appointmentId,
sessionUser: (data.sessionUser || ""),
});
const reservaDesc =
eventData.title +
" - " +
eventData.serviceName +
" (" +
eventData.collaboratorName +
")";
//si el pago es con cuenta de cliente, se debe descontar el dinero de la cuenta del cliente
if (data.method == PAYMENT_METHODS_CLIENT_ACCOUNT.CLIENT_ACCOUNT) {
await ClientAccountMovements.add({
clientId: String(appointment.clientId),
amount: data.amount,
description: reservaDesc,
sessionUser: (data.sessionUser || ""),
type: CLIENT_ACCOUNT_MOVEMENT_TYPES.WITHDRAWAL,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.APPROVED,
paymentMethod: CLIENT_ACCOUNT_PAYMENT_METHODS.CASH,
});
}
if (remainingAmount === data.amount) {
await AppointmentList.setPaymentStatus({
appointmentId: data.appointmentId,
status: true,
sessionUser: (data.sessionUser || ""),
});
}
//Creamos el movimiento de caja si es un pago que no viene de la cuenta corriente del cliente.
if (data.method != PAYMENT_METHODS_CLIENT_ACCOUNT.CLIENT_ACCOUNT) {
const paymentDate = dayjs(payment.createdAt);
const paymentYear = paymentDate.year();
const paymentMonth = paymentDate.month();
await CashFlowBalance.createCashFlowMovement({
amount: data.amount,
description: reservaDesc,
year: paymentYear,
month: paymentMonth,
companyId: String(appointment.companyId),
paymentId: String(payment._id),
employeeId: String(appointment.employeeId),
clientId: String(appointment.clientId),
appointmentId: String(appointment._id),
paymentMethod: data.method,
reference: "",
type: CLIENT_ACCOUNT_MOVEMENT_TYPES.DEPOSIT,
proccessDate: data.proccessDate,
sessionUser: (data.sessionUser || ""),
});
}
return payment;
}
public async markAsPayed(data: MarkAsPayedParams): Promise<void> {
const userSession = await this.validateSessionUser((data.sessionUser || ""));
const appointment = await AppointmentList.Appointments.findOne({
_id: data.appointmentId,
});
if (!appointment) {
throw new Error("No se encontró la reserva");
}
await this.validatePermissions(String(userSession.id), String(appointment.companyId));
const toPayAmount = appointment.price;
const payedAmount = await this.getPayedAmount(data.appointmentId);
const remainingAmount = toPayAmount - payedAmount;
if (remainingAmount == 0) {
throw new Error("La reserva ya está pagada");
}
await this.createPayment({
appointmentId: data.appointmentId,
amount: remainingAmount,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.APPROVED,
description: "Pago total de la reserva",
method: data.method,
proccessDate: PAYMENT_PROCCESS_DATE.NOW,
sessionUser: (data.sessionUser || ""),
});
}
public async deletePayment(data: DeletePaymentParams): Promise<void> {
await this.validateSessionUser((data.sessionUser || ""));
// Primero buscamos el movimiento por su ID
const payment = await this.payments.paymentsList.findOne({
_id: String(data.id),
});
if (!payment) {
throw new Error("No se encontró el pago");
}
await this.validatePermissions((data.sessionUser || ""), payment.companyId);
const appointment = await AppointmentList.Appointments.findOne({
_id: payment.appointmentId,
});
if (!appointment) {
throw new Error("No se encontró la reserva");
}
const toPayAmount = appointment.price;
const payedAmount = await this.getPayedAmount(payment.appointmentId);
const remainingAmount = toPayAmount - (payedAmount - payment.amount);
//si el pago es con cuenta de cliente, se debe agregar el dinero a la cuenta del cliente
if (payment.method == PAYMENT_METHODS_CLIENT_ACCOUNT.CLIENT_ACCOUNT) {
await ClientAccountMovements.add({
clientId: String(appointment.clientId),
amount: payment.amount,
description: "Reembolso de la reserva - " + appointment.id,
sessionUser: (data.sessionUser || ""),
type: CLIENT_ACCOUNT_MOVEMENT_TYPES.DEPOSIT,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.APPROVED,
paymentMethod: CLIENT_ACCOUNT_PAYMENT_METHODS.CASH,
});
}
//Si el pago que se borra no viene de la cuenta del cliente, borramos el movimiento de la caja.
if (payment.method != PAYMENT_METHODS_CLIENT_ACCOUNT.CLIENT_ACCOUNT) {
await CashFlowBalance.deleteByPayment({
companyId: String(appointment.companyId),
paymentId: String(payment._id),
sessionUser: (data.sessionUser || ""),
});
}
await this.payments.delete(data.id);
if (remainingAmount > 0) {
await AppointmentList.setPaymentStatus({
appointmentId: String(appointment._id),
status: false,
sessionUser: (data.sessionUser || ""),
});
}
}
public async getAppointmentPayments(data: GetAppointmentPaymentsParams): Promise<IPayment[]> {
await this.validateSessionUser((data.sessionUser || ""));
const appointment = await AppointmentList.Appointments.findOne({
_id: data.appointmentId,
});
if (!appointment) {
throw new Error("No se encontró la reserva");
}
await this.validatePermissions((data.sessionUser || ""), String(appointment.companyId));
return await this.payments.find({
appointmentId: data.appointmentId,
});
}
private getNotPayedSearchCriteria(filters: FindPaymentsParams): FilterQuery<IAppointment> {
const searchCriteria: FilterQuery<IAppointment> = {};
if (filters.startDate) {
searchCriteria.start = {
$gte: filters.startDate,
$lte: filters.endDate,
};
}
searchCriteria.payment = false;
if (filters.clientIds && filters.clientIds.length > 0) {
searchCriteria.clientId = { $in: filters.clientIds };
} else if (filters.clientId) {
searchCriteria.clientId = filters.clientId;
}
if (filters.method) {
searchCriteria.method = filters.method;
}
if (filters.companyId) {
searchCriteria.companyId = filters.companyId;
}
if (filters.employeeId) {
searchCriteria.employeeId = filters.employeeId;
}
if (filters.serviceId) {
searchCriteria.serviceId = filters.serviceId;
}
return searchCriteria;
}
public async paginateNotPayedAppointments(
filters: PaginateNotPayedAppointmentsParams
): Promise<PaginateNotPayedAppointmentsResult> {
await this.validateSessionUser(((filters.sessionUser || "") || ""));
await this.validatePermissions(((filters.sessionUser || "") || ""), String(filters.companyId));
const searchCriteria = this.getNotPayedSearchCriteria(filters);
const count = await AppointmentList.Appointments.AppointmentList.countDocuments(
searchCriteria
).exec();
const skip = (filters.page - 1) * filters.limit;
const results = await AppointmentList.Appointments.AppointmentList.find(searchCriteria)
.skip(skip)
.limit(filters.limit)
.sort({ start: -1 })
.exec();
const contentData: PaginatePaymentsView[] = [];
for (const item of results) {
const client = await ClientsList.clients.findOne({
_id: item.clientId,
});
const employee = await EmployeesList.employees.findOne({
_id: item.employeeId,
});
const service = await ServicesList.services.findOne({
_id: item.serviceId,
});
if (!client) {
continue;
}
if (!employee) {
continue;
}
if (!service) {
continue;
}
const employeeUser = await UserList.users.findOne({
_id: String(employee.userId),
});
if (!employeeUser) {
continue;
}
const clientName = await ClientsList.getClientFullName(client);
const employeeName = await UserList.getUserFullName(employeeUser);
contentData.push({
id: item.id,
appointmentId: item.id,
appointmentDate: item.start,
clientId: item.clientId,
clientName: clientName || "",
employeeId: item.employeeId,
employeeName: employeeName || "",
serviceId: item.serviceId,
serviceName: service.name,
price: item.price,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.PENDING,
method: CLIENT_ACCOUNT_PAYMENT_METHODS.OTHER,
description: "",
createdAt: item.start,
});
}
let pages = Math.ceil(count / filters.limit);
if (pages < 1) {
pages = 1;
}
return {
data: contentData,
page: filters.page,
pages: pages,
};
}
public async paginatedNotPayedAppiontmentsTotal(data: FindPaymentsParams): Promise<number> {
await this.validateSessionUser((data.sessionUser || ""));
await this.validatePermissions((data.sessionUser || ""), String(data.companyId));
const searchCriteria = this.getNotPayedSearchCriteria(data);
const pagos = await AppointmentList.Appointments.AppointmentList.find(searchCriteria);
return pagos.reduce((previous, current) => current.price + previous, 0);
}
public async getNotPayedReport(
filters: FindPaymentsParams
): Promise<NotPayedReportResult> {
await this.validateSessionUser(((filters.sessionUser || "") || ""));
await this.validatePermissions(((filters.sessionUser || "") || ""), String(filters.companyId));
const searchCriteria = this.getNotPayedSearchCriteria(filters);
const results = await AppointmentList.Appointments.AppointmentList.find(searchCriteria)
.sort({ start: -1 })
.exec();
const contentData: PaginatePaymentsView[] = [];
let totalAmount = 0;
for (const item of results) {
const client = await ClientsList.clients.findOne({
_id: item.clientId,
});
const employee = await EmployeesList.employees.findOne({
_id: item.employeeId,
});
const service = await ServicesList.services.findOne({
_id: item.serviceId,
});
if (!client || !employee || !service) {
continue;
}
const employeeUser = await UserList.users.findOne({
_id: String(employee.userId),
});
if (!employeeUser) {
continue;
}
const clientName = await ClientsList.getClientFullName(client);
const employeeName = await UserList.getUserFullName(employeeUser);
contentData.push({
id: item.id,
appointmentId: item.id,
appointmentDate: item.start,
clientId: item.clientId,
clientName: clientName || "",
employeeId: item.employeeId,
employeeName: employeeName || "",
serviceId: item.serviceId,
serviceName: service.name,
price: item.price,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.PENDING,
method: CLIENT_ACCOUNT_PAYMENT_METHODS.OTHER,
description: "",
createdAt: item.start,
});
totalAmount += item.price;
}
return {
items: contentData,
totalAmount: totalAmount,
};
}
public async payNotPayedAppointments(data: FindPaymentsParams): Promise<void> {
await this.validateSessionUser((data.sessionUser || ""));
await this.validatePermissions((data.sessionUser || ""), String(data.companyId));
const { method, ...searchCriteria } = this.getNotPayedSearchCriteria(data); //Estraigo la propiedad method de searchCriteria
const toPayAppointments = await AppointmentList.Appointments.AppointmentList.find(
searchCriteria
);
if (!method) {
throw new Error("No se ha especidicado un método de pago");
}
if (!data.proccessDate) {
throw new Error("No se ha especidicado una fecha de pago");
}
for (const item of toPayAppointments) {
try {
if (dayjs(item.dateDay).isAfter(dayjs())) {
continue; // No se pueden pagar reservas futuras de forma masiva.
}
await this.createPayment({
appointmentId: item.id,
method: method,
status: CLIENT_ACCOUNT_MOVEMENT_STATUS.APPROVED,
description: "Pago completo de la reserva",
amount: item.price,
proccessDate: data.proccessDate,
sessionUser: (data.sessionUser || ""),
});
} catch (e) {
console.log(e);
}
}
}
}
export default new PaymentsManager();