@blackcode_sa/metaestetics-api 1.6.3 → 1.6.5

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.
Files changed (40) hide show
  1. package/dist/admin/index.d.mts +439 -25
  2. package/dist/admin/index.d.ts +439 -25
  3. package/dist/admin/index.js +36107 -2493
  4. package/dist/admin/index.mjs +36093 -2461
  5. package/dist/backoffice/index.d.mts +254 -1
  6. package/dist/backoffice/index.d.ts +254 -1
  7. package/dist/backoffice/index.js +86 -12
  8. package/dist/backoffice/index.mjs +86 -13
  9. package/dist/index.d.mts +1434 -621
  10. package/dist/index.d.ts +1434 -621
  11. package/dist/index.js +1381 -970
  12. package/dist/index.mjs +1433 -1016
  13. package/package.json +1 -1
  14. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +321 -0
  15. package/src/admin/booking/booking.admin.ts +376 -3
  16. package/src/admin/index.ts +15 -1
  17. package/src/admin/notifications/notifications.admin.ts +1 -1
  18. package/src/admin/requirements/README.md +128 -0
  19. package/src/admin/requirements/patient-requirements.admin.service.ts +482 -0
  20. package/src/backoffice/types/product.types.ts +2 -0
  21. package/src/index.ts +16 -1
  22. package/src/services/appointment/appointment.service.ts +386 -250
  23. package/src/services/clinic/clinic-admin.service.ts +3 -0
  24. package/src/services/clinic/clinic-group.service.ts +8 -0
  25. package/src/services/documentation-templates/documentation-template.service.ts +24 -16
  26. package/src/services/documentation-templates/filled-document.service.ts +253 -136
  27. package/src/services/patient/patientRequirements.service.ts +285 -0
  28. package/src/services/procedure/procedure.service.ts +1 -0
  29. package/src/types/appointment/index.ts +136 -11
  30. package/src/types/documentation-templates/index.ts +34 -2
  31. package/src/types/notifications/README.md +77 -0
  32. package/src/types/notifications/index.ts +154 -27
  33. package/src/types/patient/patient-requirements.ts +81 -0
  34. package/src/types/procedure/index.ts +7 -0
  35. package/src/validations/appointment.schema.ts +298 -62
  36. package/src/validations/documentation-templates/template.schema.ts +55 -0
  37. package/src/validations/documentation-templates.schema.ts +9 -14
  38. package/src/validations/notification.schema.ts +3 -3
  39. package/src/validations/patient/patient-requirements.schema.ts +75 -0
  40. package/src/validations/procedure.schema.ts +3 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.6.3",
