@blackcode_sa/metaestetics-api 1.7.14 → 1.7.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +6 -0
- package/dist/admin/index.d.ts +6 -0
- package/dist/admin/index.js +127 -14
- package/dist/admin/index.mjs +127 -14
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +160 -14
package/dist/admin/index.d.mts
CHANGED
|
@@ -2417,6 +2417,12 @@ declare class BookingAdmin {
|
|
|
2417
2417
|
* @returns Promise resolving to an array of calendar events
|
|
2418
2418
|
*/
|
|
2419
2419
|
private getPractitionerCalendarEvents;
|
|
2420
|
+
/**
|
|
2421
|
+
* Summarizes event types for logging purposes
|
|
2422
|
+
* @param events Array of calendar events
|
|
2423
|
+
* @returns Object with counts of each event type
|
|
2424
|
+
*/
|
|
2425
|
+
private summarizeEventTypes;
|
|
2420
2426
|
private _generateCalendarProcedureInfo;
|
|
2421
2427
|
/**
|
|
2422
2428
|
* Orchestrates the creation of a new appointment, including data aggregation.
|
package/dist/admin/index.d.ts
CHANGED
|
@@ -2417,6 +2417,12 @@ declare class BookingAdmin {
|
|
|
2417
2417
|
* @returns Promise resolving to an array of calendar events
|
|
2418
2418
|
*/
|
|
2419
2419
|
private getPractitionerCalendarEvents;
|
|
2420
|
+
/**
|
|
2421
|
+
* Summarizes event types for logging purposes
|
|
2422
|
+
* @param events Array of calendar events
|
|
2423
|
+
* @returns Object with counts of each event type
|
|
2424
|
+
*/
|
|
2425
|
+
private summarizeEventTypes;
|
|
2420
2426
|
private _generateCalendarProcedureInfo;
|
|
2421
2427
|
/**
|
|
2422
2428
|
* Orchestrates the creation of a new appointment, including data aggregation.
|
package/dist/admin/index.js
CHANGED
|
@@ -6032,33 +6032,76 @@ var BookingAdmin = class {
|
|
|
6032
6032
|
* @returns Promise resolving to an array of available booking slots
|
|
6033
6033
|
*/
|
|
6034
6034
|
async getAvailableBookingSlots(clinicId, practitionerId, procedureId, timeframe) {
|
|
6035
|
+
var _a;
|
|
6035
6036
|
try {
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6037
|
+
Logger.info("[BookingAdmin] Starting availability calculation", {
|
|
6038
|
+
clinicId,
|
|
6039
|
+
practitionerId,
|
|
6040
|
+
procedureId,
|
|
6041
|
+
timeframeStart: timeframe.start instanceof Date ? timeframe.start.toISOString() : timeframe.start.toDate().toISOString(),
|
|
6042
|
+
timeframeEnd: timeframe.end instanceof Date ? timeframe.end.toISOString() : timeframe.end.toDate().toISOString()
|
|
6043
|
+
});
|
|
6039
6044
|
const start = timeframe.start instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
|
|
6040
6045
|
const end = timeframe.end instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
|
|
6046
|
+
Logger.debug("[BookingAdmin] Fetching clinic data", { clinicId });
|
|
6041
6047
|
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
6042
6048
|
if (!clinicDoc.exists) {
|
|
6049
|
+
Logger.error("[BookingAdmin] Clinic not found", { clinicId });
|
|
6043
6050
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
6044
6051
|
}
|
|
6045
6052
|
const clinic = clinicDoc.data();
|
|
6053
|
+
Logger.debug("[BookingAdmin] Retrieved clinic data", {
|
|
6054
|
+
clinicName: clinic.name,
|
|
6055
|
+
clinicHasWorkingHours: !!clinic.workingHours
|
|
6056
|
+
});
|
|
6057
|
+
Logger.debug("[BookingAdmin] Fetching practitioner data", {
|
|
6058
|
+
practitionerId
|
|
6059
|
+
});
|
|
6046
6060
|
const practitionerDoc = await this.db.collection("practitioners").doc(practitionerId).get();
|
|
6047
6061
|
if (!practitionerDoc.exists) {
|
|
6062
|
+
Logger.error("[BookingAdmin] Practitioner not found", {
|
|
6063
|
+
practitionerId
|
|
6064
|
+
});
|
|
6048
6065
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
6049
6066
|
}
|
|
6050
6067
|
const practitioner = practitionerDoc.data();
|
|
6068
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner data", {
|
|
6069
|
+
practitionerName: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
6070
|
+
pracWorkingHoursCount: ((_a = practitioner.clinicWorkingHours) == null ? void 0 : _a.length) || 0
|
|
6071
|
+
});
|
|
6072
|
+
Logger.debug("[BookingAdmin] Fetching procedure data", { procedureId });
|
|
6051
6073
|
const procedureDoc = await this.db.collection("procedures").doc(procedureId).get();
|
|
6052
6074
|
if (!procedureDoc.exists) {
|
|
6075
|
+
Logger.error("[BookingAdmin] Procedure not found", { procedureId });
|
|
6053
6076
|
throw new Error(`Procedure ${procedureId} not found`);
|
|
6054
6077
|
}
|
|
6055
6078
|
const procedure = procedureDoc.data();
|
|
6079
|
+
Logger.debug("[BookingAdmin] Retrieved procedure data", {
|
|
6080
|
+
procedureName: procedure.name,
|
|
6081
|
+
procedureDuration: procedure.duration
|
|
6082
|
+
});
|
|
6083
|
+
Logger.debug("[BookingAdmin] Fetching clinic calendar events", {
|
|
6084
|
+
clinicId,
|
|
6085
|
+
startTime: start.toDate().toISOString(),
|
|
6086
|
+
endTime: end.toDate().toISOString()
|
|
6087
|
+
});
|
|
6056
6088
|
const clinicCalendarEvents = await this.getClinicCalendarEvents(
|
|
6057
6089
|
clinicId,
|
|
6058
6090
|
start,
|
|
6059
6091
|
end
|
|
6060
6092
|
);
|
|
6093
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
6094
|
+
count: clinicCalendarEvents.length
|
|
6095
|
+
});
|
|
6096
|
+
Logger.debug("[BookingAdmin] Fetching practitioner calendar events", {
|
|
6097
|
+
practitionerId,
|
|
6098
|
+
startTime: start.toDate().toISOString(),
|
|
6099
|
+
endTime: end.toDate().toISOString()
|
|
6100
|
+
});
|
|
6061
6101
|
const practitionerCalendarEvents = await this.getPractitionerCalendarEvents(practitionerId, start, end);
|
|
6102
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
6103
|
+
count: practitionerCalendarEvents.length
|
|
6104
|
+
});
|
|
6062
6105
|
const convertedTimeframe = {
|
|
6063
6106
|
start: this.adminTimestampToClientTimestamp(start),
|
|
6064
6107
|
end: this.adminTimestampToClientTimestamp(end)
|
|
@@ -6073,14 +6116,38 @@ var BookingAdmin = class {
|
|
|
6073
6116
|
practitionerCalendarEvents
|
|
6074
6117
|
)
|
|
6075
6118
|
};
|
|
6119
|
+
Logger.info("[BookingAdmin] Calling availability calculator", {
|
|
6120
|
+
calculatorInputReady: true,
|
|
6121
|
+
timeframeDurationHours: Math.round(
|
|
6122
|
+
(end.toMillis() - start.toMillis()) / (1e3 * 60 * 60)
|
|
6123
|
+
),
|
|
6124
|
+
clinicEventsCount: clinicCalendarEvents.length,
|
|
6125
|
+
practitionerEventsCount: practitionerCalendarEvents.length
|
|
6126
|
+
});
|
|
6076
6127
|
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
6077
|
-
|
|
6128
|
+
const availableSlotsResult = {
|
|
6078
6129
|
availableSlots: result.availableSlots.map((slot) => ({
|
|
6079
6130
|
start: admin14.firestore.Timestamp.fromMillis(slot.start.toMillis())
|
|
6080
6131
|
}))
|
|
6081
6132
|
};
|
|
6133
|
+
Logger.info(
|
|
6134
|
+
"[BookingAdmin] Availability calculation completed successfully",
|
|
6135
|
+
{
|
|
6136
|
+
availableSlotsCount: availableSlotsResult.availableSlots.length,
|
|
6137
|
+
firstSlotTime: availableSlotsResult.availableSlots.length > 0 ? availableSlotsResult.availableSlots[0].start.toDate().toISOString() : "none",
|
|
6138
|
+
lastSlotTime: availableSlotsResult.availableSlots.length > 0 ? availableSlotsResult.availableSlots[availableSlotsResult.availableSlots.length - 1].start.toDate().toISOString() : "none"
|
|
6139
|
+
}
|
|
6140
|
+
);
|
|
6141
|
+
return availableSlotsResult;
|
|
6082
6142
|
} catch (error) {
|
|
6083
|
-
|
|
6143
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6144
|
+
Logger.error("[BookingAdmin] Error getting available slots", {
|
|
6145
|
+
errorMessage,
|
|
6146
|
+
clinicId,
|
|
6147
|
+
practitionerId,
|
|
6148
|
+
procedureId,
|
|
6149
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6150
|
+
});
|
|
6084
6151
|
throw error;
|
|
6085
6152
|
}
|
|
6086
6153
|
}
|
|
@@ -6117,17 +6184,32 @@ var BookingAdmin = class {
|
|
|
6117
6184
|
*/
|
|
6118
6185
|
async getClinicCalendarEvents(clinicId, start, end) {
|
|
6119
6186
|
try {
|
|
6187
|
+
Logger.debug("[BookingAdmin] Querying clinic calendar events", {
|
|
6188
|
+
clinicId,
|
|
6189
|
+
startTime: start.toDate().toISOString(),
|
|
6190
|
+
endTime: end.toDate().toISOString()
|
|
6191
|
+
});
|
|
6120
6192
|
const eventsRef = this.db.collection(`clinics/${clinicId}/calendar`).where("eventTime.start", ">=", start).where("eventTime.start", "<=", end);
|
|
6121
6193
|
const snapshot = await eventsRef.get();
|
|
6122
|
-
|
|
6194
|
+
const events = snapshot.docs.map((doc) => ({
|
|
6123
6195
|
...doc.data(),
|
|
6124
6196
|
id: doc.id
|
|
6125
6197
|
}));
|
|
6198
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
6199
|
+
clinicId,
|
|
6200
|
+
eventsCount: events.length,
|
|
6201
|
+
eventsTypes: this.summarizeEventTypes(events)
|
|
6202
|
+
});
|
|
6203
|
+
return events;
|
|
6126
6204
|
} catch (error) {
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6205
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6206
|
+
Logger.error("[BookingAdmin] Error fetching clinic calendar events", {
|
|
6207
|
+
errorMessage,
|
|
6208
|
+
clinicId,
|
|
6209
|
+
startTime: start.toDate().toISOString(),
|
|
6210
|
+
endTime: end.toDate().toISOString(),
|
|
6211
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6212
|
+
});
|
|
6131
6213
|
return [];
|
|
6132
6214
|
}
|
|
6133
6215
|
}
|
|
@@ -6141,20 +6223,51 @@ var BookingAdmin = class {
|
|
|
6141
6223
|
*/
|
|
6142
6224
|
async getPractitionerCalendarEvents(practitionerId, start, end) {
|
|
6143
6225
|
try {
|
|
6226
|
+
Logger.debug("[BookingAdmin] Querying practitioner calendar events", {
|
|
6227
|
+
practitionerId,
|
|
6228
|
+
startTime: start.toDate().toISOString(),
|
|
6229
|
+
endTime: end.toDate().toISOString()
|
|
6230
|
+
});
|
|
6144
6231
|
const eventsRef = this.db.collection(`practitioners/${practitionerId}/calendar`).where("eventTime.start", ">=", start).where("eventTime.start", "<=", end);
|
|
6145
6232
|
const snapshot = await eventsRef.get();
|
|
6146
|
-
|
|
6233
|
+
const events = snapshot.docs.map((doc) => ({
|
|
6147
6234
|
...doc.data(),
|
|
6148
6235
|
id: doc.id
|
|
6149
6236
|
}));
|
|
6237
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
6238
|
+
practitionerId,
|
|
6239
|
+
eventsCount: events.length,
|
|
6240
|
+
eventsTypes: this.summarizeEventTypes(events)
|
|
6241
|
+
});
|
|
6242
|
+
return events;
|
|
6150
6243
|
} catch (error) {
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6244
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6245
|
+
Logger.error(
|
|
6246
|
+
"[BookingAdmin] Error fetching practitioner calendar events",
|
|
6247
|
+
{
|
|
6248
|
+
errorMessage,
|
|
6249
|
+
practitionerId,
|
|
6250
|
+
startTime: start.toDate().toISOString(),
|
|
6251
|
+
endTime: end.toDate().toISOString(),
|
|
6252
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6253
|
+
}
|
|
6154
6254
|
);
|
|
6155
6255
|
return [];
|
|
6156
6256
|
}
|
|
6157
6257
|
}
|
|
6258
|
+
/**
|
|
6259
|
+
* Summarizes event types for logging purposes
|
|
6260
|
+
* @param events Array of calendar events
|
|
6261
|
+
* @returns Object with counts of each event type
|
|
6262
|
+
*/
|
|
6263
|
+
summarizeEventTypes(events) {
|
|
6264
|
+
const typeCounts = {};
|
|
6265
|
+
events.forEach((event) => {
|
|
6266
|
+
const eventType = event.eventType || "unknown";
|
|
6267
|
+
typeCounts[eventType] = (typeCounts[eventType] || 0) + 1;
|
|
6268
|
+
});
|
|
6269
|
+
return typeCounts;
|
|
6270
|
+
}
|
|
6158
6271
|
_generateCalendarProcedureInfo(procedure) {
|
|
6159
6272
|
return {
|
|
6160
6273
|
name: procedure.name,
|
package/dist/admin/index.mjs
CHANGED
|
@@ -5975,33 +5975,76 @@ var BookingAdmin = class {
|
|
|
5975
5975
|
* @returns Promise resolving to an array of available booking slots
|
|
5976
5976
|
*/
|
|
5977
5977
|
async getAvailableBookingSlots(clinicId, practitionerId, procedureId, timeframe) {
|
|
5978
|
+
var _a;
|
|
5978
5979
|
try {
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5980
|
+
Logger.info("[BookingAdmin] Starting availability calculation", {
|
|
5981
|
+
clinicId,
|
|
5982
|
+
practitionerId,
|
|
5983
|
+
procedureId,
|
|
5984
|
+
timeframeStart: timeframe.start instanceof Date ? timeframe.start.toISOString() : timeframe.start.toDate().toISOString(),
|
|
5985
|
+
timeframeEnd: timeframe.end instanceof Date ? timeframe.end.toISOString() : timeframe.end.toDate().toISOString()
|
|
5986
|
+
});
|
|
5982
5987
|
const start = timeframe.start instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
|
|
5983
5988
|
const end = timeframe.end instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
|
|
5989
|
+
Logger.debug("[BookingAdmin] Fetching clinic data", { clinicId });
|
|
5984
5990
|
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
5985
5991
|
if (!clinicDoc.exists) {
|
|
5992
|
+
Logger.error("[BookingAdmin] Clinic not found", { clinicId });
|
|
5986
5993
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
5987
5994
|
}
|
|
5988
5995
|
const clinic = clinicDoc.data();
|
|
5996
|
+
Logger.debug("[BookingAdmin] Retrieved clinic data", {
|
|
5997
|
+
clinicName: clinic.name,
|
|
5998
|
+
clinicHasWorkingHours: !!clinic.workingHours
|
|
5999
|
+
});
|
|
6000
|
+
Logger.debug("[BookingAdmin] Fetching practitioner data", {
|
|
6001
|
+
practitionerId
|
|
6002
|
+
});
|
|
5989
6003
|
const practitionerDoc = await this.db.collection("practitioners").doc(practitionerId).get();
|
|
5990
6004
|
if (!practitionerDoc.exists) {
|
|
6005
|
+
Logger.error("[BookingAdmin] Practitioner not found", {
|
|
6006
|
+
practitionerId
|
|
6007
|
+
});
|
|
5991
6008
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
5992
6009
|
}
|
|
5993
6010
|
const practitioner = practitionerDoc.data();
|
|
6011
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner data", {
|
|
6012
|
+
practitionerName: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
6013
|
+
pracWorkingHoursCount: ((_a = practitioner.clinicWorkingHours) == null ? void 0 : _a.length) || 0
|
|
6014
|
+
});
|
|
6015
|
+
Logger.debug("[BookingAdmin] Fetching procedure data", { procedureId });
|
|
5994
6016
|
const procedureDoc = await this.db.collection("procedures").doc(procedureId).get();
|
|
5995
6017
|
if (!procedureDoc.exists) {
|
|
6018
|
+
Logger.error("[BookingAdmin] Procedure not found", { procedureId });
|
|
5996
6019
|
throw new Error(`Procedure ${procedureId} not found`);
|
|
5997
6020
|
}
|
|
5998
6021
|
const procedure = procedureDoc.data();
|
|
6022
|
+
Logger.debug("[BookingAdmin] Retrieved procedure data", {
|
|
6023
|
+
procedureName: procedure.name,
|
|
6024
|
+
procedureDuration: procedure.duration
|
|
6025
|
+
});
|
|
6026
|
+
Logger.debug("[BookingAdmin] Fetching clinic calendar events", {
|
|
6027
|
+
clinicId,
|
|
6028
|
+
startTime: start.toDate().toISOString(),
|
|
6029
|
+
endTime: end.toDate().toISOString()
|
|
6030
|
+
});
|
|
5999
6031
|
const clinicCalendarEvents = await this.getClinicCalendarEvents(
|
|
6000
6032
|
clinicId,
|
|
6001
6033
|
start,
|
|
6002
6034
|
end
|
|
6003
6035
|
);
|
|
6036
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
6037
|
+
count: clinicCalendarEvents.length
|
|
6038
|
+
});
|
|
6039
|
+
Logger.debug("[BookingAdmin] Fetching practitioner calendar events", {
|
|
6040
|
+
practitionerId,
|
|
6041
|
+
startTime: start.toDate().toISOString(),
|
|
6042
|
+
endTime: end.toDate().toISOString()
|
|
6043
|
+
});
|
|
6004
6044
|
const practitionerCalendarEvents = await this.getPractitionerCalendarEvents(practitionerId, start, end);
|
|
6045
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
6046
|
+
count: practitionerCalendarEvents.length
|
|
6047
|
+
});
|
|
6005
6048
|
const convertedTimeframe = {
|
|
6006
6049
|
start: this.adminTimestampToClientTimestamp(start),
|
|
6007
6050
|
end: this.adminTimestampToClientTimestamp(end)
|
|
@@ -6016,14 +6059,38 @@ var BookingAdmin = class {
|
|
|
6016
6059
|
practitionerCalendarEvents
|
|
6017
6060
|
)
|
|
6018
6061
|
};
|
|
6062
|
+
Logger.info("[BookingAdmin] Calling availability calculator", {
|
|
6063
|
+
calculatorInputReady: true,
|
|
6064
|
+
timeframeDurationHours: Math.round(
|
|
6065
|
+
(end.toMillis() - start.toMillis()) / (1e3 * 60 * 60)
|
|
6066
|
+
),
|
|
6067
|
+
clinicEventsCount: clinicCalendarEvents.length,
|
|
6068
|
+
practitionerEventsCount: practitionerCalendarEvents.length
|
|
6069
|
+
});
|
|
6019
6070
|
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
6020
|
-
|
|
6071
|
+
const availableSlotsResult = {
|
|
6021
6072
|
availableSlots: result.availableSlots.map((slot) => ({
|
|
6022
6073
|
start: admin14.firestore.Timestamp.fromMillis(slot.start.toMillis())
|
|
6023
6074
|
}))
|
|
6024
6075
|
};
|
|
6076
|
+
Logger.info(
|
|
6077
|
+
"[BookingAdmin] Availability calculation completed successfully",
|
|
6078
|
+
{
|
|
6079
|
+
availableSlotsCount: availableSlotsResult.availableSlots.length,
|
|
6080
|
+
firstSlotTime: availableSlotsResult.availableSlots.length > 0 ? availableSlotsResult.availableSlots[0].start.toDate().toISOString() : "none",
|
|
6081
|
+
lastSlotTime: availableSlotsResult.availableSlots.length > 0 ? availableSlotsResult.availableSlots[availableSlotsResult.availableSlots.length - 1].start.toDate().toISOString() : "none"
|
|
6082
|
+
}
|
|
6083
|
+
);
|
|
6084
|
+
return availableSlotsResult;
|
|
6025
6085
|
} catch (error) {
|
|
6026
|
-
|
|
6086
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6087
|
+
Logger.error("[BookingAdmin] Error getting available slots", {
|
|
6088
|
+
errorMessage,
|
|
6089
|
+
clinicId,
|
|
6090
|
+
practitionerId,
|
|
6091
|
+
procedureId,
|
|
6092
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6093
|
+
});
|
|
6027
6094
|
throw error;
|
|
6028
6095
|
}
|
|
6029
6096
|
}
|
|
@@ -6060,17 +6127,32 @@ var BookingAdmin = class {
|
|
|
6060
6127
|
*/
|
|
6061
6128
|
async getClinicCalendarEvents(clinicId, start, end) {
|
|
6062
6129
|
try {
|
|
6130
|
+
Logger.debug("[BookingAdmin] Querying clinic calendar events", {
|
|
6131
|
+
clinicId,
|
|
6132
|
+
startTime: start.toDate().toISOString(),
|
|
6133
|
+
endTime: end.toDate().toISOString()
|
|
6134
|
+
});
|
|
6063
6135
|
const eventsRef = this.db.collection(`clinics/${clinicId}/calendar`).where("eventTime.start", ">=", start).where("eventTime.start", "<=", end);
|
|
6064
6136
|
const snapshot = await eventsRef.get();
|
|
6065
|
-
|
|
6137
|
+
const events = snapshot.docs.map((doc) => ({
|
|
6066
6138
|
...doc.data(),
|
|
6067
6139
|
id: doc.id
|
|
6068
6140
|
}));
|
|
6141
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
6142
|
+
clinicId,
|
|
6143
|
+
eventsCount: events.length,
|
|
6144
|
+
eventsTypes: this.summarizeEventTypes(events)
|
|
6145
|
+
});
|
|
6146
|
+
return events;
|
|
6069
6147
|
} catch (error) {
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6148
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6149
|
+
Logger.error("[BookingAdmin] Error fetching clinic calendar events", {
|
|
6150
|
+
errorMessage,
|
|
6151
|
+
clinicId,
|
|
6152
|
+
startTime: start.toDate().toISOString(),
|
|
6153
|
+
endTime: end.toDate().toISOString(),
|
|
6154
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6155
|
+
});
|
|
6074
6156
|
return [];
|
|
6075
6157
|
}
|
|
6076
6158
|
}
|
|
@@ -6084,20 +6166,51 @@ var BookingAdmin = class {
|
|
|
6084
6166
|
*/
|
|
6085
6167
|
async getPractitionerCalendarEvents(practitionerId, start, end) {
|
|
6086
6168
|
try {
|
|
6169
|
+
Logger.debug("[BookingAdmin] Querying practitioner calendar events", {
|
|
6170
|
+
practitionerId,
|
|
6171
|
+
startTime: start.toDate().toISOString(),
|
|
6172
|
+
endTime: end.toDate().toISOString()
|
|
6173
|
+
});
|
|
6087
6174
|
const eventsRef = this.db.collection(`practitioners/${practitionerId}/calendar`).where("eventTime.start", ">=", start).where("eventTime.start", "<=", end);
|
|
6088
6175
|
const snapshot = await eventsRef.get();
|
|
6089
|
-
|
|
6176
|
+
const events = snapshot.docs.map((doc) => ({
|
|
6090
6177
|
...doc.data(),
|
|
6091
6178
|
id: doc.id
|
|
6092
6179
|
}));
|
|
6180
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
6181
|
+
practitionerId,
|
|
6182
|
+
eventsCount: events.length,
|
|
6183
|
+
eventsTypes: this.summarizeEventTypes(events)
|
|
6184
|
+
});
|
|
6185
|
+
return events;
|
|
6093
6186
|
} catch (error) {
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6187
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6188
|
+
Logger.error(
|
|
6189
|
+
"[BookingAdmin] Error fetching practitioner calendar events",
|
|
6190
|
+
{
|
|
6191
|
+
errorMessage,
|
|
6192
|
+
practitionerId,
|
|
6193
|
+
startTime: start.toDate().toISOString(),
|
|
6194
|
+
endTime: end.toDate().toISOString(),
|
|
6195
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
6196
|
+
}
|
|
6097
6197
|
);
|
|
6098
6198
|
return [];
|
|
6099
6199
|
}
|
|
6100
6200
|
}
|
|
6201
|
+
/**
|
|
6202
|
+
* Summarizes event types for logging purposes
|
|
6203
|
+
* @param events Array of calendar events
|
|
6204
|
+
* @returns Object with counts of each event type
|
|
6205
|
+
*/
|
|
6206
|
+
summarizeEventTypes(events) {
|
|
6207
|
+
const typeCounts = {};
|
|
6208
|
+
events.forEach((event) => {
|
|
6209
|
+
const eventType = event.eventType || "unknown";
|
|
6210
|
+
typeCounts[eventType] = (typeCounts[eventType] || 0) + 1;
|
|
6211
|
+
});
|
|
6212
|
+
return typeCounts;
|
|
6213
|
+
}
|
|
6101
6214
|
_generateCalendarProcedureInfo(procedure) {
|
|
6102
6215
|
return {
|
|
6103
6216
|
name: procedure.name,
|
package/package.json
CHANGED
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
import { DocumentManagerAdminService } from "../documentation-templates/document-manager.admin";
|
|
65
65
|
import { LinkedFormInfo } from "../../types/appointment";
|
|
66
66
|
import { TimestampUtils } from "../../utils/TimestampUtils";
|
|
67
|
+
import { Logger } from "../logger";
|
|
67
68
|
|
|
68
69
|
/**
|
|
69
70
|
* Interface for the data required by orchestrateAppointmentCreation
|
|
@@ -112,9 +113,19 @@ export class BookingAdmin {
|
|
|
112
113
|
}
|
|
113
114
|
): Promise<{ availableSlots: { start: admin.firestore.Timestamp }[] }> {
|
|
114
115
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
Logger.info("[BookingAdmin] Starting availability calculation", {
|
|
117
|
+
clinicId,
|
|
118
|
+
practitionerId,
|
|
119
|
+
procedureId,
|
|
120
|
+
timeframeStart:
|
|
121
|
+
timeframe.start instanceof Date
|
|
122
|
+
? timeframe.start.toISOString()
|
|
123
|
+
: timeframe.start.toDate().toISOString(),
|
|
124
|
+
timeframeEnd:
|
|
125
|
+
timeframe.end instanceof Date
|
|
126
|
+
? timeframe.end.toISOString()
|
|
127
|
+
: timeframe.end.toDate().toISOString(),
|
|
128
|
+
});
|
|
118
129
|
|
|
119
130
|
// Convert timeframe dates to Firestore Timestamps if needed
|
|
120
131
|
const start =
|
|
@@ -128,42 +139,80 @@ export class BookingAdmin {
|
|
|
128
139
|
: timeframe.end;
|
|
129
140
|
|
|
130
141
|
// 1. Fetch clinic data
|
|
142
|
+
Logger.debug("[BookingAdmin] Fetching clinic data", { clinicId });
|
|
131
143
|
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
132
144
|
if (!clinicDoc.exists) {
|
|
145
|
+
Logger.error("[BookingAdmin] Clinic not found", { clinicId });
|
|
133
146
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
134
147
|
}
|
|
135
148
|
const clinic = clinicDoc.data() as unknown as Clinic;
|
|
149
|
+
Logger.debug("[BookingAdmin] Retrieved clinic data", {
|
|
150
|
+
clinicName: clinic.name,
|
|
151
|
+
clinicHasWorkingHours: !!clinic.workingHours,
|
|
152
|
+
});
|
|
136
153
|
|
|
137
154
|
// 2. Fetch practitioner data
|
|
155
|
+
Logger.debug("[BookingAdmin] Fetching practitioner data", {
|
|
156
|
+
practitionerId,
|
|
157
|
+
});
|
|
138
158
|
const practitionerDoc = await this.db
|
|
139
159
|
.collection("practitioners")
|
|
140
160
|
.doc(practitionerId)
|
|
141
161
|
.get();
|
|
142
162
|
if (!practitionerDoc.exists) {
|
|
163
|
+
Logger.error("[BookingAdmin] Practitioner not found", {
|
|
164
|
+
practitionerId,
|
|
165
|
+
});
|
|
143
166
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
144
167
|
}
|
|
145
168
|
const practitioner = practitionerDoc.data() as unknown as Practitioner;
|
|
169
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner data", {
|
|
170
|
+
practitionerName: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
171
|
+
pracWorkingHoursCount: practitioner.clinicWorkingHours?.length || 0,
|
|
172
|
+
});
|
|
146
173
|
|
|
147
174
|
// 3. Fetch procedure data
|
|
175
|
+
Logger.debug("[BookingAdmin] Fetching procedure data", { procedureId });
|
|
148
176
|
const procedureDoc = await this.db
|
|
149
177
|
.collection("procedures")
|
|
150
178
|
.doc(procedureId)
|
|
151
179
|
.get();
|
|
152
180
|
if (!procedureDoc.exists) {
|
|
181
|
+
Logger.error("[BookingAdmin] Procedure not found", { procedureId });
|
|
153
182
|
throw new Error(`Procedure ${procedureId} not found`);
|
|
154
183
|
}
|
|
155
184
|
const procedure = procedureDoc.data() as unknown as Procedure;
|
|
185
|
+
Logger.debug("[BookingAdmin] Retrieved procedure data", {
|
|
186
|
+
procedureName: procedure.name,
|
|
187
|
+
procedureDuration: procedure.duration,
|
|
188
|
+
});
|
|
156
189
|
|
|
157
190
|
// 4. Fetch clinic calendar events
|
|
191
|
+
Logger.debug("[BookingAdmin] Fetching clinic calendar events", {
|
|
192
|
+
clinicId,
|
|
193
|
+
startTime: start.toDate().toISOString(),
|
|
194
|
+
endTime: end.toDate().toISOString(),
|
|
195
|
+
});
|
|
158
196
|
const clinicCalendarEvents = await this.getClinicCalendarEvents(
|
|
159
197
|
clinicId,
|
|
160
198
|
start,
|
|
161
199
|
end
|
|
162
200
|
);
|
|
201
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
202
|
+
count: clinicCalendarEvents.length,
|
|
203
|
+
});
|
|
163
204
|
|
|
164
205
|
// 5. Fetch practitioner calendar events
|
|
206
|
+
Logger.debug("[BookingAdmin] Fetching practitioner calendar events", {
|
|
207
|
+
practitionerId,
|
|
208
|
+
startTime: start.toDate().toISOString(),
|
|
209
|
+
endTime: end.toDate().toISOString(),
|
|
210
|
+
});
|
|
165
211
|
const practitionerCalendarEvents =
|
|
166
212
|
await this.getPractitionerCalendarEvents(practitionerId, start, end);
|
|
213
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
214
|
+
count: practitionerCalendarEvents.length,
|
|
215
|
+
});
|
|
167
216
|
|
|
168
217
|
// Since we're working with two different Timestamp implementations (admin vs client),
|
|
169
218
|
// we need to convert our timestamps to the client-side format expected by the calculator
|
|
@@ -186,17 +235,57 @@ export class BookingAdmin {
|
|
|
186
235
|
),
|
|
187
236
|
};
|
|
188
237
|
|
|
238
|
+
Logger.info("[BookingAdmin] Calling availability calculator", {
|
|
239
|
+
calculatorInputReady: true,
|
|
240
|
+
timeframeDurationHours: Math.round(
|
|
241
|
+
(end.toMillis() - start.toMillis()) / (1000 * 60 * 60)
|
|
242
|
+
),
|
|
243
|
+
clinicEventsCount: clinicCalendarEvents.length,
|
|
244
|
+
practitionerEventsCount: practitionerCalendarEvents.length,
|
|
245
|
+
});
|
|
246
|
+
|
|
189
247
|
// Use the calculator to compute available slots
|
|
190
248
|
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
191
249
|
|
|
192
250
|
// Convert the client Timestamps to admin Timestamps before returning
|
|
193
|
-
|
|
251
|
+
const availableSlotsResult = {
|
|
194
252
|
availableSlots: result.availableSlots.map((slot) => ({
|
|
195
253
|
start: admin.firestore.Timestamp.fromMillis(slot.start.toMillis()),
|
|
196
254
|
})),
|
|
197
255
|
};
|
|
256
|
+
|
|
257
|
+
Logger.info(
|
|
258
|
+
"[BookingAdmin] Availability calculation completed successfully",
|
|
259
|
+
{
|
|
260
|
+
availableSlotsCount: availableSlotsResult.availableSlots.length,
|
|
261
|
+
firstSlotTime:
|
|
262
|
+
availableSlotsResult.availableSlots.length > 0
|
|
263
|
+
? availableSlotsResult.availableSlots[0].start
|
|
264
|
+
.toDate()
|
|
265
|
+
.toISOString()
|
|
266
|
+
: "none",
|
|
267
|
+
lastSlotTime:
|
|
268
|
+
availableSlotsResult.availableSlots.length > 0
|
|
269
|
+
? availableSlotsResult.availableSlots[
|
|
270
|
+
availableSlotsResult.availableSlots.length - 1
|
|
271
|
+
].start
|
|
272
|
+
.toDate()
|
|
273
|
+
.toISOString()
|
|
274
|
+
: "none",
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
return availableSlotsResult;
|
|
198
279
|
} catch (error) {
|
|
199
|
-
|
|
280
|
+
const errorMessage =
|
|
281
|
+
error instanceof Error ? error.message : String(error);
|
|
282
|
+
Logger.error("[BookingAdmin] Error getting available slots", {
|
|
283
|
+
errorMessage,
|
|
284
|
+
clinicId,
|
|
285
|
+
practitionerId,
|
|
286
|
+
procedureId,
|
|
287
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
288
|
+
});
|
|
200
289
|
throw error;
|
|
201
290
|
}
|
|
202
291
|
}
|
|
@@ -243,6 +332,12 @@ export class BookingAdmin {
|
|
|
243
332
|
end: admin.firestore.Timestamp
|
|
244
333
|
): Promise<any[]> {
|
|
245
334
|
try {
|
|
335
|
+
Logger.debug("[BookingAdmin] Querying clinic calendar events", {
|
|
336
|
+
clinicId,
|
|
337
|
+
startTime: start.toDate().toISOString(),
|
|
338
|
+
endTime: end.toDate().toISOString(),
|
|
339
|
+
});
|
|
340
|
+
|
|
246
341
|
const eventsRef = this.db
|
|
247
342
|
.collection(`clinics/${clinicId}/calendar`)
|
|
248
343
|
.where("eventTime.start", ">=", start)
|
|
@@ -250,15 +345,28 @@ export class BookingAdmin {
|
|
|
250
345
|
|
|
251
346
|
const snapshot = await eventsRef.get();
|
|
252
347
|
|
|
253
|
-
|
|
348
|
+
const events = snapshot.docs.map((doc) => ({
|
|
254
349
|
...doc.data(),
|
|
255
350
|
id: doc.id,
|
|
256
351
|
}));
|
|
352
|
+
|
|
353
|
+
Logger.debug("[BookingAdmin] Retrieved clinic calendar events", {
|
|
354
|
+
clinicId,
|
|
355
|
+
eventsCount: events.length,
|
|
356
|
+
eventsTypes: this.summarizeEventTypes(events),
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return events;
|
|
257
360
|
} catch (error) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
361
|
+
const errorMessage =
|
|
362
|
+
error instanceof Error ? error.message : String(error);
|
|
363
|
+
Logger.error("[BookingAdmin] Error fetching clinic calendar events", {
|
|
364
|
+
errorMessage,
|
|
365
|
+
clinicId,
|
|
366
|
+
startTime: start.toDate().toISOString(),
|
|
367
|
+
endTime: end.toDate().toISOString(),
|
|
368
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
369
|
+
});
|
|
262
370
|
return [];
|
|
263
371
|
}
|
|
264
372
|
}
|
|
@@ -277,6 +385,12 @@ export class BookingAdmin {
|
|
|
277
385
|
end: admin.firestore.Timestamp
|
|
278
386
|
): Promise<any[]> {
|
|
279
387
|
try {
|
|
388
|
+
Logger.debug("[BookingAdmin] Querying practitioner calendar events", {
|
|
389
|
+
practitionerId,
|
|
390
|
+
startTime: start.toDate().toISOString(),
|
|
391
|
+
endTime: end.toDate().toISOString(),
|
|
392
|
+
});
|
|
393
|
+
|
|
280
394
|
const eventsRef = this.db
|
|
281
395
|
.collection(`practitioners/${practitionerId}/calendar`)
|
|
282
396
|
.where("eventTime.start", ">=", start)
|
|
@@ -284,19 +398,51 @@ export class BookingAdmin {
|
|
|
284
398
|
|
|
285
399
|
const snapshot = await eventsRef.get();
|
|
286
400
|
|
|
287
|
-
|
|
401
|
+
const events = snapshot.docs.map((doc) => ({
|
|
288
402
|
...doc.data(),
|
|
289
403
|
id: doc.id,
|
|
290
404
|
}));
|
|
405
|
+
|
|
406
|
+
Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
|
|
407
|
+
practitionerId,
|
|
408
|
+
eventsCount: events.length,
|
|
409
|
+
eventsTypes: this.summarizeEventTypes(events),
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
return events;
|
|
291
413
|
} catch (error) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
414
|
+
const errorMessage =
|
|
415
|
+
error instanceof Error ? error.message : String(error);
|
|
416
|
+
Logger.error(
|
|
417
|
+
"[BookingAdmin] Error fetching practitioner calendar events",
|
|
418
|
+
{
|
|
419
|
+
errorMessage,
|
|
420
|
+
practitionerId,
|
|
421
|
+
startTime: start.toDate().toISOString(),
|
|
422
|
+
endTime: end.toDate().toISOString(),
|
|
423
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
424
|
+
}
|
|
295
425
|
);
|
|
296
426
|
return [];
|
|
297
427
|
}
|
|
298
428
|
}
|
|
299
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Summarizes event types for logging purposes
|
|
432
|
+
* @param events Array of calendar events
|
|
433
|
+
* @returns Object with counts of each event type
|
|
434
|
+
*/
|
|
435
|
+
private summarizeEventTypes(events: any[]): Record<string, number> {
|
|
436
|
+
const typeCounts: Record<string, number> = {};
|
|
437
|
+
|
|
438
|
+
events.forEach((event) => {
|
|
439
|
+
const eventType = event.eventType || "unknown";
|
|
440
|
+
typeCounts[eventType] = (typeCounts[eventType] || 0) + 1;
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
return typeCounts;
|
|
444
|
+
}
|
|
445
|
+
|
|
300
446
|
private _generateCalendarProcedureInfo(
|
|
301
447
|
procedure: Procedure
|
|
302
448
|
): CalendarProcedureInfo {
|