feat: introduce service-based availability filtering for collaborator schedules and overrides

This commit is contained in:
2026-07-19 23:36:18 -03:00
parent f017f7e9aa
commit b01149a19e
10 changed files with 493 additions and 31 deletions
+37 -3
View File
@@ -70,7 +70,7 @@ import { DiscountType } from "../Discounts/Discounts.Interface";
import Discounts from "../Discounts/Discounts";
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
import Templates from "../Templates/Templates";
import { CollaboratorSchedulesView } from "../Schedules/Schedules.Interface";
import { CollaboratorSchedulesView, ScheduleItem } from "../Schedules/Schedules.Interface";
import ClientAccount from "../ClientAccounts/ClientAccount";
import SchedulesDisabledList from "../SchedulesDisabled/SchedulesDisabled";
import DiscountsEmail from "../DiscountsEmail/DiscountsEmail";
@@ -89,6 +89,14 @@ class AppointmentManager implements IAppointmentsManager {
this.Appointments = new AppointmentsAdapterMongoose();
}
private isScheduleAvailableForService(schedule: ScheduleItem, serviceId?: string): boolean {
if (!serviceId || !schedule.serviceScope || schedule.serviceScope === "all") {
return true;
}
return schedule.serviceScope === "specific" && (schedule.serviceIds || []).includes(serviceId);
}
public async setPaymentStatus(data: SetPaymentStatusParams): Promise<void> {
const sessionUser = await UsersManager.users.findOne({
_id: data.sessionUser,
@@ -935,6 +943,7 @@ class AppointmentManager implements IAppointmentsManager {
dateDay: dayjs(data.start).startOf("day").toDate(),
from: checkFrom,
to: checkTo,
serviceId: data.serviceId,
});
} else {
//chequear que el colaborador este disponible.
@@ -944,6 +953,7 @@ class AppointmentManager implements IAppointmentsManager {
weekDay: weekDay,
from: checkFrom,
to: checkTo,
serviceId: data.serviceId,
});
}
@@ -2289,6 +2299,10 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
continue;
}
if (!this.isScheduleAvailableForService(schedule, data.serviceId)) {
continue;
}
const scheduleDataFrom = schedule.from.split(":");
const scheduleDataTo = schedule.to.split(":");
const hourFrom = parseInt(scheduleDataFrom[0]);
@@ -2761,7 +2775,17 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
date: currentDate.toDate(),
});
if (hasOverride && hasOverride.schedules.length > 0) {
if (
hasOverride &&
hasOverride.schedules.some(
(schedule) =>
!schedule.disabled &&
(!schedule.serviceScope ||
schedule.serviceScope === "all" ||
(schedule.serviceScope === "specific" &&
(schedule.serviceIds || []).includes(data.serviceId)))
)
) {
returnAvailableDates.push(currentDate.toDate());
} else if (!hasOverride) {
// Check if the current date is within any enabled schedule range
@@ -2784,7 +2808,17 @@ public async updateAppointment(data: UpdateAppointmentParams): Promise<void> {
weekDay: currentDate.day(),
});
if (schedule && schedule.schedules.length > 0) {
if (
schedule &&
schedule.schedules.some(
(scheduleItem) =>
!scheduleItem.disabled &&
(!scheduleItem.serviceScope ||
scheduleItem.serviceScope === "all" ||
(scheduleItem.serviceScope === "specific" &&
(scheduleItem.serviceIds || []).includes(data.serviceId)))
)
) {
returnAvailableDates.push(currentDate.toDate());
}
}
@@ -4,6 +4,8 @@ export type ScheduleItem = {
from: string;
to: string;
disabled: boolean;
serviceScope?: "all" | "specific";
serviceIds?: string[];
};
export type FindSchedulesParams = {
@@ -19,6 +21,7 @@ export type AvailableSchedulesParams = {
weekDay: number;
from: string;
to: string;
serviceId?: string;
};
export type PaginateSchedulesParams = FindSchedulesParams & {
+12
View File
@@ -173,6 +173,14 @@ class SchedulesManager implements ISchedulesManager {
return await this.schedules.find(filters);
}
private isScheduleAvailableForService(schedule: ScheduleItem, serviceId?: string): boolean {
if (!serviceId || !schedule.serviceScope || schedule.serviceScope === "all") {
return true;
}
return schedule.serviceScope === "specific" && (schedule.serviceIds || []).includes(serviceId);
}
private async getScheduleByFrom(data: AvailableSchedulesParams): Promise<ScheduleItem | null> {
const schedule = await this.schedules.findOne({
employeeId: data.employeeId,
@@ -185,6 +193,9 @@ class SchedulesManager implements ISchedulesManager {
if (horario.disabled) {
continue;
}
if (!this.isScheduleAvailableForService(horario, data.serviceId)) {
continue;
}
if (horario.from == data.from) {
return horario;
}
@@ -236,6 +247,7 @@ class SchedulesManager implements ISchedulesManager {
weekDay: data.weekDay,
from: scheduleFrom.to,
to: data.to,
serviceId: data.serviceId,
});
if (!scheduleTo) {
@@ -14,6 +14,7 @@ export type AvailableSchedulesOverridesParams = {
dateDay: Date;
from: string;
to: string;
serviceId?: string;
};
export type PaginateSchedulesOverridesParams = FindSchedulesOverridesParams & {
@@ -154,6 +154,14 @@ class SchedulesOverridesManager implements ISchedulesOverridesManager {
return await this.schedulesOverrides.findOne(filters);
}
private isScheduleAvailableForService(schedule: ScheduleItem, serviceId?: string): boolean {
if (!serviceId || !schedule.serviceScope || schedule.serviceScope === "all") {
return true;
}
return schedule.serviceScope === "specific" && (schedule.serviceIds || []).includes(serviceId);
}
private async getScheduleByFrom(data: AvailableSchedulesOverridesParams): Promise<ScheduleItem | null> {
const exactDate = dayjs(data.dateDay).startOf("day").toDate();
const schedule = await this.schedulesOverrides.findOne({
@@ -165,6 +173,7 @@ class SchedulesOverridesManager implements ISchedulesOverridesManager {
if (schedule) {
for (const horario of schedule.schedules) {
if (horario.disabled) continue;
if (!this.isScheduleAvailableForService(horario, data.serviceId)) continue;
if (horario.from == data.from) return horario;
if (horario.from < data.from && horario.to > data.from) return horario;
}