@blackcode_sa/metaestetics-api 1.15.16 → 1.15.17

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 (42) hide show
  1. package/dist/admin/index.d.mts +377 -222
  2. package/dist/admin/index.d.ts +377 -222
  3. package/dist/admin/index.js +625 -206
  4. package/dist/admin/index.mjs +624 -206
  5. package/dist/backoffice/index.d.mts +24 -0
  6. package/dist/backoffice/index.d.ts +24 -0
  7. package/dist/index.d.mts +371 -4
  8. package/dist/index.d.ts +371 -4
  9. package/dist/index.js +2227 -1580
  10. package/dist/index.mjs +1543 -891
  11. package/package.json +1 -1
  12. package/src/admin/aggregation/appointment/README.md +24 -2
  13. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +46 -0
  14. package/src/admin/booking/README.md +61 -2
  15. package/src/admin/booking/booking.admin.ts +257 -0
  16. package/src/admin/booking/booking.calculator.ts +139 -1
  17. package/src/admin/booking/booking.types.ts +17 -0
  18. package/src/admin/calendar/README.md +56 -1
  19. package/src/admin/calendar/index.ts +1 -0
  20. package/src/admin/calendar/resource-calendar.admin.ts +198 -0
  21. package/src/config/index.ts +1 -0
  22. package/src/config/tiers.config.ts +116 -0
  23. package/src/services/index.ts +1 -0
  24. package/src/services/plan-config.service.ts +55 -0
  25. package/src/services/resource/README.md +119 -0
  26. package/src/services/resource/index.ts +1 -0
  27. package/src/services/resource/resource.service.ts +555 -0
  28. package/src/services/tier-enforcement.ts +15 -10
  29. package/src/types/appointment/index.ts +7 -0
  30. package/src/types/calendar/index.ts +1 -0
  31. package/src/types/clinic/index.ts +1 -0
  32. package/src/types/clinic/rbac.types.ts +3 -2
  33. package/src/types/index.ts +6 -0
  34. package/src/types/procedure/index.ts +6 -0
  35. package/src/types/resource/README.md +153 -0
  36. package/src/types/resource/index.ts +199 -0
  37. package/src/types/system/index.ts +1 -0
  38. package/src/types/system/planConfig.types.ts +86 -0
  39. package/src/validations/README.md +94 -0
  40. package/src/validations/index.ts +1 -0
  41. package/src/validations/procedure.schema.ts +12 -0
  42. package/src/validations/resource.schema.ts +57 -0
@@ -421,6 +421,7 @@ var BillingTransactionType = /* @__PURE__ */ ((BillingTransactionType2) => {
421
421
  BillingTransactionType2["SUBSCRIPTION_CANCELED"] = "subscription_canceled";
422
422
  BillingTransactionType2["SUBSCRIPTION_REACTIVATED"] = "subscription_reactivated";
423
423
  BillingTransactionType2["SUBSCRIPTION_DELETED"] = "subscription_deleted";
424
+ BillingTransactionType2["ADDON_PURCHASED"] = "addon_purchased";
424
425
  return BillingTransactionType2;
425
426
  })(BillingTransactionType || {});
426
427
 