4
+ "version": "1.6.5",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -0,0 +1,321 @@
1
+ import * as admin from "firebase-admin";
2
+ import {
3
+ Appointment,
4
+ AppointmentStatus,
5
+ APPOINTMENTS_COLLECTION,
6
+ } from "../../../types/appointment"; // Assuming APPOINTMENTS_COLLECTION is exported here
7
+ import {
8
+ PatientRequirementInstance,
9
+ PatientRequirementOverallStatus,
10
+ PatientInstructionStatus,
11
+ PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME,
12
+ } from "../../../types/patient/patient-requirements";
13
+ import {
14
+ Requirement as RequirementTemplate,
15
+ REQUIREMENTS_COLLECTION as REQUIREMENTS_TEMPLATES_COLLECTION,
16
+ RequirementType as RequirementTemplateType,
17
+ } from "../../../backoffice/types/requirement.types";
18
+ import { PATIENTS_COLLECTION } from "../../../types/patient";
19
+
20
+ // Dependent Admin Services
21
+ import { PatientRequirementsAdminService } from "../../requirements/patient-requirements.admin.service";
22
+ import { NotificationsAdmin } from "../../notifications/notifications.admin";
23
+ // import { CalendarAdminService } from "../../calendar/calendar.admin.service"; // Placeholder - To be created
24
+ // import { AppointmentMailingService } from "../mailing/appointment/appointment.mailing.service"; // Placeholder - To be created
25
+ // import { PatientAdminService } from "../patient/patient.admin.service"; // Placeholder for specific patient admin actions
26
+ // import { PractitionerAdminService } from "../practitioner/practitioner.admin.service"; // Placeholder for specific practitioner admin actions
27
+ // import { ClinicAdminService } from "../clinic/clinic.admin.service"; // Placeholder for specific clinic admin actions
28
+
29
+ /**
30
+ * @class AppointmentAggregationService
31
+ * @description Handles aggregation tasks and side effects related to appointment lifecycle events.
32
+ * This service is intended to be used primarily by background functions (e.g., Cloud Functions)
33
+ * triggered by changes in the appointments collection.
34
+ */
35
+ export class AppointmentAggregationService {
36
+ private db: admin.firestore.Firestore;
37
+ private patientRequirementsAdminService: PatientRequirementsAdminService;
38
+ private notificationsAdmin: NotificationsAdmin;
39
+ // private calendarAdminService: CalendarAdminService; // Placeholder
40
+ // private appointmentMailingService: AppointmentMailingService; // Placeholder
41
+ // private patientAdminService: PatientAdminService; // Placeholder
42
+ // private practitionerAdminService: PractitionerAdminService; // Placeholder
43
+ // private clinicAdminService: ClinicAdminService; // Placeholder
44
+
45
+ /**
46
+ * Constructor for AppointmentAggregationService.
47
+ * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
48
+ */
49
+ constructor(firestore?: admin.firestore.Firestore) {
50
+ this.db = firestore || admin.firestore();
51
+ this.patientRequirementsAdminService = new PatientRequirementsAdminService(
52
+ this.db
53
+ );
54
+ this.notificationsAdmin = new NotificationsAdmin(this.db);
55
+ // this.calendarAdminService = new CalendarAdminService(this.db); // Placeholder
56
+ // this.appointmentMailingService = new AppointmentMailingService(this.db /*, mailgunClient */); // Placeholder
57
+ // this.patientAdminService = new PatientAdminService(this.db); // Placeholder
58
+ // this.practitionerAdminService = new PractitionerAdminService(this.db); // Placeholder
59
+ // this.clinicAdminService = new ClinicAdminService(this.db); // Placeholder
60
+ }
61
+
62
+ /**
63
+ * Handles side effects when an appointment is confirmed.
64
+ * - Creates PRE-appointment PatientRequirementInstances.
65
+ * - Triggers calendar event creation.
66
+ * - Triggers email/push notifications.
67
+ * - Manages patient-clinic-practitioner links.
68
+ * @param appointment - The confirmed Appointment object.
69
+ * @returns {Promise<void>}
70
+ */
71
+ async handleAppointmentConfirmed(appointment: Appointment): Promise<void> {
72
+ console.log(
73
+ `[AppointmentAggregationService] Handling confirmed appointment: ${appointment.id}`
74
+ );
75
+ // 1. Create PRE-appointment PatientRequirementInstances
76
+ // - Fetch RequirementTemplates for procedureId
77
+ // - Construct and save PatientRequirementInstance documents
78
+ // - Rely on PatientRequirementInstance triggers for notification scheduling
79
+
80
+ // 2. Trigger calendar event creation (e.g., this.calendarAdminService.createEventForAppointment(appointment))
81
+
82
+ // 3. Trigger email/push notifications (e.g., this.appointmentMailingService.sendAppointmentConfirmedEmail(appointment))
83
+
84
+ // 4. Manage patient-clinic-practitioner links (e.g., this.managePatientClinicPractitionerLinks(appointment, 'create'))
85
+
86
+ // Example: Placeholder for creating pre-requirements
87
+ // const requirementTemplates = await this.fetchRequirementTemplates(appointment.procedureId, 'PRE');
88
+ // for (const template of requirementTemplates) {
89
+ // await this.createPatientRequirementInstance(appointment, template, 'PRE');
90
+ // }
91
+ }
92
+
93
+ /**
94
+ * Handles side effects when an appointment is completed.
95
+ * - Creates POST-appointment PatientRequirementInstances.
96
+ * - Triggers review request notifications.
97
+ * @param appointment - The completed Appointment object.
98
+ * @returns {Promise<void>}
99
+ */
100
+ async handleAppointmentCompleted(appointment: Appointment): Promise<void> {
101
+ console.log(
102
+ `[AppointmentAggregationService] Handling completed appointment: ${appointment.id}`
103
+ );
104
+ // 1. Create POST-appointment PatientRequirementInstances
105
+ // - Fetch RequirementTemplates for procedureId
106
+ // - Construct and save PatientRequirementInstance documents
107
+
108
+ // 2. Trigger review request (e.g., this.appointmentMailingService.sendReviewRequestEmail(appointment))
109
+ }
110
+
111
+ /**
112
+ * Handles side effects when an appointment is cancelled.
113
+ * - Updates overallStatus of related PatientRequirementInstances to CANCELLED_APPOINTMENT.
114
+ * - Triggers calendar event cancellation/update.
115
+ * - Triggers email/push notifications for cancellation.
116
+ * - Evaluates patient-clinic-practitioner links.
117
+ * @param appointment - The cancelled Appointment object.
118
+ * @param previousStatus - The status of the appointment before cancellation.
119
+ * @returns {Promise<void>}
120
+ */
121
+ async handleAppointmentCancelled(
122
+ appointment: Appointment,
123
+ previousStatus?: AppointmentStatus
124
+ ): Promise<void> {
125
+ console.log(
126
+ `[AppointmentAggregationService] Handling cancelled appointment: ${appointment.id}, previous status: ${previousStatus}`
127
+ );
128
+ // 1. Update related PatientRequirementInstances' overallStatus
129
+ // - Query instances by appointment.id
130
+ // - Update overallStatus to PatientRequirementOverallStatus.CANCELLED_APPOINTMENT
131
+ // - PatientRequirementsAdminService (via trigger) should handle notification cancellations.
132
+
133
+ // 2. Trigger calendar event cancellation (e.g., this.calendarAdminService.cancelEventForAppointment(appointment))
134
+
135
+ // 3. Trigger email notifications (e.g., this.appointmentMailingService.sendAppointmentCancelledEmail(appointment))
136
+
137
+ // 4. Evaluate patient-clinic-practitioner links for removal
138
+ }
139
+
140
+ /**
141
+ * Handles side effects when an appointment is rescheduled.
142
+ * - Updates/recreates PRE-appointment PatientRequirementInstances based on new times.
143
+ * - Triggers calendar event updates.
144
+ * - Triggers email/push notifications for rescheduling.
145
+ * @param appointment - The rescheduled Appointment object with new times.
146
+ * @param previousAppointmentData - The appointment data before rescheduling.
147
+ * @returns {Promise<void>}
148
+ */
149
+ async handleAppointmentRescheduled(
150
+ appointment: Appointment,
151
+ previousAppointmentData: Appointment
152
+ ): Promise<void> {
153
+ console.log(
154
+ `[AppointmentAggregationService] Handling rescheduled appointment: ${appointment.id}`
155
+ );
156
+ // 1. Manage PatientRequirementInstances for PRE-requirements:
157
+ // - Mark old instances as SUPERSEDED_RESCHEDULE.
158
+ // - Create new instances for the new time OR update existing ones (more complex).
159
+ // - PatientRequirementsAdminService (via trigger) will manage notification updates.
160
+
161
+ // 2. Trigger calendar event update (e.g., this.calendarAdminService.updateEventForAppointment(appointment, previousAppointmentData))
162
+
163
+ // 3. Trigger email notifications for reschedule (e.g., this.appointmentMailingService.sendAppointmentRescheduledEmail(appointment))
164
+ }
165
+
166
+ /**
167
+ * Manages the links between a patient and the clinic/practitioner associated with an appointment.
168
+ * Adds links on creation, potentially removes them on cancellation if it's the only link.
169
+ * @param appointment - The appointment object.
170
+ * @param action - 'create' to add links, 'cancel' to evaluate removal.
171
+ * @returns {Promise<void>}
172
+ */
173
+ async managePatientClinicPractitionerLinks(
174
+ appointment: Appointment,
175
+ action: "create" | "cancel"
176
+ ): Promise<void> {
177
+ console.log(
178
+ `[AppointmentAggregationService] Managing patient-clinic-practitioner links for appointment ${appointment.id}, action: ${action}`
179
+ );
180
+ // Logic to add patientId to clinic.patientIds / practitioner.patientIds if 'create'
181
+ // Logic to check and potentially remove if 'cancel' and no other appointments link them.
182
+ // This would likely involve calling methods on a PatientAdminService, ClinicAdminService, PractitionerAdminService
183
+ }
184
+
185
+ /**
186
+ * Placeholder for a more generic calendar update trigger.
187
+ * @param appointment The appointment data.
188
+ * @param action The type of action ('create', 'update', 'delete').
189
+ * @param previousAppointmentData Optional, for 'update' actions.
190
+ * @returns {Promise<void>}
191
+ */
192
+ async triggerCalendarUpdate(
193
+ appointment: Appointment,
194
+ action: "create" | "update" | "delete",
195
+ previousAppointmentData?: Appointment
196
+ ): Promise<void> {
197
+ console.log(
198
+ `[AppointmentAggregationService] Triggering calendar update for appointment ${appointment.id}, action: ${action}`
199
+ );
200
+ // Call methods on a dedicated CalendarAdminService
201
+ }
202
+
203
+ /**
204
+ * Placeholder for a more generic email notification trigger.
205
+ * @param appointment The appointment data.
206
+ * @param emailType A string identifying the type of email (e.g., 'APPOINTMENT_CONFIRMED_PATIENT').
207
+ * @param previousAppointmentData Optional, for context.
208
+ * @returns {Promise<void>}
209
+ */
210
+ async triggerEmailNotification(
211
+ appointment: Appointment,
212
+ emailType: string,
213
+ previousAppointmentData?: Appointment
214
+ ): Promise<void> {
215
+ console.log(
216
+ `[AppointmentAggregationService] Triggering email type '${emailType}' for appointment ${appointment.id}`
217
+ );
218
+ // Call methods on a dedicated AppointmentMailingService
219
+ }
220
+
221
+ /**
222
+ * Handles logic for sending a review request after appointment completion.
223
+ * @param appointment - The completed appointment.
224
+ * @returns {Promise<void>}
225
+ */
226
+ async handleReviewRequest(appointment: Appointment): Promise<void> {
227
+ console.log(
228
+ `[AppointmentAggregationService] Handling review request for appointment ${appointment.id}`
229
+ );
230
+ // Logic to schedule or send a review request notification/email.
231
+ // Might involve checking if a review already exists.
232
+ }
233
+
234
+ // --- Helper methods --- //
235
+
236
+ /**
237
+ * Fetches requirement templates based on procedure ID and type (PRE/POST).
238
+ * @param procedureId The ID of the procedure.
239
+ * @param type 'PRE' or 'POST'.
240
+ * @returns {Promise<RequirementTemplate[]>}
241
+ */
242
+ private async fetchRequirementTemplates(
243
+ procedureId: string,
244
+ type: "PRE" | "POST" // Assuming type is stored on the template
245
+ ): Promise<RequirementTemplate[]> {
246
+ console.log(
247
+ `[AppointmentAggregationService] Fetching '${type}' requirement templates for procedure ${procedureId}`
248
+ );
249
+ // const templatesSnapshot = await this.db
250
+ // .collection(REQUIREMENTS_TEMPLATES_COLLECTION)
251
+ // .where("procedureIds", "array-contains", procedureId)
252
+ // .where("type", "==", type) // Assuming a 'type' field on the template
253
+ // .where("isActive", "==", true)
254
+ // .get();
255
+ // return templatesSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() } as RequirementTemplate));
256
+ return []; // Placeholder
257
+ }
258
+
259
+ /**
260
+ * Creates a PatientRequirementInstance document.
261
+ * @param appointment The parent appointment.
262
+ * @param template The RequirementTemplate to base the instance on.
263
+ * @param type 'PRE' or 'POST'.
264
+ * @returns {Promise<string>} ID of the created instance.
265
+ */
266
+ private async createPatientRequirementInstance(
267
+ appointment: Appointment,
268
+ template: RequirementTemplate,
269
+ type: "PRE" | "POST"
270
+ ): Promise<string> {
271
+ console.log(
272
+ `[AppointmentAggregationService] Creating '${type}' PatientRequirementInstance for appointment ${appointment.id} from template ${template.id}`
273
+ );
274
+ const newInstanceRef = this.db
275
+ .collection(PATIENTS_COLLECTION)
276
+ .doc(appointment.patientId)
277
+ .collection(PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME)
278
+ .doc(); // Auto-generate ID
279
+
280
+ // const instructions = template.instructions.map(instrTemplate => ({
281
+ // ...instrTemplate, // Spread common fields like instructionText, instructionId (if pre-defined)
282
+ // instructionId: instrTemplate.instructionId || this.db.collection('_').doc().id, // Ensure unique ID
283
+ // status: PatientInstructionStatus.PENDING_NOTIFICATION,
284
+ // dueTime: this.calculateInstructionDueTime(appointment, instrTemplate, type),
285
+ // createdAt: admin.firestore.FieldValue.serverTimestamp(),
286
+ // updatedAt: admin.firestore.FieldValue.serverTimestamp(),
287
+ // }));
288
+
289
+ // const instanceData: Omit<PatientRequirementInstance, 'id' | 'createdAt' | 'updatedAt'> = {
290
+ // patientId: appointment.patientId,
291
+ // appointmentId: appointment.id,
292
+ // originalRequirementId: template.id,
293
+ // requirementName: template.name,
294
+ // type: type, // 'PRE' or 'POST'
295
+ // overallStatus: PatientRequirementOverallStatus.ACTIVE,
296
+ // instructions: instructions,
297
+ // // ... other fields like clinicId, practitionerId if needed from appointment
298
+ // };
299
+
300
+ // await newInstanceRef.set({
301
+ // ...instanceData,
302
+ // createdAt: admin.firestore.FieldValue.serverTimestamp(),
303
+ // updatedAt: admin.firestore.FieldValue.serverTimestamp(),
304
+ // });
305
+ // return newInstanceRef.id;
306
+ return newInstanceRef.id; // Placeholder
307
+ }
308
+
309
+ /**
310
+ * Calculates the due time for an instruction.
311
+ * @param appointment The appointment.
312
+ * @param instructionTemplate The instruction template from RequirementTemplate.
313
+ * @param type 'PRE' or 'POST'.
314
+ * @returns {admin.firestore.Timestamp}
315
+ */
316
+ // private calculateInstructionDueTime(appointment: Appointment, instructionTemplate: any, type: 'PRE' | 'POST'): admin.firestore.Timestamp {
317
+ // const baseTime = type === 'PRE' ? appointment.appointmentStartTime : appointment.appointmentEndTime;
318
+ // // Logic to add/subtract instructionTemplate.offset (e.g., { value: 24, unit: 'hours' }) from baseTime
319
+ // return baseTime; // Placeholder
320
+ // }
321
+ }