@@ -524,7 +525,7 @@ var PatientRequirementOverallStatus = /* @__PURE__ */ ((PatientRequirementOveral
524
525
  var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
525
526
 
526
527
  // src/admin/aggregation/appointment/appointment.aggregation.service.ts
527
- import * as admin6 from "firebase-admin";
528
+ import * as admin7 from "firebase-admin";
528
529
 
529
530
  // src/backoffice/types/requirement.types.ts
530
531
  var REQUIREMENTS_COLLECTION = "backoffice_requirements";
@@ -543,6 +544,11 @@ var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
543
544
  var USER_FORMS_SUBCOLLECTION = "user-forms";
544
545
  var DOCTOR_FORMS_SUBCOLLECTION = "doctor-forms";
545
546
 
547
+ // src/types/resource/index.ts
548
+ var RESOURCES_COLLECTION = "resources";
549
+ var RESOURCE_INSTANCES_SUBCOLLECTION = "instances";
550
+ var RESOURCE_CALENDAR_SUBCOLLECTION = "calendar";
551
+
546
552
  // src/types/reviews/index.ts
547
553
  var REVIEWS_COLLECTION = "reviews";
548
554
 
@@ -621,9 +627,9 @@ var Logger = class {
621
627
 
622
628
  // src/admin/notifications/notifications.admin.ts
623
629
  var NotificationsAdmin = class {
624
- constructor(firestore19) {
630
+ constructor(firestore20) {
625
631
  this.expo = new Expo();
626
- this.db = firestore19 || admin2.firestore();
632
+ this.db = firestore20 || admin2.firestore();
627
633
  }
628
634
  /**
629
635
  * Dohvata notifikaciju po ID-u
@@ -1215,8 +1221,8 @@ var NotificationsAdmin = class {
1215
1221
 
1216
1222
  // src/admin/requirements/patient-requirements.admin.service.ts
1217
1223
  var PatientRequirementsAdminService = class {
1218
- constructor(firestore19) {
1219
- this.db = firestore19 || admin3.firestore();
1224
+ constructor(firestore20) {
1225
+ this.db = firestore20 || admin3.firestore();
1220
1226
  this.notificationsAdmin = new NotificationsAdmin(this.db);
1221
1227
  }
1222
1228
  /**
@@ -1544,8 +1550,8 @@ var PatientRequirementsAdminService = class {
1544
1550
  // src/admin/calendar/calendar.admin.service.ts
1545
1551
  import * as admin4 from "firebase-admin";
1546
1552
  var CalendarAdminService = class {
1547
- constructor(firestore19) {
1548
- this.db = firestore19 || admin4.firestore();
1553
+ constructor(firestore20) {
1554
+ this.db = firestore20 || admin4.firestore();
1549
1555
  Logger.info("[CalendarAdminService] Initialized.");
1550
1556
  }
1551
1557
  /**
@@ -1819,11 +1825,163 @@ var CalendarAdminService = class {
1819
1825
  }
1820
1826
  };
1821
1827
 
1828
+ // src/admin/calendar/resource-calendar.admin.ts
1829
+ import * as admin5 from "firebase-admin";
1830
+ var ResourceCalendarAdminService = class {
1831
+ constructor(firestore20) {
1832
+ this.db = firestore20 || admin5.firestore();
1833
+ Logger.info("[ResourceCalendarAdminService] Initialized.");
1834
+ }
1835
+ /**
1836
+ * Builds the Firestore document path for a resource calendar event.
1837
+ */
1838
+ getResourceCalendarEventPath(clinicBranchId, booking) {
1839
+ return `${CLINICS_COLLECTION}/${clinicBranchId}/${RESOURCES_COLLECTION}/${booking.resourceId}/${RESOURCE_INSTANCES_SUBCOLLECTION}/${booking.resourceInstanceId}/${RESOURCE_CALENDAR_SUBCOLLECTION}/${booking.calendarEventId}`;
1840
+ }
1841
+ /**
1842
+ * Updates the status of all resource calendar events associated with a given appointment.
1843
+ *
1844
+ * @param appointment - The appointment object containing resourceBookings.
1845
+ * @param newStatus - The new CalendarEventStatus to set.
1846
+ */
1847
+ async updateResourceBookingEventsStatus(appointment, newStatus) {
1848
+ const resourceBookings = appointment.resourceBookings;
1849
+ if (!resourceBookings || resourceBookings.length === 0) {
1850
+ Logger.debug(
1851
+ `[ResourceCalendarAdminService] No resource bookings on appointment ${appointment.id}, skipping status update.`
1852
+ );
1853
+ return;
1854
+ }
1855
+ Logger.info(
1856
+ `[ResourceCalendarAdminService] Updating ${resourceBookings.length} resource calendar event(s) to ${newStatus} for appointment ${appointment.id}`
1857
+ );
1858
+ const batch = this.db.batch();
1859
+ const serverTimestamp = admin5.firestore.FieldValue.serverTimestamp();
1860
+ for (const booking of resourceBookings) {
1861
+ const eventPath = this.getResourceCalendarEventPath(
1862
+ appointment.clinicBranchId,
1863
+ booking
1864
+ );
1865
+ const eventRef = this.db.doc(eventPath);
1866
+ batch.update(eventRef, {
1867
+ status: newStatus,
1868
+ updatedAt: serverTimestamp
1869
+ });
1870
+ Logger.debug(
1871
+ `[ResourceCalendarAdminService] Added status update for resource event ${booking.calendarEventId} (${booking.resourceName} / ${booking.resourceInstanceLabel})`
1872
+ );
1873
+ }
1874
+ try {
1875
+ await batch.commit();
1876
+ Logger.info(
1877
+ `[ResourceCalendarAdminService] Successfully updated ${resourceBookings.length} resource calendar event statuses for appointment ${appointment.id}.`
1878
+ );
1879
+ } catch (error) {
1880
+ Logger.error(
1881
+ `[ResourceCalendarAdminService] Error updating resource calendar event statuses for appointment ${appointment.id}:`,
1882
+ error
1883
+ );
1884
+ throw error;
1885
+ }
1886
+ }
1887
+ /**
1888
+ * Updates the eventTime (start and end) of all resource calendar events
1889
+ * associated with a given appointment.
1890
+ *
1891
+ * @param appointment - The appointment object containing resourceBookings.
1892
+ * @param newEventTime - The new event time with admin Timestamps.
1893
+ */
1894
+ async updateResourceBookingEventsTime(appointment, newEventTime) {
1895
+ const resourceBookings = appointment.resourceBookings;
1896
+ if (!resourceBookings || resourceBookings.length === 0) {
1897
+ Logger.debug(
1898
+ `[ResourceCalendarAdminService] No resource bookings on appointment ${appointment.id}, skipping time update.`
1899
+ );
1900
+ return;
1901
+ }
1902
+ Logger.info(
1903
+ `[ResourceCalendarAdminService] Updating ${resourceBookings.length} resource calendar event time(s) for appointment ${appointment.id}`
1904
+ );
1905
+ const batch = this.db.batch();
1906
+ const serverTimestamp = admin5.firestore.FieldValue.serverTimestamp();
1907
+ for (const booking of resourceBookings) {
1908
+ const eventPath = this.getResourceCalendarEventPath(
1909
+ appointment.clinicBranchId,
1910
+ booking
1911
+ );
1912
+ const eventRef = this.db.doc(eventPath);
1913
+ batch.update(eventRef, {
1914
+ eventTime: {
1915
+ start: newEventTime.start,
1916
+ end: newEventTime.end
1917
+ },
1918
+ updatedAt: serverTimestamp
1919
+ });
1920
+ Logger.debug(
1921
+ `[ResourceCalendarAdminService] Added time update for resource event ${booking.calendarEventId} (${booking.resourceName} / ${booking.resourceInstanceLabel})`
1922
+ );
1923
+ }
1924
+ try {
1925
+ await batch.commit();
1926
+ Logger.info(
1927
+ `[ResourceCalendarAdminService] Successfully updated ${resourceBookings.length} resource calendar event times for appointment ${appointment.id}.`
1928
+ );
1929
+ } catch (error) {
1930
+ Logger.error(
1931
+ `[ResourceCalendarAdminService] Error updating resource calendar event times for appointment ${appointment.id}:`,
1932
+ error
1933
+ );
1934
+ throw error;
1935
+ }
1936
+ }
1937
+ /**
1938
+ * Deletes all resource calendar events associated with a given appointment.
1939
+ *
1940
+ * @param appointment - The appointment object containing resourceBookings.
1941
+ */
1942
+ async deleteResourceBookingEvents(appointment) {
1943
+ const resourceBookings = appointment.resourceBookings;
1944
+ if (!resourceBookings || resourceBookings.length === 0) {
1945
+ Logger.debug(
1946
+ `[ResourceCalendarAdminService] No resource bookings on appointment ${appointment.id}, skipping deletion.`
1947
+ );
1948
+ return;
1949
+ }
1950
+ Logger.info(
1951
+ `[ResourceCalendarAdminService] Deleting ${resourceBookings.length} resource calendar event(s) for appointment ${appointment.id}`
1952
+ );
1953
+ const batch = this.db.batch();
1954
+ for (const booking of resourceBookings) {
1955
+ const eventPath = this.getResourceCalendarEventPath(
1956
+ appointment.clinicBranchId,
1957
+ booking
1958
+ );
1959
+ const eventRef = this.db.doc(eventPath);
1960
+ batch.delete(eventRef);
1961
+ Logger.debug(
1962
+ `[ResourceCalendarAdminService] Added deletion for resource event ${booking.calendarEventId} (${booking.resourceName} / ${booking.resourceInstanceLabel})`
1963
+ );
1964
+ }
1965
+ try {
1966
+ await batch.commit();
1967
+ Logger.info(
1968
+ `[ResourceCalendarAdminService] Successfully deleted ${resourceBookings.length} resource calendar events for appointment ${appointment.id}.`
1969
+ );
1970
+ } catch (error) {
1971
+ Logger.error(
1972
+ `[ResourceCalendarAdminService] Error deleting resource calendar events for appointment ${appointment.id}:`,
1973
+ error
1974
+ );
1975
+ throw error;
1976
+ }
1977
+ }
1978
+ };
1979
+
1822
1980
  // src/admin/mailing/appointment/appointment.mailing.service.ts
1823
1981
  import { DateTime } from "luxon";
1824
1982
 
1825
1983
  // src/admin/mailing/base.mailing.service.ts
1826
- import * as admin5 from "firebase-admin";
1984
+ import * as admin6 from "firebase-admin";
1827
1985
  var BaseMailingService = class {
1828
1986
  // Expecting the new mailgun.js client
1829
1987
  /**
@@ -1831,9 +1989,9 @@ var BaseMailingService = class {
1831
1989
  * @param firestore Firestore instance provided by the caller
1832
1990
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
1833
1991
  */
1834
- constructor(firestore19, mailgunClient) {
1992
+ constructor(firestore20, mailgunClient) {
1835
1993
  var _a;
1836
- this.db = firestore19;
1994
+ this.db = firestore20;
1837
1995
  this.mailgunClient = mailgunClient;
1838
1996
  if (!this.db) {
1839
1997
  Logger.error("[BaseMailingService] No Firestore instance provided");
@@ -1933,7 +2091,7 @@ var BaseMailingService = class {
1933
2091
  status: error.status,
1934
2092
  stack: error.stack
1935
2093
  } : null,
1936
- sentAt: admin5.firestore.FieldValue.serverTimestamp()
2094
+ sentAt: admin6.firestore.FieldValue.serverTimestamp()
1937
2095
  });
1938
2096
  Logger.info(
1939
2097
  `[BaseMailingService] Email log recorded. Success: ${success}`
@@ -2640,8 +2798,8 @@ var appointmentRescheduledProposalTemplate = `
2640
2798
  </html>
2641
2799
  `;
2642
2800
  var AppointmentMailingService = class extends BaseMailingService {
2643
- constructor(firestore19, mailgunClient) {
2644
- super(firestore19, mailgunClient);
2801
+ constructor(firestore20, mailgunClient) {
2802
+ super(firestore20, mailgunClient);
2645
2803
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
2646
2804
  Logger.info("[AppointmentMailingService] Initialized.");
2647
2805
  }
@@ -3081,8 +3239,8 @@ var AppointmentAggregationService = class {
3081
3239
  * @param mailgunClient - An initialized Mailgun client instance.
3082
3240
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
3083
3241
  */
3084
- constructor(mailgunClient, firestore19) {
3085
- this.db = firestore19 || admin6.firestore();
3242
+ constructor(mailgunClient, firestore20) {
3243
+ this.db = firestore20 || admin7.firestore();
3086
3244
  this.appointmentMailingService = new AppointmentMailingService(
3087
3245
  this.db,
3088
3246
  mailgunClient
@@ -3090,6 +3248,7 @@ var AppointmentAggregationService = class {
3090
3248
  );
3091
3249
  this.notificationsAdmin = new NotificationsAdmin(this.db);
3092
3250
  this.calendarAdminService = new CalendarAdminService(this.db);
3251
+ this.resourceCalendarAdminService = new ResourceCalendarAdminService(this.db);
3093
3252
  this.patientRequirementsAdminService = new PatientRequirementsAdminService(this.db);
3094
3253
  Logger.info("[AppointmentAggregationService] Initialized.");
3095
3254
  }
@@ -3231,6 +3390,10 @@ var AppointmentAggregationService = class {
3231
3390
  after,
3232
3391
  "confirmed" /* CONFIRMED */
3233
3392
  );
3393
+ await this.resourceCalendarAdminService.updateResourceBookingEventsStatus(
3394
+ after,
3395
+ "confirmed" /* CONFIRMED */
3396
+ );
3234
3397
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
3235
3398
  Logger.info(
3236
3399
  `[AggService] Sending appointment confirmed email to patient ${patientSensitiveInfo.email}`
@@ -3281,6 +3444,10 @@ var AppointmentAggregationService = class {
3281
3444
  after,
3282
3445
  "confirmed" /* CONFIRMED */
3283
3446
  );
3447
+ await this.resourceCalendarAdminService.updateResourceBookingEventsStatus(
3448
+ after,
3449
+ "confirmed" /* CONFIRMED */
3450
+ );
3284
3451
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
3285
3452
  Logger.info(
3286
3453
  `[AggService] Sending appointment confirmed email to patient ${patientSensitiveInfo.email}`
@@ -3348,6 +3515,10 @@ var AppointmentAggregationService = class {
3348
3515
  after,
3349
3516
  calendarStatus(after.status)
3350
3517
  );
3518
+ await this.resourceCalendarAdminService.updateResourceBookingEventsStatus(
3519
+ after,
3520
+ calendarStatus(after.status)
3521
+ );
3351
3522
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
3352
3523
  Logger.info(
3353
3524
  `[AggService] Sending appointment cancellation email to patient ${patientSensitiveInfo.email}`
@@ -3394,6 +3565,10 @@ var AppointmentAggregationService = class {
3394
3565
  after,
3395
3566
  "completed" /* COMPLETED */
3396
3567
  );
3568
+ await this.resourceCalendarAdminService.updateResourceBookingEventsStatus(
3569
+ after,
3570
+ "completed" /* COMPLETED */
3571
+ );
3397
3572
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
3398
3573
  Logger.info(
3399
3574
  `[AggService] Sending review request email to patient ${patientSensitiveInfo.email}`
@@ -3424,6 +3599,14 @@ var AppointmentAggregationService = class {
3424
3599
  after,
3425
3600
  "pending" /* PENDING */
3426
3601
  );
3602
+ await this.resourceCalendarAdminService.updateResourceBookingEventsTime(after, {
3603
+ start: after.appointmentStartTime,
3604
+ end: after.appointmentEndTime
3605
+ });
3606
+ await this.resourceCalendarAdminService.updateResourceBookingEventsStatus(
3607
+ after,
3608
+ "pending" /* PENDING */
3609
+ );
3427
3610
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
3428
3611
  Logger.info(
3429
3612
  `[AggService] Sending reschedule proposal email to patient ${patientSensitiveInfo.email}`
@@ -3467,6 +3650,10 @@ var AppointmentAggregationService = class {
3467
3650
  start: after.appointmentStartTime,
3468
3651
  end: after.appointmentEndTime
3469
3652
  });
3653
+ await this.resourceCalendarAdminService.updateResourceBookingEventsTime(after, {
3654
+ start: after.appointmentStartTime,
3655
+ end: after.appointmentEndTime
3656
+ });
3470
3657
  } else {
3471
3658
  Logger.warn(
3472
3659
  `[AggService] Independent time change detected for ${after.id} with status ${after.status}. Review implications for requirements and calendar.`
@@ -3511,6 +3698,7 @@ var AppointmentAggregationService = class {
3511
3698
  );
3512
3699
  }
3513
3700
  await this.calendarAdminService.deleteAppointmentCalendarEvents(deletedAppointment);
3701
+ await this.resourceCalendarAdminService.deleteResourceBookingEvents(deletedAppointment);
3514
3702
  }
3515
3703
  // --- Helper Methods for Aggregation Logic ---
3516
3704
  /**
@@ -3602,7 +3790,7 @@ var AppointmentAggregationService = class {
3602
3790
  } else if (template.timeframe.unit === "hours" /* HOURS */) {
3603
3791
  dueDateTime.setHours(dueDateTime.getHours() - notifyAtValue);
3604
3792
  }
3605
- dueTime = admin6.firestore.Timestamp.fromDate(dueDateTime);
3793
+ dueTime = admin7.firestore.Timestamp.fromDate(dueDateTime);
3606
3794
  }
3607
3795
  const actionableWindowHours = template.importance === "high" ? 1 : template.importance === "medium" ? 4 : 15;
3608
3796
  const instructionObject = {
@@ -3617,7 +3805,7 @@ var AppointmentAggregationService = class {
3617
3805
  status: "pendingNotification" /* PENDING_NOTIFICATION */,
3618
3806
  originalNotifyAtValue: notifyAtValue,
3619
3807
  originalTimeframeUnit: template.timeframe.unit,
3620
- updatedAt: admin6.firestore.Timestamp.now()
3808
+ updatedAt: admin7.firestore.Timestamp.now()
3621
3809
  // Use current server timestamp
3622
3810
  };
3623
3811
  return instructionObject;
@@ -3636,8 +3824,8 @@ var AppointmentAggregationService = class {
3636
3824
  overallStatus: "active" /* ACTIVE */,
3637
3825
  instructions,
3638
3826
  // Timestamps - cast to any to satisfy client-side Timestamp type for now
3639
- createdAt: admin6.firestore.FieldValue.serverTimestamp(),
3640
- updatedAt: admin6.firestore.FieldValue.serverTimestamp()
3827
+ createdAt: admin7.firestore.FieldValue.serverTimestamp(),
3828
+ updatedAt: admin7.firestore.FieldValue.serverTimestamp()
3641
3829
  };
3642
3830
  Logger.debug(
3643
3831
  `[AggService] Setting data for requirement: ${JSON.stringify({
@@ -3987,7 +4175,7 @@ var AppointmentAggregationService = class {
3987
4175
  } else if (template.timeframe.unit === "hours" /* HOURS */) {
3988
4176
  dueDateTime.setHours(dueDateTime.getHours() + notifyAtValue);
3989
4177
  }
3990
- dueTime = admin6.firestore.Timestamp.fromDate(dueDateTime);
4178
+ dueTime = admin7.firestore.Timestamp.fromDate(dueDateTime);
3991
4179
  }
3992
4180
  const actionableWindowHours = template.importance === "high" ? 1 : template.importance === "medium" ? 4 : 15;
3993
4181
  const instructionObject = {
@@ -4001,7 +4189,7 @@ var AppointmentAggregationService = class {
4001
4189
  status: "pendingNotification" /* PENDING_NOTIFICATION */,
4002
4190
  originalNotifyAtValue: notifyAtValue,
4003
4191
  originalTimeframeUnit: template.timeframe.unit,
4004
- updatedAt: admin6.firestore.Timestamp.now(),
4192
+ updatedAt: admin7.firestore.Timestamp.now(),
4005
4193
  notificationId: void 0,
4006
4194
  actionTakenAt: void 0
4007
4195
  };
@@ -4020,8 +4208,8 @@ var AppointmentAggregationService = class {
4020
4208
  instructions,
4021
4209
  sourceProcedures: reqWithSource.sourceProcedures,
4022
4210
  // Track which procedures this requirement comes from
4023
- createdAt: admin6.firestore.FieldValue.serverTimestamp(),
4024
- updatedAt: admin6.firestore.FieldValue.serverTimestamp()
4211
+ createdAt: admin7.firestore.FieldValue.serverTimestamp(),
4212
+ updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4025
4213
  };
4026
4214
  Logger.debug(
4027
4215
  `[AggService] Setting data for requirement: ${JSON.stringify({
@@ -4181,7 +4369,7 @@ var AppointmentAggregationService = class {
4181
4369
  if (instance.overallStatus !== newOverallStatus && instance.overallStatus !== "failedToProcess" /* FAILED_TO_PROCESS */) {
4182
4370
  batch.update(doc3.ref, {
4183
4371
  overallStatus: newOverallStatus,
4184
- updatedAt: admin6.firestore.FieldValue.serverTimestamp()
4372
+ updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4185
4373
  // Cast for now
4186
4374
  // Potentially also cancel individual instructions if not handled by another trigger
4187
4375
  // instructions: instance.instructions.map(instr => ({ ...instr, status: PatientInstructionStatus.CANCELLED, updatedAt: admin.firestore.FieldValue.serverTimestamp() as any }))
@@ -4257,17 +4445,17 @@ var AppointmentAggregationService = class {
4257
4445
  if (!hasDoctor || !hasClinic) {
4258
4446
  const patientRef = this.db.collection(PATIENTS_COLLECTION).doc(patientProfile.id);
4259
4447
  const updateData = {
4260
- updatedAt: admin6.firestore.FieldValue.serverTimestamp()
4448
+ updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4261
4449
  };
4262
4450
  if (!hasDoctor) {
4263
4451
  Logger.debug(
4264
4452
  `[AggService] Adding practitioner ${practitionerId} to patient ${patientProfile.id}`
4265
4453
  );
4266
- updateData.doctorIds = admin6.firestore.FieldValue.arrayUnion(practitionerId);
4454
+ updateData.doctorIds = admin7.firestore.FieldValue.arrayUnion(practitionerId);
4267
4455
  }
4268
4456
  if (!hasClinic) {
4269
4457
  Logger.debug(`[AggService] Adding clinic ${clinicId} to patient ${patientProfile.id}`);
4270
- updateData.clinicIds = admin6.firestore.FieldValue.arrayUnion(clinicId);
4458
+ updateData.clinicIds = admin7.firestore.FieldValue.arrayUnion(clinicId);
4271
4459
  }
4272
4460
  await patientRef.update(updateData);
4273
4461
  Logger.info(
@@ -4317,16 +4505,16 @@ var AppointmentAggregationService = class {
4317
4505
  Logger.debug(
4318
4506
  `[AggService] Removing practitioner ${practitionerId} from patient ${patientProfile.id}`
4319
4507
  );
4320
- updateData.doctorIds = admin6.firestore.FieldValue.arrayRemove(practitionerId);
4508
+ updateData.doctorIds = admin7.firestore.FieldValue.arrayRemove(practitionerId);
4321
4509
  updateNeeded = true;
4322
4510
  }
4323
4511
  if (activeClinicAppointments === 0 && ((_b = patientProfile.clinicIds) == null ? void 0 : _b.includes(clinicId))) {
4324
4512
  Logger.debug(`[AggService] Removing clinic ${clinicId} from patient ${patientProfile.id}`);
4325
- updateData.clinicIds = admin6.firestore.FieldValue.arrayRemove(clinicId);
4513
+ updateData.clinicIds = admin7.firestore.FieldValue.arrayRemove(clinicId);
4326
4514
  updateNeeded = true;
4327
4515
  }
4328
4516
  if (updateNeeded) {
4329
- updateData.updatedAt = admin6.firestore.FieldValue.serverTimestamp();
4517
+ updateData.updatedAt = admin7.firestore.FieldValue.serverTimestamp();
4330
4518
  await patientRef.update(updateData);
4331
4519
  Logger.info(`[AggService] Successfully removed links from patient ${patientProfile.id}`);
4332
4520
  } else {
@@ -4617,7 +4805,7 @@ var AppointmentAggregationService = class {
4617
4805
  userId: after.patientId,
4618
4806
  userRole: "patient" /* PATIENT */,
4619
4807
  notificationType: "procedureRecommendation" /* PROCEDURE_RECOMMENDATION */,
4620
- notificationTime: admin6.firestore.Timestamp.now(),
4808
+ notificationTime: admin7.firestore.Timestamp.now(),
4621
4809
  notificationTokens: (patientProfile == null ? void 0 : patientProfile.expoTokens) || [],
4622
4810
  title: "New Procedure Recommendation",
4623
4811
  body: `${((_c = after.practitionerInfo) == null ? void 0 : _c.name) || "Your doctor"} recommended "${recommendation.procedure.procedureName}" for you. Suggested timeframe: in ${timeframeText}`,
@@ -4663,15 +4851,15 @@ var AppointmentAggregationService = class {
4663
4851
  };
4664
4852
 
4665
4853
  // src/admin/aggregation/clinic/clinic.aggregation.service.ts
4666
- import * as admin7 from "firebase-admin";
4854
+ import * as admin8 from "firebase-admin";
4667
4855
  var CALENDAR_SUBCOLLECTION_ID = "calendar";
4668
4856
  var ClinicAggregationService = class {
4669
4857
  /**
4670
4858
  * Constructor for ClinicAggregationService.
4671
4859
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
4672
4860
  */
4673
- constructor(firestore19) {
4674
- this.db = firestore19 || admin7.firestore();
4861
+ constructor(firestore20) {
4862
+ this.db = firestore20 || admin8.firestore();
4675
4863
  }
4676
4864
  /**
4677
4865
  * Adds clinic information to a clinic group when a new clinic is created
@@ -4694,9 +4882,9 @@ var ClinicAggregationService = class {
4694
4882
  );
4695
4883
  try {
4696
4884
  await groupRef.update({
4697
- clinicsInfo: admin7.firestore.FieldValue.arrayUnion(clinicInfo),
4698
- clinicIds: admin7.firestore.FieldValue.arrayUnion(clinicId),
4699
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4885
+ clinicsInfo: admin8.firestore.FieldValue.arrayUnion(clinicInfo),
4886
+ clinicIds: admin8.firestore.FieldValue.arrayUnion(clinicId),
4887
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4700
4888
  });
4701
4889
  console.log(
4702
4890
  `[ClinicAggregationService] Successfully added ClinicInfo (ID: ${clinicId}) to Clinic Group ${clinicGroupId}.`
@@ -4741,7 +4929,7 @@ var ClinicAggregationService = class {
4741
4929
  const updatedClinicsInfo = [...filteredClinicsInfo, clinicInfo];
4742
4930
  batch.update(practitionerRef, {
4743
4931
  clinicsInfo: updatedClinicsInfo,
4744
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4932
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4745
4933
  });
4746
4934
  }
4747
4935
  }
@@ -4779,7 +4967,7 @@ var ClinicAggregationService = class {
4779
4967
  const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(procedureId);
4780
4968
  batch.update(procedureRef, {
4781
4969
  clinicInfo,
4782
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
4970
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4783
4971
  });
4784
4972
  }
4785
4973
  try {
@@ -4823,7 +5011,7 @@ var ClinicAggregationService = class {
4823
5011
  const updatedClinicsInfo = [...filteredClinicsInfo, clinicInfo];
4824
5012
  await groupRef.update({
4825
5013
  clinicsInfo: updatedClinicsInfo,
4826
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5014
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4827
5015
  });
4828
5016
  console.log(
4829
5017
  `[ClinicAggregationService] Successfully updated ClinicInfo (ID: ${clinicId}) in Clinic Group ${clinicGroupId}.`
@@ -4882,7 +5070,7 @@ var ClinicAggregationService = class {
4882
5070
  console.log(
4883
5071
  `[ClinicAggregationService] Querying upcoming calendar events via collection group '${CALENDAR_SUBCOLLECTION_ID}' for clinic ${clinicId} to update location.`
4884
5072
  );
4885
- const now = admin7.firestore.Timestamp.now();
5073
+ const now = admin8.firestore.Timestamp.now();
4886
5074
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID).where("clinicBranchId", "==", clinicId).where("eventTime.start", ">", now);
4887
5075
  try {
4888
5076
  const snapshot = await calendarEventsQuery.get();
@@ -4899,7 +5087,7 @@ var ClinicAggregationService = class {
4899
5087
  );
4900
5088
  batch.update(doc3.ref, {
4901
5089
  eventLocation: newLocation,
4902
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5090
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4903
5091
  });
4904
5092
  });
4905
5093
  await batch.commit();
@@ -4929,7 +5117,7 @@ var ClinicAggregationService = class {
4929
5117
  console.log(
4930
5118
  `[ClinicAggregationService] Querying upcoming calendar events for clinic ${clinicId} to update clinic info.`
4931
5119
  );
4932
- const now = admin7.firestore.Timestamp.now();
5120
+ const now = admin8.firestore.Timestamp.now();
4933
5121
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID).where("clinicBranchId", "==", clinicId).where("eventTime.start", ">", now);
4934
5122
  try {
4935
5123
  const snapshot = await calendarEventsQuery.get();
@@ -4946,7 +5134,7 @@ var ClinicAggregationService = class {
4946
5134
  );
4947
5135
  batch.update(doc3.ref, {
4948
5136
  clinicInfo,
4949
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5137
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4950
5138
  });
4951
5139
  });
4952
5140
  await batch.commit();
@@ -4995,7 +5183,7 @@ var ClinicAggregationService = class {
4995
5183
  batch.update(practitionerRef, {
4996
5184
  clinics: filteredClinicIds,
4997
5185
  clinicsInfo: filteredClinicsInfo,
4998
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5186
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
4999
5187
  });
5000
5188
  if (checkAndDeactivateDoctors) {
5001
5189
  practitionersToCheck.push(practitionerId);
@@ -5047,7 +5235,7 @@ var ClinicAggregationService = class {
5047
5235
  );
5048
5236
  batch.update(practitionerRef, {
5049
5237
  isActive: false,
5050
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5238
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5051
5239
  });
5052
5240
  deactivationCount++;
5053
5241
  } else {
@@ -5069,7 +5257,7 @@ var ClinicAggregationService = class {
5069
5257
  );
5070
5258
  batch.update(practitionerRef, {
5071
5259
  isActive: false,
5072
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5260
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5073
5261
  });
5074
5262
  deactivationCount++;
5075
5263
  }
@@ -5126,7 +5314,7 @@ var ClinicAggregationService = class {
5126
5314
  );
5127
5315
  batch.update(practitionerRef, {
5128
5316
  isActive: true,
5129
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5317
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5130
5318
  });
5131
5319
  reactivationCount++;
5132
5320
  } else {
@@ -5148,7 +5336,7 @@ var ClinicAggregationService = class {
5148
5336
  );
5149
5337
  batch.update(practitionerRef, {
5150
5338
  isActive: true,
5151
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5339
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5152
5340
  });
5153
5341
  reactivationCount++;
5154
5342
  }
@@ -5194,7 +5382,7 @@ var ClinicAggregationService = class {
5194
5382
  const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(procedureId);
5195
5383
  batch.update(procedureRef, {
5196
5384
  isActive: false,
5197
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5385
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5198
5386
  });
5199
5387
  }
5200
5388
  try {
@@ -5233,7 +5421,7 @@ var ClinicAggregationService = class {
5233
5421
  if (procedureDoc.exists) {
5234
5422
  batch.update(procedureRef, {
5235
5423
  isActive: true,
5236
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5424
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5237
5425
  });
5238
5426
  reactivationCount++;
5239
5427
  } else {
@@ -5292,7 +5480,7 @@ var ClinicAggregationService = class {
5292
5480
  await groupRef.update({
5293
5481
  clinics: filteredClinicIds,
5294
5482
  clinicsInfo: filteredClinicsInfo,
5295
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5483
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5296
5484
  });
5297
5485
  console.log(
5298
5486
  `[ClinicAggregationService] Successfully removed Clinic (ID: ${clinicId}) from Clinic Group ${clinicGroupId}.`
@@ -5329,8 +5517,8 @@ var ClinicAggregationService = class {
5329
5517
  for (const patientId of patientIds) {
5330
5518
  const patientRef = this.db.collection(PATIENTS_COLLECTION).doc(patientId);
5331
5519
  batch.update(patientRef, {
5332
- clinicIds: admin7.firestore.FieldValue.arrayRemove(clinicId),
5333
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5520
+ clinicIds: admin8.firestore.FieldValue.arrayRemove(clinicId),
5521
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5334
5522
  });
5335
5523
  }
5336
5524
  try {
@@ -5360,7 +5548,7 @@ var ClinicAggregationService = class {
5360
5548
  console.log(
5361
5549
  `[ClinicAggregationService] Querying upcoming calendar events for clinic ${clinicId} to cancel.`
5362
5550
  );
5363
- const now = admin7.firestore.Timestamp.now();
5551
+ const now = admin8.firestore.Timestamp.now();
5364
5552
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID).where("clinicBranchId", "==", clinicId).where("eventTime.start", ">", now);
5365
5553
  try {
5366
5554
  const snapshot = await calendarEventsQuery.get();
@@ -5378,7 +5566,7 @@ var ClinicAggregationService = class {
5378
5566
  batch.update(doc3.ref, {
5379
5567
  status: "CANCELED",
5380
5568
  cancelReason: "Clinic deleted",
5381
- updatedAt: admin7.firestore.FieldValue.serverTimestamp()
5569
+ updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5382
5570
  });
5383
5571
  });
5384
5572
  await batch.commit();
@@ -5396,14 +5584,14 @@ var ClinicAggregationService = class {
5396
5584
  };
5397
5585
 
5398
5586
  // src/admin/aggregation/forms/filled-forms.aggregation.service.ts
5399
- import * as admin8 from "firebase-admin";
5587
+ import * as admin9 from "firebase-admin";
5400
5588
  var FilledFormsAggregationService = class {
5401
5589
  /**
5402
5590
  * Constructor for FilledFormsAggregationService.
5403
5591
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
5404
5592
  */
5405
- constructor(firestore19) {
5406
- this.db = firestore19 || admin8.firestore();
5593
+ constructor(firestore20) {
5594
+ this.db = firestore20 || admin9.firestore();
5407
5595
  Logger.info("[FilledFormsAggregationService] Initialized");
5408
5596
  }
5409
5597
  /**
@@ -5462,12 +5650,12 @@ var FilledFormsAggregationService = class {
5462
5650
  path: `${APPOINTMENTS_COLLECTION}/${filledDocument.appointmentId}/${formSubcollection}/${filledDocument.id}`
5463
5651
  };
5464
5652
  if (filledDocument.updatedAt) {
5465
- linkedFormInfo.submittedAt = admin8.firestore.Timestamp.fromMillis(
5653
+ linkedFormInfo.submittedAt = admin9.firestore.Timestamp.fromMillis(
5466
5654
  filledDocument.updatedAt
5467
5655
  );
5468
5656
  }
5469
5657
  if (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */) {
5470
- linkedFormInfo.completedAt = admin8.firestore.Timestamp.fromMillis(
5658
+ linkedFormInfo.completedAt = admin9.firestore.Timestamp.fromMillis(
5471
5659
  filledDocument.updatedAt
5472
5660
  );
5473
5661
  }
@@ -5481,7 +5669,7 @@ var FilledFormsAggregationService = class {
5481
5669
  updatedLinkedForms.push(linkedFormInfo);
5482
5670
  updateData = {
5483
5671
  linkedForms: updatedLinkedForms,
5484
- updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5672
+ updatedAt: admin9.firestore.FieldValue.serverTimestamp()
5485
5673
  };
5486
5674
  let updatedLinkedFormIds = appointment.linkedFormIds || [];
5487
5675
  if (!updatedLinkedFormIds.includes(filledDocument.id)) {
@@ -5490,7 +5678,7 @@ var FilledFormsAggregationService = class {
5490
5678
  }
5491
5679
  if (filledDocument.isUserForm && filledDocument.isRequired && (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */)) {
5492
5680
  if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.includes(filledDocument.id)) {
5493
- updateData.pendingUserFormsIds = admin8.firestore.FieldValue.arrayRemove(
5681
+ updateData.pendingUserFormsIds = admin9.firestore.FieldValue.arrayRemove(
5494
5682
  filledDocument.id
5495
5683
  );
5496
5684
  Logger.info(
@@ -5536,21 +5724,21 @@ var FilledFormsAggregationService = class {
5536
5724
  (form) => form.formId === filledDocument.id
5537
5725
  );
5538
5726
  if (formToRemove) {
5539
- updateData.linkedForms = admin8.firestore.FieldValue.arrayRemove(formToRemove);
5727
+ updateData.linkedForms = admin9.firestore.FieldValue.arrayRemove(formToRemove);
5540
5728
  }
5541
5729
  }
5542
5730
  if (appointment.linkedFormIds && appointment.linkedFormIds.includes(filledDocument.id)) {
5543
- updateData.linkedFormIds = admin8.firestore.FieldValue.arrayRemove(filledDocument.id);
5731
+ updateData.linkedFormIds = admin9.firestore.FieldValue.arrayRemove(filledDocument.id);
5544
5732
  }
5545
5733
  if (filledDocument.isUserForm && filledDocument.isRequired) {
5546
5734
  if (filledDocument.status !== "completed" /* COMPLETED */ && filledDocument.status !== "signed" /* SIGNED */) {
5547
5735
  if (!appointment.pendingUserFormsIds || !appointment.pendingUserFormsIds.includes(filledDocument.id)) {
5548
- updateData.pendingUserFormsIds = appointment.pendingUserFormsIds ? admin8.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id];
5736
+ updateData.pendingUserFormsIds = appointment.pendingUserFormsIds ? admin9.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id];
5549
5737
  }
5550
5738
  }
5551
5739
  }
5552
5740
  if (Object.keys(updateData).length > 0) {
5553
- updateData.updatedAt = admin8.firestore.FieldValue.serverTimestamp();
5741
+ updateData.updatedAt = admin9.firestore.FieldValue.serverTimestamp();
5554
5742
  await appointmentRef.update(updateData);
5555
5743
  Logger.info(
5556
5744
  `[FilledFormsAggregationService] Successfully updated appointment ${filledDocument.appointmentId} after form deletion`
@@ -5590,8 +5778,8 @@ var FilledFormsAggregationService = class {
5590
5778
  return;
5591
5779
  }
5592
5780
  await appointmentRef.update({
5593
- pendingUserFormsIds: admin8.firestore.FieldValue.arrayUnion(filledDocument.id),
5594
- updatedAt: admin8.firestore.FieldValue.serverTimestamp()
5781
+ pendingUserFormsIds: admin9.firestore.FieldValue.arrayUnion(filledDocument.id),
5782
+ updatedAt: admin9.firestore.FieldValue.serverTimestamp()
5595
5783
  });
5596
5784
  Logger.info(
5597
5785
  `[FilledFormsAggregationService] Successfully added form ${filledDocument.id} to pendingUserFormsIds for appointment ${filledDocument.appointmentId}`
@@ -5607,11 +5795,11 @@ var FilledFormsAggregationService = class {
5607
5795
  };
5608
5796
 
5609
5797
  // src/admin/aggregation/patient/patient.aggregation.service.ts
5610
- import * as admin9 from "firebase-admin";
5798
+ import * as admin10 from "firebase-admin";
5611
5799
  var CALENDAR_SUBCOLLECTION_ID2 = "calendar";
5612
5800
  var PatientAggregationService = class {
5613
- constructor(firestore19) {
5614
- this.db = firestore19 || admin9.firestore();
5801
+ constructor(firestore20) {
5802
+ this.db = firestore20 || admin10.firestore();
5615
5803
  }
5616
5804
  // --- Methods for Patient Creation --- >
5617
5805
  // No specific aggregations defined for patient creation in the plan.
@@ -5632,7 +5820,7 @@ var PatientAggregationService = class {
5632
5820
  console.log(
5633
5821
  `[PatientAggregationService] Querying upcoming calendar events for patient ${patientId} to update patient info.`
5634
5822
  );
5635
- const now = admin9.firestore.Timestamp.now();
5823
+ const now = admin10.firestore.Timestamp.now();
5636
5824
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID2).where("patientId", "==", patientId).where("eventTime.start", ">", now);
5637
5825
  try {
5638
5826
  const snapshot = await calendarEventsQuery.get();
@@ -5649,7 +5837,7 @@ var PatientAggregationService = class {
5649
5837
  );
5650
5838
  batch.update(doc3.ref, {
5651
5839
  patientInfo,
5652
- updatedAt: admin9.firestore.FieldValue.serverTimestamp()
5840
+ updatedAt: admin10.firestore.FieldValue.serverTimestamp()
5653
5841
  });
5654
5842
  });
5655
5843
  await batch.commit();
@@ -5680,7 +5868,7 @@ var PatientAggregationService = class {
5680
5868
  console.log(
5681
5869
  `[PatientAggregationService] Querying upcoming calendar events for patient ${patientId} to cancel.`
5682
5870
  );
5683
- const now = admin9.firestore.Timestamp.now();
5871
+ const now = admin10.firestore.Timestamp.now();
5684
5872
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID2).where("patientId", "==", patientId).where("eventTime.start", ">", now);
5685
5873
  try {
5686
5874
  const snapshot = await calendarEventsQuery.get();
@@ -5698,7 +5886,7 @@ var PatientAggregationService = class {
5698
5886
  batch.update(doc3.ref, {
5699
5887
  status: "CANCELED",
5700
5888
  cancelReason: "Patient deleted",
5701
- updatedAt: admin9.firestore.FieldValue.serverTimestamp()
5889
+ updatedAt: admin10.firestore.FieldValue.serverTimestamp()
5702
5890
  });
5703
5891
  });
5704
5892
  await batch.commit();
@@ -5716,11 +5904,11 @@ var PatientAggregationService = class {
5716
5904
  };
5717
5905
 
5718
5906
  // src/admin/aggregation/practitioner/practitioner.aggregation.service.ts
5719
- import * as admin10 from "firebase-admin";
5907
+ import * as admin11 from "firebase-admin";
5720
5908
  var CALENDAR_SUBCOLLECTION_ID3 = "calendar";
5721
5909
  var PractitionerAggregationService = class {
5722
- constructor(firestore19) {
5723
- this.db = firestore19 || admin10.firestore();
5910
+ constructor(firestore20) {
5911
+ this.db = firestore20 || admin11.firestore();
5724
5912
  }
5725
5913
  /**
5726
5914
  * Adds practitioner information to a clinic when a new practitioner is created
@@ -5742,9 +5930,9 @@ var PractitionerAggregationService = class {
5742
5930
  const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
5743
5931
  try {
5744
5932
  await clinicRef.update({
5745
- doctors: admin10.firestore.FieldValue.arrayUnion(practitionerId),
5746
- doctorsInfo: admin10.firestore.FieldValue.arrayUnion(doctorInfo),
5747
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
5933
+ doctors: admin11.firestore.FieldValue.arrayUnion(practitionerId),
5934
+ doctorsInfo: admin11.firestore.FieldValue.arrayUnion(doctorInfo),
5935
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5748
5936
  });
5749
5937
  console.log(
5750
5938
  `[PractitionerAggregationService] Successfully added practitioner ${practitionerId} to clinic ${clinicId}.`
@@ -5778,14 +5966,14 @@ var PractitionerAggregationService = class {
5778
5966
  for (const clinicId of clinicIds) {
5779
5967
  const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
5780
5968
  batch.update(clinicRef, {
5781
- doctorsInfo: admin10.firestore.FieldValue.arrayRemove({
5969
+ doctorsInfo: admin11.firestore.FieldValue.arrayRemove({
5782
5970
  id: practitionerId
5783
5971
  }),
5784
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
5972
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5785
5973
  });
5786
5974
  batch.update(clinicRef, {
5787
- doctorsInfo: admin10.firestore.FieldValue.arrayUnion(doctorInfo),
5788
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
5975
+ doctorsInfo: admin11.firestore.FieldValue.arrayUnion(doctorInfo),
5976
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5789
5977
  });
5790
5978
  }
5791
5979
  try {
@@ -5823,7 +6011,7 @@ var PractitionerAggregationService = class {
5823
6011
  const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(procedureId);
5824
6012
  batch.update(procedureRef, {
5825
6013
  doctorInfo,
5826
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6014
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5827
6015
  });
5828
6016
  }
5829
6017
  try {
@@ -5855,7 +6043,7 @@ var PractitionerAggregationService = class {
5855
6043
  console.log(
5856
6044
  `[PractitionerAggregationService] Querying upcoming calendar events for practitioner ${practitionerId} to update practitioner info.`
5857
6045
  );
5858
- const now = admin10.firestore.Timestamp.now();
6046
+ const now = admin11.firestore.Timestamp.now();
5859
6047
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID3).where("practitionerId", "==", practitionerId).where("eventTime.start", ">", now);
5860
6048
  try {
5861
6049
  const snapshot = await calendarEventsQuery.get();
@@ -5872,7 +6060,7 @@ var PractitionerAggregationService = class {
5872
6060
  );
5873
6061
  batch.update(doc3.ref, {
5874
6062
  practitionerInfo,
5875
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6063
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5876
6064
  });
5877
6065
  });
5878
6066
  await batch.commit();
@@ -5907,12 +6095,12 @@ var PractitionerAggregationService = class {
5907
6095
  for (const clinicId of clinicIds) {
5908
6096
  const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
5909
6097
  batch.update(clinicRef, {
5910
- doctors: admin10.firestore.FieldValue.arrayRemove(practitionerId),
6098
+ doctors: admin11.firestore.FieldValue.arrayRemove(practitionerId),
5911
6099
  // Remove all doctor info objects where id matches the practitioner ID
5912
- doctorsInfo: admin10.firestore.FieldValue.arrayRemove({
6100
+ doctorsInfo: admin11.firestore.FieldValue.arrayRemove({
5913
6101
  id: practitionerId
5914
6102
  }),
5915
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6103
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5916
6104
  });
5917
6105
  }
5918
6106
  try {
@@ -5943,7 +6131,7 @@ var PractitionerAggregationService = class {
5943
6131
  console.log(
5944
6132
  `[PractitionerAggregationService] Querying upcoming calendar events for practitioner ${practitionerId} to cancel.`
5945
6133
  );
5946
- const now = admin10.firestore.Timestamp.now();
6134
+ const now = admin11.firestore.Timestamp.now();
5947
6135
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID3).where("practitionerId", "==", practitionerId).where("eventTime.start", ">", now);
5948
6136
  try {
5949
6137
  const snapshot = await calendarEventsQuery.get();
@@ -5961,7 +6149,7 @@ var PractitionerAggregationService = class {
5961
6149
  batch.update(doc3.ref, {
5962
6150
  status: "CANCELED",
5963
6151
  cancelReason: "Practitioner deleted",
5964
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6152
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
5965
6153
  });
5966
6154
  });
5967
6155
  await batch.commit();
@@ -5996,8 +6184,8 @@ var PractitionerAggregationService = class {
5996
6184
  for (const patientId of patientIds) {
5997
6185
  const patientRef = this.db.collection(PATIENTS_COLLECTION).doc(patientId);
5998
6186
  batch.update(patientRef, {
5999
- doctorIds: admin10.firestore.FieldValue.arrayRemove(practitionerId),
6000
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6187
+ doctorIds: admin11.firestore.FieldValue.arrayRemove(practitionerId),
6188
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6001
6189
  });
6002
6190
  }
6003
6191
  try {
@@ -6033,7 +6221,7 @@ var PractitionerAggregationService = class {
6033
6221
  const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(procedureId);
6034
6222
  batch.update(procedureRef, {
6035
6223
  isActive: false,
6036
- updatedAt: admin10.firestore.FieldValue.serverTimestamp()
6224
+ updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6037
6225
  });
6038
6226
  }
6039
6227
  try {
@@ -6052,15 +6240,15 @@ var PractitionerAggregationService = class {
6052
6240
  };
6053
6241
 
6054
6242
  // src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts
6055
- import * as admin11 from "firebase-admin";
6243
+ import * as admin12 from "firebase-admin";
6056
6244
  var PractitionerInviteAggregationService = class {
6057
6245
  /**
6058
6246
  * Constructor for PractitionerInviteAggregationService.
6059
6247
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
6060
6248
  * @param mailingService Optional mailing service for sending emails
6061
6249
  */
6062
- constructor(firestore19, mailingService) {
6063
- this.db = firestore19 || admin11.firestore();
6250
+ constructor(firestore20, mailingService) {
6251
+ this.db = firestore20 || admin12.firestore();
6064
6252
  this.mailingService = mailingService;
6065
6253
  Logger.info("[PractitionerInviteAggregationService] Initialized.");
6066
6254
  }
@@ -6357,9 +6545,9 @@ var PractitionerInviteAggregationService = class {
6357
6545
  try {
6358
6546
  const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
6359
6547
  await clinicRef.update({
6360
- doctors: admin11.firestore.FieldValue.arrayUnion(doctorInfo.id),
6361
- doctorsInfo: admin11.firestore.FieldValue.arrayUnion(doctorInfo),
6362
- updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6548
+ doctors: admin12.firestore.FieldValue.arrayUnion(doctorInfo.id),
6549
+ doctorsInfo: admin12.firestore.FieldValue.arrayUnion(doctorInfo),
6550
+ updatedAt: admin12.firestore.FieldValue.serverTimestamp()
6363
6551
  });
6364
6552
  Logger.info(
6365
6553
  `[PractitionerInviteAggService] Successfully added practitioner ${doctorInfo.id} to clinic ${clinicId}`
@@ -6394,7 +6582,7 @@ var PractitionerInviteAggregationService = class {
6394
6582
  const updatedDoctorsInfo = [...filteredDoctorsInfo, doctorInfo];
6395
6583
  await clinicRef.update({
6396
6584
  doctorsInfo: updatedDoctorsInfo,
6397
- updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6585
+ updatedAt: admin12.firestore.FieldValue.serverTimestamp()
6398
6586
  });
6399
6587
  Logger.info(
6400
6588
  `[PractitionerInviteAggService] Successfully updated practitioner ${doctorInfo.id} info in clinic ${clinicId}`
@@ -6429,14 +6617,14 @@ var PractitionerInviteAggregationService = class {
6429
6617
  clinicId: clinicInfo.id,
6430
6618
  workingHours: invite.proposedWorkingHours,
6431
6619
  isActive: true,
6432
- createdAt: admin11.firestore.Timestamp.now(),
6433
- updatedAt: admin11.firestore.Timestamp.now()
6620
+ createdAt: admin12.firestore.Timestamp.now(),
6621
+ updatedAt: admin12.firestore.Timestamp.now()
6434
6622
  };
6435
6623
  await practitionerRef.update({
6436
- clinics: admin11.firestore.FieldValue.arrayUnion(clinicInfo.id),
6437
- clinicsInfo: admin11.firestore.FieldValue.arrayUnion(clinicInfo),
6438
- clinicWorkingHours: admin11.firestore.FieldValue.arrayUnion(workingHours),
6439
- updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6624
+ clinics: admin12.firestore.FieldValue.arrayUnion(clinicInfo.id),
6625
+ clinicsInfo: admin12.firestore.FieldValue.arrayUnion(clinicInfo),
6626
+ clinicWorkingHours: admin12.firestore.FieldValue.arrayUnion(workingHours),
6627
+ updatedAt: admin12.firestore.FieldValue.serverTimestamp()
6440
6628
  });
6441
6629
  Logger.info(
6442
6630
  `[PractitionerInviteAggService] Successfully added clinic ${clinicInfo.id} to practitioner ${practitionerId}`
@@ -6473,7 +6661,7 @@ var PractitionerInviteAggregationService = class {
6473
6661
  ...wh,
6474
6662
  workingHours: invite.proposedWorkingHours,
6475
6663
  isActive: true,
6476
- updatedAt: admin11.firestore.Timestamp.now()
6664
+ updatedAt: admin12.firestore.Timestamp.now()
6477
6665
  };
6478
6666
  }
6479
6667
  return wh;
@@ -6483,13 +6671,13 @@ var PractitionerInviteAggregationService = class {
6483
6671
  clinicId: invite.clinicId,
6484
6672
  workingHours: invite.proposedWorkingHours,
6485
6673
  isActive: true,
6486
- createdAt: admin11.firestore.Timestamp.now(),
6487
- updatedAt: admin11.firestore.Timestamp.now()
6674
+ createdAt: admin12.firestore.Timestamp.now(),
6675
+ updatedAt: admin12.firestore.Timestamp.now()
6488
6676
  });
6489
6677
  }
6490
6678
  await practitionerRef.update({
6491
6679
  clinicWorkingHours: updatedWorkingHours,
6492
- updatedAt: admin11.firestore.FieldValue.serverTimestamp()
6680
+ updatedAt: admin12.firestore.FieldValue.serverTimestamp()
6493
6681
  });
6494
6682
  Logger.info(
6495
6683
  `[PractitionerInviteAggService] Successfully updated working hours for practitioner ${practitionerId} at clinic ${invite.clinicId}`
@@ -6566,8 +6754,8 @@ var PractitionerInviteAggregationService = class {
6566
6754
  var _a, _b, _c, _d, _e, _f;
6567
6755
  if (!this.mailingService) return;
6568
6756
  try {
6569
- const admin19 = await this.fetchClinicAdminById(invite.invitedBy);
6570
- if (!admin19) {
6757
+ const admin20 = await this.fetchClinicAdminById(invite.invitedBy);
6758
+ if (!admin20) {
6571
6759
  Logger.warn(
6572
6760
  `[PractitionerInviteAggService] Admin ${invite.invitedBy} not found, using clinic contact email as fallback`
6573
6761
  );
@@ -6605,7 +6793,7 @@ var PractitionerInviteAggregationService = class {
6605
6793
  );
6606
6794
  return;
6607
6795
  }
6608
- const adminName = `${admin19.contactInfo.firstName} ${admin19.contactInfo.lastName}`;
6796
+ const adminName = `${admin20.contactInfo.firstName} ${admin20.contactInfo.lastName}`;
6609
6797
  const notificationData = {
6610
6798
  invite,
6611
6799
  practitioner: {
@@ -6621,7 +6809,7 @@ var PractitionerInviteAggregationService = class {
6621
6809
  clinic: {
6622
6810
  name: clinic.name,
6623
6811
  adminName,
6624
- adminEmail: admin19.contactInfo.email
6812
+ adminEmail: admin20.contactInfo.email
6625
6813
  // Use the specific admin's email
6626
6814
  },
6627
6815
  context: {
@@ -6657,8 +6845,8 @@ var PractitionerInviteAggregationService = class {
6657
6845
  var _a, _b, _c, _d, _e, _f;
6658
6846
  if (!this.mailingService) return;
6659
6847
  try {
6660
- const admin19 = await this.fetchClinicAdminById(invite.invitedBy);
6661
- if (!admin19) {
6848
+ const admin20 = await this.fetchClinicAdminById(invite.invitedBy);
6849
+ if (!admin20) {
6662
6850
  Logger.warn(
6663
6851
  `[PractitionerInviteAggService] Admin ${invite.invitedBy} not found, using clinic contact email as fallback`
6664
6852
  );
@@ -6696,7 +6884,7 @@ var PractitionerInviteAggregationService = class {
6696
6884
  );
6697
6885
  return;
6698
6886
  }
6699
- const adminName = `${admin19.contactInfo.firstName} ${admin19.contactInfo.lastName}`;
6887
+ const adminName = `${admin20.contactInfo.firstName} ${admin20.contactInfo.lastName}`;
6700
6888
  const notificationData = {
6701
6889
  invite,
6702
6890
  practitioner: {
@@ -6710,7 +6898,7 @@ var PractitionerInviteAggregationService = class {
6710
6898
  clinic: {
6711
6899
  name: clinic.name,
6712
6900
  adminName,
6713
- adminEmail: admin19.contactInfo.email
6901
+ adminEmail: admin20.contactInfo.email
6714
6902
  // Use the specific admin's email
6715
6903
  },
6716
6904
  context: {
@@ -6739,11 +6927,11 @@ var PractitionerInviteAggregationService = class {
6739
6927
  };
6740
6928
 
6741
6929
  // src/admin/aggregation/procedure/procedure.aggregation.service.ts
6742
- import * as admin12 from "firebase-admin";
6930
+ import * as admin13 from "firebase-admin";
6743
6931
  var CALENDAR_SUBCOLLECTION_ID4 = "calendar";
6744
6932
  var ProcedureAggregationService = class {
6745
- constructor(firestore19) {
6746
- this.db = firestore19 || admin12.firestore();
6933
+ constructor(firestore20) {
6934
+ this.db = firestore20 || admin13.firestore();
6747
6935
  }
6748
6936
  /**
6749
6937
  * Adds procedure information to a practitioner when a new procedure is created
@@ -6767,9 +6955,9 @@ var ProcedureAggregationService = class {
6767
6955
  const practitionerRef = this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId);
6768
6956
  try {
6769
6957
  const updateData = {
6770
- procedureIds: admin12.firestore.FieldValue.arrayUnion(procedureId),
6771
- proceduresInfo: admin12.firestore.FieldValue.arrayUnion(procedureSummary),
6772
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
6958
+ procedureIds: admin13.firestore.FieldValue.arrayUnion(procedureId),
6959
+ proceduresInfo: admin13.firestore.FieldValue.arrayUnion(procedureSummary),
6960
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6773
6961
  };
6774
6962
  if (isFreeConsultation) {
6775
6963
  await this.db.runTransaction(async (transaction) => {
@@ -6832,9 +7020,9 @@ var ProcedureAggregationService = class {
6832
7020
  const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
6833
7021
  try {
6834
7022
  await clinicRef.update({
6835
- procedures: admin12.firestore.FieldValue.arrayUnion(procedureId),
6836
- proceduresInfo: admin12.firestore.FieldValue.arrayUnion(procedureSummary),
6837
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7023
+ procedures: admin13.firestore.FieldValue.arrayUnion(procedureId),
7024
+ proceduresInfo: admin13.firestore.FieldValue.arrayUnion(procedureSummary),
7025
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6838
7026
  });
6839
7027
  console.log(
6840
7028
  `[ProcedureAggregationService] Successfully added procedure ${procedureId} to clinic ${clinicId}.`
@@ -6886,7 +7074,7 @@ var ProcedureAggregationService = class {
6886
7074
  updatedProceduresInfo.push(procedureSummary);
6887
7075
  transaction.update(practitionerRef, {
6888
7076
  proceduresInfo: updatedProceduresInfo,
6889
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7077
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6890
7078
  });
6891
7079
  });
6892
7080
  console.log(
@@ -6939,7 +7127,7 @@ var ProcedureAggregationService = class {
6939
7127
  updatedProceduresInfo.push(procedureSummary);
6940
7128
  transaction.update(clinicRef, {
6941
7129
  proceduresInfo: updatedProceduresInfo,
6942
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7130
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6943
7131
  });
6944
7132
  });
6945
7133
  console.log(
@@ -6969,7 +7157,7 @@ var ProcedureAggregationService = class {
6969
7157
  console.log(
6970
7158
  `[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to update procedure info.`
6971
7159
  );
6972
- const now = admin12.firestore.Timestamp.now();
7160
+ const now = admin13.firestore.Timestamp.now();
6973
7161
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID4).where("procedureId", "==", procedureId).where("eventTime.start", ">", now);
6974
7162
  try {
6975
7163
  const snapshot = await calendarEventsQuery.get();
@@ -6986,7 +7174,7 @@ var ProcedureAggregationService = class {
6986
7174
  );
6987
7175
  batch.update(doc3.ref, {
6988
7176
  procedureInfo,
6989
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7177
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6990
7178
  });
6991
7179
  });
6992
7180
  await batch.commit();
@@ -7016,7 +7204,7 @@ var ProcedureAggregationService = class {
7016
7204
  console.log(
7017
7205
  `[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to cancel.`
7018
7206
  );
7019
- const now = admin12.firestore.Timestamp.now();
7207
+ const now = admin13.firestore.Timestamp.now();
7020
7208
  const calendarEventsQuery = this.db.collectionGroup(CALENDAR_SUBCOLLECTION_ID4).where("procedureId", "==", procedureId).where("eventTime.start", ">", now);
7021
7209
  try {
7022
7210
  const snapshot = await calendarEventsQuery.get();
@@ -7034,7 +7222,7 @@ var ProcedureAggregationService = class {
7034
7222
  batch.update(doc3.ref, {
7035
7223
  status: "CANCELED",
7036
7224
  cancelReason: "Procedure deleted or inactivated",
7037
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7225
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7038
7226
  });
7039
7227
  });
7040
7228
  await batch.commit();
@@ -7091,9 +7279,9 @@ var ProcedureAggregationService = class {
7091
7279
  (p) => p.id !== procedureId
7092
7280
  );
7093
7281
  const updateData = {
7094
- procedureIds: admin12.firestore.FieldValue.arrayRemove(procedureId),
7282
+ procedureIds: admin13.firestore.FieldValue.arrayRemove(procedureId),
7095
7283
  proceduresInfo: updatedProceduresInfo,
7096
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7284
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7097
7285
  };
7098
7286
  if (isFreeConsultation && procedureClinicId) {
7099
7287
  const currentFreeConsultations = practitionerData.freeConsultations || {};
@@ -7159,9 +7347,9 @@ var ProcedureAggregationService = class {
7159
7347
  (p) => p.id !== procedureId
7160
7348
  );
7161
7349
  transaction.update(clinicRef, {
7162
- procedures: admin12.firestore.FieldValue.arrayRemove(procedureId),
7350
+ procedures: admin13.firestore.FieldValue.arrayRemove(procedureId),
7163
7351
  proceduresInfo: updatedProceduresInfo,
7164
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7352
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7165
7353
  });
7166
7354
  });
7167
7355
  console.log(
@@ -7238,7 +7426,7 @@ var ProcedureAggregationService = class {
7238
7426
  }
7239
7427
  transaction.update(practitionerRef, {
7240
7428
  freeConsultations: updatedFreeConsultations,
7241
- updatedAt: admin12.firestore.FieldValue.serverTimestamp()
7429
+ updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7242
7430
  });
7243
7431
  });
7244
7432
  console.log(
@@ -7255,14 +7443,14 @@ var ProcedureAggregationService = class {
7255
7443
  };
7256
7444
 
7257
7445
  // src/admin/aggregation/reviews/reviews.aggregation.service.ts
7258
- import * as admin13 from "firebase-admin";
7446
+ import * as admin14 from "firebase-admin";
7259
7447
  var ReviewsAggregationService = class {
7260
7448
  /**
7261
7449
  * Constructor for ReviewsAggregationService.
7262
7450
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
7263
7451
  */
7264
- constructor(firestore19) {
7265
- this.db = firestore19 || admin13.firestore();
7452
+ constructor(firestore20) {
7453
+ this.db = firestore20 || admin14.firestore();
7266
7454
  }
7267
7455
  /**
7268
7456
  * Process a newly created review and update all related entities
@@ -7405,7 +7593,7 @@ var ReviewsAggregationService = class {
7405
7593
  };
7406
7594
  await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
7407
7595
  reviewInfo: updatedReviewInfo2,
7408
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7596
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7409
7597
  });
7410
7598
  console.log(
7411
7599
  `[ReviewsAggregationService] Reset review info for clinic: ${clinicId}`
@@ -7444,7 +7632,7 @@ var ReviewsAggregationService = class {
7444
7632
  };
7445
7633
  await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
7446
7634
  reviewInfo: updatedReviewInfo,
7447
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7635
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7448
7636
  });
7449
7637
  console.log(
7450
7638
  `[ReviewsAggregationService] Updated review info for clinic: ${clinicId}`
@@ -7494,7 +7682,7 @@ var ReviewsAggregationService = class {
7494
7682
  };
7495
7683
  await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
7496
7684
  reviewInfo: updatedReviewInfo2,
7497
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7685
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7498
7686
  });
7499
7687
  await this.updateDoctorInfoInProcedures(practitionerId, 0);
7500
7688
  console.log(
@@ -7534,7 +7722,7 @@ var ReviewsAggregationService = class {
7534
7722
  };
7535
7723
  await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
7536
7724
  reviewInfo: updatedReviewInfo,
7537
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7725
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7538
7726
  });
7539
7727
  await this.updateDoctorInfoInProcedures(
7540
7728
  practitionerId,
@@ -7601,7 +7789,7 @@ var ReviewsAggregationService = class {
7601
7789
  };
7602
7790
  await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
7603
7791
  reviewInfo: updatedReviewInfo2,
7604
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7792
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7605
7793
  });
7606
7794
  console.log(
7607
7795
  `[ReviewsAggregationService] Reset review info for procedure: ${procedureId}`
@@ -7640,7 +7828,7 @@ var ReviewsAggregationService = class {
7640
7828
  };
7641
7829
  await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
7642
7830
  reviewInfo: updatedReviewInfo,
7643
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7831
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7644
7832
  });
7645
7833
  console.log(
7646
7834
  `[ReviewsAggregationService] Updated review info for procedure: ${procedureId}`
@@ -7668,7 +7856,7 @@ var ReviewsAggregationService = class {
7668
7856
  const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(docSnapshot.id);
7669
7857
  batch.update(procedureRef, {
7670
7858
  "doctorInfo.rating": rating,
7671
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7859
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7672
7860
  });
7673
7861
  });
7674
7862
  await batch.commit();
@@ -7711,7 +7899,7 @@ var ReviewsAggregationService = class {
7711
7899
  practitionerReview: review.practitionerReview,
7712
7900
  procedureReview: review.procedureReview,
7713
7901
  extendedProcedureReviews: review.extendedProcedureReviews,
7714
- updatedAt: admin13.firestore.FieldValue.serverTimestamp()
7902
+ updatedAt: admin14.firestore.FieldValue.serverTimestamp()
7715
7903
  });
7716
7904
  await batch.commit();
7717
7905
  console.log(
@@ -7742,7 +7930,7 @@ var ReviewsAggregationService = class {
7742
7930
  };
7743
7931
 
7744
7932
  // src/admin/analytics/analytics.admin.service.ts
7745
- import * as admin14 from "firebase-admin";
7933
+ import * as admin15 from "firebase-admin";
7746
7934
 
7747
7935
  // src/services/analytics/analytics.service.ts
7748
7936
  import { where as where2, Timestamp as Timestamp3 } from "firebase/firestore";
@@ -10805,8 +10993,8 @@ var AnalyticsAdminService = class {
10805
10993
  *
10806
10994
  * @param firestore - Admin Firestore instance (optional, defaults to admin.firestore())
10807
10995
  */
10808
- constructor(firestore19) {
10809
- this.db = firestore19 || admin14.firestore();
10996
+ constructor(firestore20) {
10997
+ this.db = firestore20 || admin15.firestore();
10810
10998
  const mockApp = {
10811
10999
  name: "[DEFAULT]",
10812
11000
  options: {},
@@ -10843,12 +11031,12 @@ var AnalyticsAdminService = class {
10843
11031
  }
10844
11032
  if (params.startDate) {
10845
11033
  const startDate = params.startDate instanceof Date ? params.startDate : params.startDate.toDate();
10846
- const startTimestamp = admin14.firestore.Timestamp.fromDate(startDate);
11034
+ const startTimestamp = admin15.firestore.Timestamp.fromDate(startDate);
10847
11035
  query3 = query3.where("appointmentStartTime", ">=", startTimestamp);
10848
11036
  }
10849
11037
  if (params.endDate) {
10850
11038
  const endDate = params.endDate instanceof Date ? params.endDate : params.endDate.toDate();
10851
- const endTimestamp = admin14.firestore.Timestamp.fromDate(endDate);
11039
+ const endTimestamp = admin15.firestore.Timestamp.fromDate(endDate);
10852
11040
  query3 = query3.where("appointmentStartTime", "<=", endTimestamp);
10853
11041
  }
10854
11042
  const snapshot = await query3.get();
@@ -10999,6 +11187,13 @@ var BookingAvailabilityCalculator = class {
10999
11187
  availableIntervals,
11000
11188
  practitionerCalendarEvents
11001
11189
  );
11190
+ if (request.resourceCalendarEventsMap) {
11191
+ availableIntervals = this.applyResourceAvailability(
11192
+ availableIntervals,
11193
+ request.resourceCalendarEventsMap,
11194
+ timeframe
11195
+ );
11196
+ }
11002
11197
  console.log(
11003
11198
  `After all filters, have ${availableIntervals.length} available intervals`
11004
11199
  );
@@ -11404,18 +11599,98 @@ var BookingAvailabilityCalculator = class {
11404
11599
  }
11405
11600
  return result;
11406
11601
  }
11602
+ /**
11603
+ * Apply resource availability constraints to the available intervals.
11604
+ * For each required resource:
11605
+ * - Compute each instance's availability (full timeframe minus its busy events)
11606
+ * - Union all instance availabilities (at least one must be free)
11607
+ * Then intersect all resource availabilities with the current doctor availability.
11608
+ *
11609
+ * @param intervals - Current available intervals (doctor availability)
11610
+ * @param resourceEventsMap - Map of resource data with per-instance calendar events
11611
+ * @param timeframe - Overall timeframe being considered
11612
+ * @returns Intervals where doctor AND all required resources are available
11613
+ */
11614
+ static applyResourceAvailability(intervals, resourceEventsMap, timeframe) {
11615
+ if (!intervals.length) return [];
11616
+ const resourceIds = Object.keys(resourceEventsMap);
11617
+ if (resourceIds.length === 0) return intervals;
11618
+ console.log(
11619
+ `Applying resource availability for ${resourceIds.length} resource(s)`
11620
+ );
11621
+ let result = intervals;
11622
+ for (const resourceId of resourceIds) {
11623
+ const resourceData = resourceEventsMap[resourceId];
11624
+ const resourceAvailability = this.computeResourceAvailability(
11625
+ resourceData.instanceEvents,
11626
+ timeframe
11627
+ );
11628
+ console.log(
11629
+ `Resource "${resourceData.resourceName}" (qty=${resourceData.quantity}): ${resourceAvailability.length} available intervals`
11630
+ );
11631
+ result = this.intersectIntervals(result, resourceAvailability);
11632
+ if (result.length === 0) {
11633
+ console.log(
11634
+ `No availability remaining after applying resource "${resourceData.resourceName}"`
11635
+ );
11636
+ return [];
11637
+ }
11638
+ }
11639
+ return result;
11640
+ }
11641
+ /**
11642
+ * Compute combined availability for a single resource across all its instances.
11643
+ * For each instance, subtract its busy events from the full timeframe.
11644
+ * Then union all instance availabilities — if at least one instance is free, the resource is available.
11645
+ *
11646
+ * @param instanceEvents - Calendar events keyed by instanceId
11647
+ * @param timeframe - Overall timeframe
11648
+ * @returns Combined availability intervals for the resource
11649
+ */
11650
+ static computeResourceAvailability(instanceEvents, timeframe) {
11651
+ const instanceIds = Object.keys(instanceEvents);
11652
+ if (instanceIds.length === 0) {
11653
+ return [{ start: timeframe.start, end: timeframe.end }];
11654
+ }
11655
+ const allInstanceAvailabilities = [];
11656
+ for (const instanceId of instanceIds) {
11657
+ const events = instanceEvents[instanceId];
11658
+ let instanceAvailability = [
11659
+ { start: timeframe.start, end: timeframe.end }
11660
+ ];
11661
+ for (const event of events) {
11662
+ const isBlockingEvent = event.eventType === "blocking" /* BLOCKING */ || event.eventType === "break" /* BREAK */ || event.eventType === "free_day" /* FREE_DAY */;
11663
+ const isActiveBooking = event.status === "pending" /* PENDING */ || event.status === "confirmed" /* CONFIRMED */;
11664
+ if (isBlockingEvent || isActiveBooking) {
11665
+ const busyInterval = {
11666
+ start: event.eventTime.start,
11667
+ end: event.eventTime.end
11668
+ };
11669
+ const newAvailability = [];
11670
+ for (const interval of instanceAvailability) {
11671
+ newAvailability.push(
11672
+ ...this.subtractInterval(interval, busyInterval)
11673
+ );
11674
+ }
11675
+ instanceAvailability = newAvailability;
11676
+ }
11677
+ }
11678
+ allInstanceAvailabilities.push(...instanceAvailability);
11679
+ }
11680
+ return this.mergeOverlappingIntervals(allInstanceAvailabilities);
11681
+ }
11407
11682
  };
11408
11683
  /** Default scheduling interval in minutes if not specified by the clinic */
11409
11684
  BookingAvailabilityCalculator.DEFAULT_INTERVAL_MINUTES = 15;
11410
11685
 
11411
11686
  // src/admin/booking/booking.admin.ts
11412
- import * as admin16 from "firebase-admin";
11687
+ import * as admin17 from "firebase-admin";
11413
11688
 
11414
11689
  // src/admin/documentation-templates/document-manager.admin.ts
11415
- import * as admin15 from "firebase-admin";
11690
+ import * as admin16 from "firebase-admin";
11416
11691
  var DocumentManagerAdminService = class {
11417
- constructor(firestore19) {
11418
- this.db = firestore19;
11692
+ constructor(firestore20) {
11693
+ this.db = firestore20;
11419
11694
  }
11420
11695
  /**
11421
11696
  * Adds operations to a Firestore batch to initialize all linked forms for a new appointment
@@ -11518,7 +11793,7 @@ var DocumentManagerAdminService = class {
11518
11793
  };
11519
11794
  }
11520
11795
  const templateIds = technologyTemplates.map((t) => t.templateId);
11521
- const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(admin15.firestore.FieldPath.documentId(), "in", templateIds).get();
11796
+ const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(admin16.firestore.FieldPath.documentId(), "in", templateIds).get();
11522
11797
  const templatesMap = /* @__PURE__ */ new Map();
11523
11798
  templatesSnapshot.forEach((doc3) => {
11524
11799
  templatesMap.set(doc3.id, doc3.data());
@@ -11584,8 +11859,8 @@ var BookingAdmin = class {
11584
11859
  * Creates a new BookingAdmin instance
11585
11860
  * @param firestore - Firestore instance provided by the caller
11586
11861
  */
11587
- constructor(firestore19) {
11588
- this.db = firestore19 || admin16.firestore();
11862
+ constructor(firestore20) {
11863
+ this.db = firestore20 || admin17.firestore();
11589
11864
  this.documentManagerAdmin = new DocumentManagerAdminService(this.db);
11590
11865
  }
11591
11866
  /**
@@ -11598,7 +11873,7 @@ var BookingAdmin = class {
11598
11873
  * @returns Promise resolving to an array of available booking slots
11599
11874
  */
11600
11875
  async getAvailableBookingSlots(clinicId, practitionerId, procedureId, timeframe) {
11601
- var _a;
11876
+ var _a, _b;
11602
11877
  try {
11603
11878
  Logger.info("[BookingAdmin] Starting availability calculation", {
11604
11879
  clinicId,
@@ -11607,8 +11882,8 @@ var BookingAdmin = class {
11607
11882
  timeframeStart: timeframe.start instanceof Date ? timeframe.start.toISOString() : timeframe.start.toDate().toISOString(),
11608
11883
  timeframeEnd: timeframe.end instanceof Date ? timeframe.end.toISOString() : timeframe.end.toDate().toISOString()
11609
11884
  });
11610
- const start = timeframe.start instanceof Date ? admin16.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
11611
- const end = timeframe.end instanceof Date ? admin16.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
11885
+ const start = timeframe.start instanceof Date ? admin17.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
11886
+ const end = timeframe.end instanceof Date ? admin17.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
11612
11887
  Logger.debug("[BookingAdmin] Fetching clinic data", { clinicId });
11613
11888
  const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
11614
11889
  if (!clinicDoc.exists) {
@@ -11668,6 +11943,74 @@ var BookingAdmin = class {
11668
11943
  Logger.debug("[BookingAdmin] Retrieved practitioner calendar events", {
11669
11944
  count: practitionerCalendarEvents.length
11670
11945
  });
11946
+ let resourceCalendarEventsMap;
11947
+ if (procedure.resourceRequirements && procedure.resourceRequirements.length > 0) {
11948
+ Logger.debug(
11949
+ "[BookingAdmin] Procedure has resource requirements, fetching resource data",
11950
+ {
11951
+ resourceRequirementCount: procedure.resourceRequirements.length,
11952
+ resourceIds: procedure.resourceRequirements.map(
11953
+ (r) => r.resourceId
11954
+ )
11955
+ }
11956
+ );
11957
+ resourceCalendarEventsMap = {};
11958
+ for (const requirement of procedure.resourceRequirements) {
11959
+ const resourceDoc = await this.db.collection(CLINICS_COLLECTION).doc(clinicId).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).get();
11960
+ if (!resourceDoc.exists) {
11961
+ Logger.warn(
11962
+ "[BookingAdmin] Required resource not found, skipping",
11963
+ {
11964
+ resourceId: requirement.resourceId,
11965
+ resourceName: requirement.resourceName
11966
+ }
11967
+ );
11968
+ continue;
11969
+ }
11970
+ const resource = resourceDoc.data();
11971
+ if (resource.status !== "active" /* ACTIVE */) {
11972
+ Logger.warn(
11973
+ "[BookingAdmin] Required resource is not active, skipping",
11974
+ {
11975
+ resourceId: requirement.resourceId,
11976
+ resourceStatus: resource.status
11977
+ }
11978
+ );
11979
+ continue;
11980
+ }
11981
+ const instancesSnapshot = await this.db.collection(CLINICS_COLLECTION).doc(clinicId).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).collection(RESOURCE_INSTANCES_SUBCOLLECTION).where("status", "==", "active" /* ACTIVE */).get();
11982
+ const instanceEvents = {};
11983
+ for (const instanceDoc of instancesSnapshot.docs) {
11984
+ const instanceId = instanceDoc.id;
11985
+ const MAX_EVENT_DURATION_MS = 30 * 24 * 60 * 60 * 1e3;
11986
+ const queryStart = admin17.firestore.Timestamp.fromMillis(
11987
+ start.toMillis() - MAX_EVENT_DURATION_MS
11988
+ );
11989
+ const eventsSnapshot = await this.db.collection(CLINICS_COLLECTION).doc(clinicId).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).collection(RESOURCE_INSTANCES_SUBCOLLECTION).doc(instanceId).collection(RESOURCE_CALENDAR_SUBCOLLECTION).where("eventTime.start", ">=", queryStart).where("eventTime.start", "<=", end).orderBy("eventTime.start").get();
11990
+ const events = eventsSnapshot.docs.map((doc3) => ({ ...doc3.data(), id: doc3.id })).filter(
11991
+ (event) => event.eventTime.end.toMillis() > start.toMillis()
11992
+ );
11993
+ instanceEvents[instanceId] = this.convertEventsTimestamps(
11994
+ events
11995
+ );
11996
+ }
11997
+ resourceCalendarEventsMap[requirement.resourceId] = {
11998
+ resourceId: requirement.resourceId,
11999
+ resourceName: requirement.resourceName,
12000
+ quantity: resource.quantity,
12001
+ instanceEvents
12002
+ };
12003
+ Logger.debug("[BookingAdmin] Fetched resource calendar data", {
12004
+ resourceId: requirement.resourceId,
12005
+ resourceName: requirement.resourceName,
12006
+ instanceCount: instancesSnapshot.size,
12007
+ totalEvents: Object.values(instanceEvents).reduce(
12008
+ (sum, events) => sum + events.length,
12009
+ 0
12010
+ )
12011
+ });
12012
+ }
12013
+ }
11671
12014
  const convertedTimeframe = {
11672
12015
  start: this.adminTimestampToClientTimestamp(start),
11673
12016
  end: this.adminTimestampToClientTimestamp(end)
@@ -11681,7 +12024,8 @@ var BookingAdmin = class {
11681
12024
  practitionerCalendarEvents: this.convertEventsTimestamps(
11682
12025
  practitionerCalendarEvents
11683
12026
  ),
11684
- tz: clinic.location.tz || "UTC"
12027
+ tz: clinic.location.tz || "UTC",
12028
+ ...resourceCalendarEventsMap && { resourceCalendarEventsMap }
11685
12029
  };
11686
12030
  Logger.info("[BookingAdmin] Calling availability calculator", {
11687
12031
  calculatorInputReady: true,
@@ -11689,12 +12033,14 @@ var BookingAdmin = class {
11689
12033
  (end.toMillis() - start.toMillis()) / (1e3 * 60 * 60)
11690
12034
  ),
11691
12035
  clinicEventsCount: clinicCalendarEvents.length,
11692
- practitionerEventsCount: practitionerCalendarEvents.length
12036
+ practitionerEventsCount: practitionerCalendarEvents.length,
12037
+ resourceRequirementCount: ((_b = procedure.resourceRequirements) == null ? void 0 : _b.length) || 0,
12038
+ hasResourceData: !!resourceCalendarEventsMap
11693
12039
  });
11694
12040
  const result = BookingAvailabilityCalculator.calculateSlots(request);
11695
12041
  const availableSlotsResult = {
11696
12042
  availableSlots: result.availableSlots.map((slot) => ({
11697
- start: admin16.firestore.Timestamp.fromMillis(slot.start.toMillis())
12043
+ start: admin17.firestore.Timestamp.fromMillis(slot.start.toMillis())
11698
12044
  }))
11699
12045
  };
11700
12046
  Logger.info(
@@ -11757,7 +12103,7 @@ var BookingAdmin = class {
11757
12103
  endTime: end.toDate().toISOString()
11758
12104
  });
11759
12105
  const MAX_EVENT_DURATION_MS = 30 * 24 * 60 * 60 * 1e3;
11760
- const queryStart = admin16.firestore.Timestamp.fromMillis(
12106
+ const queryStart = admin17.firestore.Timestamp.fromMillis(
11761
12107
  start.toMillis() - MAX_EVENT_DURATION_MS
11762
12108
  );
11763
12109
  const eventsRef = this.db.collection(`clinics/${clinicId}/calendar`).where("eventTime.start", ">=", queryStart).where("eventTime.start", "<=", end).orderBy("eventTime.start");
@@ -11813,7 +12159,7 @@ var BookingAdmin = class {
11813
12159
  endTime: end.toDate().toISOString()
11814
12160
  });
11815
12161
  const MAX_EVENT_DURATION_MS = 30 * 24 * 60 * 60 * 1e3;
11816
- const queryStart = admin16.firestore.Timestamp.fromMillis(
12162
+ const queryStart = admin17.firestore.Timestamp.fromMillis(
11817
12163
  start.toMillis() - MAX_EVENT_DURATION_MS
11818
12164
  );
11819
12165
  const eventsRef = this.db.collection(`practitioners/${practitionerId}/calendar`).where("eventTime.start", ">=", queryStart).where("eventTime.start", "<=", end).orderBy("eventTime.start");
@@ -11892,8 +12238,8 @@ var BookingAdmin = class {
11892
12238
  `[BookingAdmin] Orchestrating appointment creation for patient ${data.patientId} by user ${authenticatedUserId}`
11893
12239
  );
11894
12240
  const batch = this.db.batch();
11895
- const adminTsNow = admin16.firestore.Timestamp.now();
11896
- const serverTimestampValue = admin16.firestore.FieldValue.serverTimestamp();
12241
+ const adminTsNow = admin17.firestore.Timestamp.now();
12242
+ const serverTimestampValue = admin17.firestore.FieldValue.serverTimestamp();
11897
12243
  try {
11898
12244
  if (!data.patientId || !data.procedureId || !data.appointmentStartTime || !data.appointmentEndTime) {
11899
12245
  return {
@@ -11990,7 +12336,7 @@ var BookingAdmin = class {
11990
12336
  fullName: `${(patientSensitiveData == null ? void 0 : patientSensitiveData.firstName) || ""} ${(patientSensitiveData == null ? void 0 : patientSensitiveData.lastName) || ""}`.trim() || patientProfileData.displayName,
11991
12337
  email: (patientSensitiveData == null ? void 0 : patientSensitiveData.email) || "",
11992
12338
  phone: (patientSensitiveData == null ? void 0 : patientSensitiveData.phoneNumber) || patientProfileData.phoneNumber || null,
11993
- dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth || admin16.firestore.Timestamp.now(),
12339
+ dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth || admin17.firestore.Timestamp.now(),
11994
12340
  gender: (patientSensitiveData == null ? void 0 : patientSensitiveData.gender) || "other" /* OTHER */
11995
12341
  };
11996
12342
  const newAppointmentId = this.db.collection(APPOINTMENTS_COLLECTION).doc().id;
@@ -12072,6 +12418,76 @@ var BookingAdmin = class {
12072
12418
  this.db.collection(CLINICS_COLLECTION).doc(clinicData.id).collection(CALENDAR_COLLECTION).doc(practitionerCalendarEventId),
12073
12419
  clinicCalendarEventData
12074
12420
  );
12421
+ let resourceBookings = [];
12422
+ if (procedure.resourceRequirements && procedure.resourceRequirements.length > 0) {
12423
+ console.log(
12424
+ `[BookingAdmin] Allocating resources for appointment ${newAppointmentId}, ${procedure.resourceRequirements.length} resource(s) required`
12425
+ );
12426
+ for (const requirement of procedure.resourceRequirements) {
12427
+ const instancesSnapshot = await this.db.collection(CLINICS_COLLECTION).doc(clinicData.id).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).collection(RESOURCE_INSTANCES_SUBCOLLECTION).where("status", "==", "active" /* ACTIVE */).orderBy("index").get();
12428
+ if (instancesSnapshot.empty) {
12429
+ return {
12430
+ success: false,
12431
+ error: `Resource "${requirement.resourceName}" has no active instances available.`
12432
+ };
12433
+ }
12434
+ let allocatedInstance = null;
12435
+ for (const instanceDoc of instancesSnapshot.docs) {
12436
+ const instance = {
12437
+ ...instanceDoc.data(),
12438
+ id: instanceDoc.id
12439
+ };
12440
+ const overlappingEventsSnapshot = await this.db.collection(CLINICS_COLLECTION).doc(clinicData.id).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).collection(RESOURCE_INSTANCES_SUBCOLLECTION).doc(instance.id).collection(RESOURCE_CALENDAR_SUBCOLLECTION).where("eventTime.start", "<", data.appointmentEndTime).where("status", "in", [
12441
+ "pending" /* PENDING */,
12442
+ "confirmed" /* CONFIRMED */,
12443
+ "checked_in" /* CHECKED_IN */,
12444
+ "in_progress" /* IN_PROGRESS */
12445
+ ]).get();
12446
+ const hasOverlap = overlappingEventsSnapshot.docs.some((doc3) => {
12447
+ const event = doc3.data();
12448
+ return event.eventTime.end.toMillis() > data.appointmentStartTime.toMillis();
12449
+ });
12450
+ if (!hasOverlap) {
12451
+ allocatedInstance = instance;
12452
+ break;
12453
+ }
12454
+ }
12455
+ if (!allocatedInstance) {
12456
+ return {
12457
+ success: false,
12458
+ error: `Resource "${requirement.resourceName}" is not available at the selected time. All instances are booked.`
12459
+ };
12460
+ }
12461
+ const resourceCalendarEventRef = this.db.collection(CLINICS_COLLECTION).doc(clinicData.id).collection(RESOURCES_COLLECTION).doc(requirement.resourceId).collection(RESOURCE_INSTANCES_SUBCOLLECTION).doc(allocatedInstance.id).collection(RESOURCE_CALENDAR_SUBCOLLECTION).doc();
12462
+ const resourceCalendarEventData = {
12463
+ id: resourceCalendarEventRef.id,
12464
+ resourceId: requirement.resourceId,
12465
+ resourceInstanceId: allocatedInstance.id,
12466
+ clinicBranchId: clinicData.id,
12467
+ eventType: "resource_booking" /* RESOURCE_BOOKING */,
12468
+ appointmentId: newAppointmentId,
12469
+ procedureId: procedure.id,
12470
+ practitionerId: practitionerData.id,
12471
+ patientId: data.patientId,
12472
+ eventTime: eventTimeForCalendarEvents,
12473
+ status: initialCalendarEventStatus,
12474
+ eventName: `${procedure.name} - ${allocatedInstance.label}`,
12475
+ createdAt: serverTimestampValue,
12476
+ updatedAt: serverTimestampValue
12477
+ };
12478
+ batch.set(resourceCalendarEventRef, resourceCalendarEventData);
12479
+ resourceBookings.push({
12480
+ resourceId: requirement.resourceId,
12481
+ resourceName: requirement.resourceName,
12482
+ resourceInstanceId: allocatedInstance.id,
12483
+ resourceInstanceLabel: allocatedInstance.label,
12484
+ calendarEventId: resourceCalendarEventRef.id
12485
+ });
12486
+ console.log(
12487
+ `[BookingAdmin] Allocated resource "${requirement.resourceName}" \u2192 instance "${allocatedInstance.label}" (${allocatedInstance.id})`
12488
+ );
12489
+ }
12490
+ }
12075
12491
  let initializedFormsInfo = [];
12076
12492
  let pendingUserFormTemplateIds = [];
12077
12493
  let allLinkedFormTemplateIds = [];
@@ -12154,6 +12570,7 @@ var BookingAdmin = class {
12154
12570
  isRecurring: false,
12155
12571
  recurringAppointmentId: null,
12156
12572
  isArchived: false,
12573
+ ...resourceBookings.length > 0 && { resourceBookings },
12157
12574
  createdAt: adminTsNow,
12158
12575
  updatedAt: adminTsNow
12159
12576
  };
@@ -12255,7 +12672,7 @@ var BookingAdmin = class {
12255
12672
  };
12256
12673
 
12257
12674
  // src/admin/free-consultation/free-consultation-utils.admin.ts
12258
- import * as admin17 from "firebase-admin";
12675
+ import * as admin18 from "firebase-admin";
12259
12676
 
12260
12677
  // src/backoffice/types/category.types.ts
12261
12678
  var CATEGORIES_COLLECTION = "backoffice_categories";
@@ -12268,10 +12685,10 @@ var TECHNOLOGIES_COLLECTION = "technologies";
12268
12685
 
12269
12686
  // src/admin/free-consultation/free-consultation-utils.admin.ts
12270
12687
  async function freeConsultationInfrastructure(db) {
12271
- const firestore19 = db || admin17.firestore();
12688
+ const firestore20 = db || admin18.firestore();
12272
12689
  try {
12273
12690
  console.log("[freeConsultationInfrastructure] Checking free consultation infrastructure...");
12274
- const technologyRef = firestore19.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
12691
+ const technologyRef = firestore20.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
12275
12692
  const technologyDoc = await technologyRef.get();
12276
12693
  if (technologyDoc.exists) {
12277
12694
  console.log(
@@ -12280,7 +12697,7 @@ async function freeConsultationInfrastructure(db) {
12280
12697
  return true;
12281
12698
  }
12282
12699
  console.log("[freeConsultationInfrastructure] Creating free consultation infrastructure...");
12283
- await createFreeConsultationInfrastructure(firestore19);
12700
+ await createFreeConsultationInfrastructure(firestore20);
12284
12701
  console.log(
12285
12702
  "[freeConsultationInfrastructure] Successfully created free consultation infrastructure"
12286
12703
  );
@@ -12477,8 +12894,8 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
12477
12894
  * @param firestore Firestore instance provided by the caller
12478
12895
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
12479
12896
  */
12480
- constructor(firestore19, mailgunClient) {
12481
- super(firestore19, mailgunClient);
12897
+ constructor(firestore20, mailgunClient) {
12898
+ super(firestore20, mailgunClient);
12482
12899
  this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
12483
12900
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
12484
12901
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
@@ -13341,8 +13758,8 @@ var ExistingPractitionerInviteMailingService = class extends BaseMailingService
13341
13758
  * @param firestore Firestore instance provided by the caller
13342
13759
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
13343
13760
  */
13344
- constructor(firestore19, mailgunClient) {
13345
- super(firestore19, mailgunClient);
13761
+ constructor(firestore20, mailgunClient) {
13762
+ super(firestore20, mailgunClient);
13346
13763
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
13347
13764
  this.DEFAULT_FROM_ADDRESS = "Metaesthetics <no-reply@mg.metaesthetics.net>";
13348
13765
  }
@@ -13824,8 +14241,8 @@ var PatientInviteMailingService = class extends BaseMailingService {
13824
14241
  * @param firestore - Firestore instance provided by the caller
13825
14242
  * @param mailgunClient - Mailgun client instance (mailgun.js v10+) provided by the caller
13826
14243
  */
13827
- constructor(firestore19, mailgunClient) {
13828
- super(firestore19, mailgunClient);
14244
+ constructor(firestore20, mailgunClient) {
14245
+ super(firestore20, mailgunClient);
13829
14246
  this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/patient/register";
13830
14247
  this.DEFAULT_SUBJECT = "Claim Your Patient Profile - Metaesthetics";
13831
14248
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
@@ -14300,8 +14717,8 @@ var ClinicWelcomeMailingService = class extends BaseMailingService {
14300
14717
  * @param firestore - Firestore instance provided by the caller
14301
14718
  * @param mailgunClient - Mailgun client instance (mailgun.js v10+) provided by the caller
14302
14719
  */
14303
- constructor(firestore19, mailgunClient) {
14304
- super(firestore19, mailgunClient);
14720
+ constructor(firestore20, mailgunClient) {
14721
+ super(firestore20, mailgunClient);
14305
14722
  this.DEFAULT_DASHBOARD_URL = "https://app.metaesthetics.net/dashboard";
14306
14723
  this.DEFAULT_SUPPORT_EMAIL = "support@metaesthetics.net";
14307
14724
  this.DEFAULT_SUBJECT = "Welcome to Metaesthetics - Your Clinic Registration is Complete";
@@ -14471,14 +14888,14 @@ var ClinicWelcomeMailingService = class extends BaseMailingService {
14471
14888
  };
14472
14889
 
14473
14890
  // src/admin/users/user-profile.admin.ts
14474
- import * as admin18 from "firebase-admin";
14891
+ import * as admin19 from "firebase-admin";
14475
14892
  var UserProfileAdminService = class {
14476
14893
  /**
14477
14894
  * Constructor for UserProfileAdminService
14478
14895
  * @param firestore Optional Firestore instance. If not provided, uses the default admin SDK instance.
14479
14896
  */
14480
- constructor(firestore19) {
14481
- this.db = firestore19 || admin18.firestore();
14897
+ constructor(firestore20) {
14898
+ this.db = firestore20 || admin19.firestore();
14482
14899
  }
14483
14900
  /**
14484
14901
  * Creates a blank user profile with minimal information
@@ -14495,9 +14912,9 @@ var UserProfileAdminService = class {
14495
14912
  roles: [],
14496
14913
  // Empty roles array as requested
14497
14914
  isAnonymous: authUserData.isAnonymous,
14498
- createdAt: admin18.firestore.FieldValue.serverTimestamp(),
14499
- updatedAt: admin18.firestore.FieldValue.serverTimestamp(),
14500
- lastLoginAt: admin18.firestore.FieldValue.serverTimestamp()
14915
+ createdAt: admin19.firestore.FieldValue.serverTimestamp(),
14916
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp(),
14917
+ lastLoginAt: admin19.firestore.FieldValue.serverTimestamp()
14501
14918
  };
14502
14919
  try {
14503
14920
  const userRef = this.db.collection(USERS_COLLECTION).doc(authUserData.uid);
@@ -14573,8 +14990,8 @@ var UserProfileAdminService = class {
14573
14990
  clinics: mergedProfileData.clinics || [],
14574
14991
  doctorIds: mergedProfileData.doctorIds || [],
14575
14992
  clinicIds: mergedProfileData.clinicIds || [],
14576
- createdAt: admin18.firestore.FieldValue.serverTimestamp(),
14577
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
14993
+ createdAt: admin19.firestore.FieldValue.serverTimestamp(),
14994
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14578
14995
  };
14579
14996
  await patientProfileRef.set(patientProfileData);
14580
14997
  patientProfile = {
@@ -14613,8 +15030,8 @@ var UserProfileAdminService = class {
14613
15030
  };
14614
15031
  const sensitiveInfoData = {
14615
15032
  ...mergedSensitiveData,
14616
- createdAt: admin18.firestore.FieldValue.serverTimestamp(),
14617
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
15033
+ createdAt: admin19.firestore.FieldValue.serverTimestamp(),
15034
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14618
15035
  // Leave dateOfBirth as is
14619
15036
  };
14620
15037
  await sensitiveInfoRef.set(sensitiveInfoData);
@@ -14640,7 +15057,7 @@ var UserProfileAdminService = class {
14640
15057
  contraindications: [],
14641
15058
  allergies: [],
14642
15059
  currentMedications: [],
14643
- lastUpdated: admin18.firestore.FieldValue.serverTimestamp(),
15060
+ lastUpdated: admin19.firestore.FieldValue.serverTimestamp(),
14644
15061
  updatedBy: userId
14645
15062
  };
14646
15063
  await medicalInfoRef.set(medicalInfoData);
@@ -14657,14 +15074,14 @@ var UserProfileAdminService = class {
14657
15074
  const batch = this.db.batch();
14658
15075
  if (!userData.roles.includes("patient" /* PATIENT */)) {
14659
15076
  batch.update(userRef, {
14660
- roles: admin18.firestore.FieldValue.arrayUnion("patient" /* PATIENT */),
14661
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
15077
+ roles: admin19.firestore.FieldValue.arrayUnion("patient" /* PATIENT */),
15078
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14662
15079
  });
14663
15080
  }
14664
15081
  if (!userData.patientProfile) {
14665
15082
  batch.update(userRef, {
14666
15083
  patientProfile: patientProfileId,
14667
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
15084
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14668
15085
  });
14669
15086
  }
14670
15087
  await batch.commit();
@@ -14705,8 +15122,8 @@ var UserProfileAdminService = class {
14705
15122
  const userData = userDoc.data();
14706
15123
  if (!userData.roles.includes("clinic_admin" /* CLINIC_ADMIN */)) {
14707
15124
  await userRef.update({
14708
- roles: admin18.firestore.FieldValue.arrayUnion("clinic_admin" /* CLINIC_ADMIN */),
14709
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
15125
+ roles: admin19.firestore.FieldValue.arrayUnion("clinic_admin" /* CLINIC_ADMIN */),
15126
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14710
15127
  });
14711
15128
  }
14712
15129
  const updatedUserDoc = await userRef.get();
@@ -14737,8 +15154,8 @@ var UserProfileAdminService = class {
14737
15154
  const userData = userDoc.data();
14738
15155
  if (!userData.roles.includes("practitioner" /* PRACTITIONER */)) {
14739
15156
  await userRef.update({
14740
- roles: admin18.firestore.FieldValue.arrayUnion("practitioner" /* PRACTITIONER */),
14741
- updatedAt: admin18.firestore.FieldValue.serverTimestamp()
15157
+ roles: admin19.firestore.FieldValue.arrayUnion("practitioner" /* PRACTITIONER */),
15158
+ updatedAt: admin19.firestore.FieldValue.serverTimestamp()
14742
15159
  });
14743
15160
  }
14744
15161
  const updatedUserDoc = await userRef.get();
@@ -14801,6 +15218,7 @@ export {
14801
15218
  PractitionerTokenStatus,
14802
15219
  ProcedureAggregationService,
14803
15220
  REVENUE_ANALYTICS_SUBCOLLECTION,
15221
+ ResourceCalendarAdminService,
14804
15222
  ReviewsAggregationService,
14805
15223
  SubscriptionStatus,
14806
15224
  TIME_EFFICIENCY_ANALYTICS_SUBCOLLECTION,