@blackcode_sa/metaestetics-api 1.7.3 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +41 -8
- package/dist/admin/index.d.ts +41 -8
- package/dist/admin/index.js +621 -109
- package/dist/admin/index.mjs +621 -109
- package/dist/index.d.mts +18 -17
- package/dist/index.d.ts +18 -17
- package/dist/index.js +1024 -1014
- package/dist/index.mjs +296 -286
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +553 -112
- package/src/admin/calendar/calendar.admin.service.ts +230 -65
- package/src/services/calendar/calendar-refactored.service.ts +2 -0
- package/src/services/media/media.service.ts +330 -0
- package/src/types/calendar/index.ts +1 -0
- package/src/types/index.ts +7 -7
- package/src/validations/schemas.ts +17 -6
package/dist/admin/index.mjs
CHANGED
|
@@ -2545,23 +2545,81 @@ var CalendarAdminService = class {
|
|
|
2545
2545
|
);
|
|
2546
2546
|
const batch = this.db.batch();
|
|
2547
2547
|
const serverTimestamp = admin8.firestore.FieldValue.serverTimestamp();
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2548
|
+
let updatesAdded = 0;
|
|
2549
|
+
const calendarEventId = appointment.calendarEventId;
|
|
2550
|
+
if (!calendarEventId) {
|
|
2551
|
+
Logger.warn(
|
|
2552
|
+
`[CalendarAdminService] Missing calendar event ID for appointment ${appointment.id}`
|
|
2553
|
+
);
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
if (appointment.practitionerId) {
|
|
2557
|
+
const practitionerEventRef = this.db.doc(
|
|
2558
|
+
`${PRACTITIONERS_COLLECTION}/${appointment.practitionerId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2559
|
+
);
|
|
2551
2560
|
batch.update(practitionerEventRef, {
|
|
2552
2561
|
status: newStatus,
|
|
2553
2562
|
updatedAt: serverTimestamp
|
|
2554
2563
|
});
|
|
2564
|
+
updatesAdded++;
|
|
2565
|
+
Logger.debug(
|
|
2566
|
+
`[CalendarAdminService] Added practitioner calendar event status update to batch`
|
|
2567
|
+
);
|
|
2568
|
+
} else {
|
|
2569
|
+
Logger.warn(
|
|
2570
|
+
`[CalendarAdminService] Missing practitioner ID for appointment ${appointment.id}`
|
|
2571
|
+
);
|
|
2555
2572
|
}
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
`[CalendarAdminService] Successfully updated calendar event statuses for appointment ${appointment.id}.`
|
|
2573
|
+
if (appointment.patientId) {
|
|
2574
|
+
const patientEventRef = this.db.doc(
|
|
2575
|
+
`${PATIENTS_COLLECTION}/${appointment.patientId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2560
2576
|
);
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2577
|
+
batch.update(patientEventRef, {
|
|
2578
|
+
status: newStatus,
|
|
2579
|
+
updatedAt: serverTimestamp
|
|
2580
|
+
});
|
|
2581
|
+
updatesAdded++;
|
|
2582
|
+
Logger.debug(
|
|
2583
|
+
`[CalendarAdminService] Added patient calendar event status update to batch`
|
|
2584
|
+
);
|
|
2585
|
+
} else {
|
|
2586
|
+
Logger.warn(
|
|
2587
|
+
`[CalendarAdminService] Missing patient ID for appointment ${appointment.id}`
|
|
2588
|
+
);
|
|
2589
|
+
}
|
|
2590
|
+
if (appointment.clinicBranchId) {
|
|
2591
|
+
const clinicEventRef = this.db.doc(
|
|
2592
|
+
`${CLINICS_COLLECTION}/${appointment.clinicBranchId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2593
|
+
);
|
|
2594
|
+
batch.update(clinicEventRef, {
|
|
2595
|
+
status: newStatus,
|
|
2596
|
+
updatedAt: serverTimestamp
|
|
2597
|
+
});
|
|
2598
|
+
updatesAdded++;
|
|
2599
|
+
Logger.debug(
|
|
2600
|
+
`[CalendarAdminService] Added clinic calendar event status update to batch`
|
|
2601
|
+
);
|
|
2602
|
+
} else {
|
|
2603
|
+
Logger.warn(
|
|
2604
|
+
`[CalendarAdminService] Missing clinic ID for appointment ${appointment.id}`
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
if (updatesAdded > 0) {
|
|
2608
|
+
try {
|
|
2609
|
+
await batch.commit();
|
|
2610
|
+
Logger.info(
|
|
2611
|
+
`[CalendarAdminService] Successfully updated ${updatesAdded} calendar event statuses for appointment ${appointment.id}.`
|
|
2612
|
+
);
|
|
2613
|
+
} catch (error) {
|
|
2614
|
+
Logger.error(
|
|
2615
|
+
`[CalendarAdminService] Error updating calendar event statuses for appointment ${appointment.id}:`,
|
|
2616
|
+
error
|
|
2617
|
+
);
|
|
2618
|
+
throw error;
|
|
2619
|
+
}
|
|
2620
|
+
} else {
|
|
2621
|
+
Logger.warn(
|
|
2622
|
+
`[CalendarAdminService] No calendar event status updates were made for appointment ${appointment.id}`
|
|
2565
2623
|
);
|
|
2566
2624
|
}
|
|
2567
2625
|
}
|
|
@@ -2570,7 +2628,7 @@ var CalendarAdminService = class {
|
|
|
2570
2628
|
* associated with a given appointment.
|
|
2571
2629
|
*
|
|
2572
2630
|
* @param appointment - The appointment object.
|
|
2573
|
-
* @param newEventTime - The new CalendarEventTime object (using
|
|
2631
|
+
* @param newEventTime - The new CalendarEventTime object (using admin.firestore.Timestamp).
|
|
2574
2632
|
* @returns {Promise<void>} A promise that resolves when all updates are attempted.
|
|
2575
2633
|
*/
|
|
2576
2634
|
async updateAppointmentCalendarEventsTime(appointment, newEventTime) {
|
|
@@ -2579,28 +2637,85 @@ var CalendarAdminService = class {
|
|
|
2579
2637
|
);
|
|
2580
2638
|
const batch = this.db.batch();
|
|
2581
2639
|
const serverTimestamp = admin8.firestore.FieldValue.serverTimestamp();
|
|
2640
|
+
let updatesAdded = 0;
|
|
2641
|
+
const calendarEventId = appointment.calendarEventId;
|
|
2642
|
+
if (!calendarEventId) {
|
|
2643
|
+
Logger.warn(
|
|
2644
|
+
`[CalendarAdminService] Missing calendar event ID for appointment ${appointment.id}`
|
|
2645
|
+
);
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2582
2648
|
const firestoreCompatibleEventTime = {
|
|
2583
|
-
start:
|
|
2584
|
-
end:
|
|
2649
|
+
start: newEventTime.start,
|
|
2650
|
+
end: newEventTime.end
|
|
2585
2651
|
};
|
|
2586
|
-
if (appointment.
|
|
2652
|
+
if (appointment.practitionerId) {
|
|
2587
2653
|
const practitionerEventRef = this.db.doc(
|
|
2588
|
-
`${PRACTITIONERS_COLLECTION}/${appointment.practitionerId}/${CALENDAR_COLLECTION}/${
|
|
2654
|
+
`${PRACTITIONERS_COLLECTION}/${appointment.practitionerId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2589
2655
|
);
|
|
2590
2656
|
batch.update(practitionerEventRef, {
|
|
2591
2657
|
eventTime: firestoreCompatibleEventTime,
|
|
2592
2658
|
updatedAt: serverTimestamp
|
|
2593
2659
|
});
|
|
2660
|
+
updatesAdded++;
|
|
2661
|
+
Logger.debug(
|
|
2662
|
+
`[CalendarAdminService] Added practitioner calendar event time update to batch`
|
|
2663
|
+
);
|
|
2664
|
+
} else {
|
|
2665
|
+
Logger.warn(
|
|
2666
|
+
`[CalendarAdminService] Missing practitioner ID for appointment ${appointment.id}`
|
|
2667
|
+
);
|
|
2594
2668
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
`[CalendarAdminService] Successfully updated calendar event times for appointment ${appointment.id}.`
|
|
2669
|
+
if (appointment.patientId) {
|
|
2670
|
+
const patientEventRef = this.db.doc(
|
|
2671
|
+
`${PATIENTS_COLLECTION}/${appointment.patientId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2599
2672
|
);
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2673
|
+
batch.update(patientEventRef, {
|
|
2674
|
+
eventTime: firestoreCompatibleEventTime,
|
|
2675
|
+
updatedAt: serverTimestamp
|
|
2676
|
+
});
|
|
2677
|
+
updatesAdded++;
|
|
2678
|
+
Logger.debug(
|
|
2679
|
+
`[CalendarAdminService] Added patient calendar event time update to batch`
|
|
2680
|
+
);
|
|
2681
|
+
} else {
|
|
2682
|
+
Logger.warn(
|
|
2683
|
+
`[CalendarAdminService] Missing patient ID for appointment ${appointment.id}`
|
|
2684
|
+
);
|
|
2685
|
+
}
|
|
2686
|
+
if (appointment.clinicBranchId) {
|
|
2687
|
+
const clinicEventRef = this.db.doc(
|
|
2688
|
+
`${CLINICS_COLLECTION}/${appointment.clinicBranchId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2689
|
+
);
|
|
2690
|
+
batch.update(clinicEventRef, {
|
|
2691
|
+
eventTime: firestoreCompatibleEventTime,
|
|
2692
|
+
updatedAt: serverTimestamp
|
|
2693
|
+
});
|
|
2694
|
+
updatesAdded++;
|
|
2695
|
+
Logger.debug(
|
|
2696
|
+
`[CalendarAdminService] Added clinic calendar event time update to batch`
|
|
2697
|
+
);
|
|
2698
|
+
} else {
|
|
2699
|
+
Logger.warn(
|
|
2700
|
+
`[CalendarAdminService] Missing clinic ID for appointment ${appointment.id}`
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2703
|
+
if (updatesAdded > 0) {
|
|
2704
|
+
try {
|
|
2705
|
+
await batch.commit();
|
|
2706
|
+
Logger.info(
|
|
2707
|
+
`[CalendarAdminService] Successfully updated ${updatesAdded} calendar event times for appointment ${appointment.id}.`
|
|
2708
|
+
);
|
|
2709
|
+
} catch (error) {
|
|
2710
|
+
Logger.error(
|
|
2711
|
+
`[CalendarAdminService] Error updating calendar event times for appointment ${appointment.id}:`,
|
|
2712
|
+
error
|
|
2713
|
+
);
|
|
2714
|
+
throw error;
|
|
2715
|
+
}
|
|
2716
|
+
} else {
|
|
2717
|
+
Logger.warn(
|
|
2718
|
+
`[CalendarAdminService] No calendar event time updates were made for appointment ${appointment.id}`
|
|
2604
2719
|
);
|
|
2605
2720
|
}
|
|
2606
2721
|
}
|
|
@@ -2616,21 +2731,72 @@ var CalendarAdminService = class {
|
|
|
2616
2731
|
`[CalendarAdminService] Deleting calendar events for appointment ${appointment.id}`
|
|
2617
2732
|
);
|
|
2618
2733
|
const batch = this.db.batch();
|
|
2619
|
-
|
|
2734
|
+
let deletesAdded = 0;
|
|
2735
|
+
const calendarEventId = appointment.calendarEventId;
|
|
2736
|
+
if (!calendarEventId) {
|
|
2737
|
+
Logger.warn(
|
|
2738
|
+
`[CalendarAdminService] Missing calendar event ID for appointment ${appointment.id}`
|
|
2739
|
+
);
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
if (appointment.practitionerId) {
|
|
2620
2743
|
const practitionerEventRef = this.db.doc(
|
|
2621
|
-
`${PRACTITIONERS_COLLECTION}/${appointment.practitionerId}/${CALENDAR_COLLECTION}/${
|
|
2744
|
+
`${PRACTITIONERS_COLLECTION}/${appointment.practitionerId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2622
2745
|
);
|
|
2623
2746
|
batch.delete(practitionerEventRef);
|
|
2747
|
+
deletesAdded++;
|
|
2748
|
+
Logger.debug(
|
|
2749
|
+
`[CalendarAdminService] Added practitioner calendar event deletion to batch`
|
|
2750
|
+
);
|
|
2751
|
+
} else {
|
|
2752
|
+
Logger.warn(
|
|
2753
|
+
`[CalendarAdminService] Missing practitioner ID for appointment ${appointment.id}`
|
|
2754
|
+
);
|
|
2624
2755
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
`[CalendarAdminService] Successfully deleted calendar events for appointment ${appointment.id}.`
|
|
2756
|
+
if (appointment.patientId) {
|
|
2757
|
+
const patientEventRef = this.db.doc(
|
|
2758
|
+
`${PATIENTS_COLLECTION}/${appointment.patientId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2629
2759
|
);
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2760
|
+
batch.delete(patientEventRef);
|
|
2761
|
+
deletesAdded++;
|
|
2762
|
+
Logger.debug(
|
|
2763
|
+
`[CalendarAdminService] Added patient calendar event deletion to batch`
|
|
2764
|
+
);
|
|
2765
|
+
} else {
|
|
2766
|
+
Logger.warn(
|
|
2767
|
+
`[CalendarAdminService] Missing patient ID for appointment ${appointment.id}`
|
|
2768
|
+
);
|
|
2769
|
+
}
|
|
2770
|
+
if (appointment.clinicBranchId) {
|
|
2771
|
+
const clinicEventRef = this.db.doc(
|
|
2772
|
+
`${CLINICS_COLLECTION}/${appointment.clinicBranchId}/${CALENDAR_COLLECTION}/${calendarEventId}`
|
|
2773
|
+
);
|
|
2774
|
+
batch.delete(clinicEventRef);
|
|
2775
|
+
deletesAdded++;
|
|
2776
|
+
Logger.debug(
|
|
2777
|
+
`[CalendarAdminService] Added clinic calendar event deletion to batch`
|
|
2778
|
+
);
|
|
2779
|
+
} else {
|
|
2780
|
+
Logger.warn(
|
|
2781
|
+
`[CalendarAdminService] Missing clinic ID for appointment ${appointment.id}`
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
if (deletesAdded > 0) {
|
|
2785
|
+
try {
|
|
2786
|
+
await batch.commit();
|
|
2787
|
+
Logger.info(
|
|
2788
|
+
`[CalendarAdminService] Successfully deleted ${deletesAdded} calendar events for appointment ${appointment.id}.`
|
|
2789
|
+
);
|
|
2790
|
+
} catch (error) {
|
|
2791
|
+
Logger.error(
|
|
2792
|
+
`[CalendarAdminService] Error deleting calendar events for appointment ${appointment.id}:`,
|
|
2793
|
+
error
|
|
2794
|
+
);
|
|
2795
|
+
throw error;
|
|
2796
|
+
}
|
|
2797
|
+
} else {
|
|
2798
|
+
Logger.warn(
|
|
2799
|
+
`[CalendarAdminService] No calendar event deletions were made for appointment ${appointment.id}`
|
|
2634
2800
|
);
|
|
2635
2801
|
}
|
|
2636
2802
|
}
|
|
@@ -2967,7 +3133,6 @@ var AppointmentAggregationService = class {
|
|
|
2967
3133
|
`[AggService] Handling CREATE for appointment: ${appointment.id}, patient: ${appointment.patientId}, status: ${appointment.status}`
|
|
2968
3134
|
);
|
|
2969
3135
|
try {
|
|
2970
|
-
await this.managePatientClinicPractitionerLinks(appointment, "create");
|
|
2971
3136
|
const [
|
|
2972
3137
|
patientProfile,
|
|
2973
3138
|
patientSensitiveInfo,
|
|
@@ -2981,6 +3146,14 @@ var AppointmentAggregationService = class {
|
|
|
2981
3146
|
this.fetchClinicInfo(appointment.clinicBranchId)
|
|
2982
3147
|
// Needed for clinic admin notifications
|
|
2983
3148
|
]);
|
|
3149
|
+
if (patientProfile) {
|
|
3150
|
+
await this.managePatientClinicPractitionerLinks(
|
|
3151
|
+
patientProfile,
|
|
3152
|
+
appointment.practitionerId,
|
|
3153
|
+
appointment.clinicBranchId,
|
|
3154
|
+
"create"
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
2984
3157
|
if (appointment.status === "confirmed" /* CONFIRMED */) {
|
|
2985
3158
|
Logger.info(
|
|
2986
3159
|
`[AggService] Appt ${appointment.id} created as CONFIRMED.`
|
|
@@ -3070,7 +3243,7 @@ var AppointmentAggregationService = class {
|
|
|
3070
3243
|
* @returns {Promise<void>}
|
|
3071
3244
|
*/
|
|
3072
3245
|
async handleAppointmentUpdate(before, after) {
|
|
3073
|
-
var _a, _b;
|
|
3246
|
+
var _a, _b, _c;
|
|
3074
3247
|
Logger.info(
|
|
3075
3248
|
`[AggService] Handling UPDATE for appointment: ${after.id}. Status ${before.status} -> ${after.status}`
|
|
3076
3249
|
);
|
|
@@ -3095,6 +3268,10 @@ var AppointmentAggregationService = class {
|
|
|
3095
3268
|
if (before.status === "pending" /* PENDING */ && after.status === "confirmed" /* CONFIRMED */) {
|
|
3096
3269
|
Logger.info(`[AggService] Appt ${after.id} PENDING -> CONFIRMED.`);
|
|
3097
3270
|
await this.createPreAppointmentRequirementInstances(after);
|
|
3271
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
|
|
3272
|
+
after,
|
|
3273
|
+
"confirmed" /* CONFIRMED */
|
|
3274
|
+
);
|
|
3098
3275
|
if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
|
|
3099
3276
|
Logger.info(
|
|
3100
3277
|
`[AggService] Sending appointment confirmed email to patient ${patientSensitiveInfo.email}`
|
|
@@ -3134,7 +3311,53 @@ var AppointmentAggregationService = class {
|
|
|
3134
3311
|
};
|
|
3135
3312
|
await this.appointmentMailingService.sendAppointmentConfirmedEmail(
|
|
3136
3313
|
practitionerEmailData
|
|
3137
|
-
|
|
3314
|
+
);
|
|
3315
|
+
}
|
|
3316
|
+
} else if (before.status === "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */ && after.status === "confirmed" /* CONFIRMED */) {
|
|
3317
|
+
Logger.info(
|
|
3318
|
+
`[AggService] Appt ${after.id} RESCHEDULED_BY_CLINIC -> CONFIRMED.`
|
|
3319
|
+
);
|
|
3320
|
+
await this.updateRelatedPatientRequirementInstances(
|
|
3321
|
+
before,
|
|
3322
|
+
"supersededReschedule" /* SUPERSEDED_RESCHEDULE */
|
|
3323
|
+
);
|
|
3324
|
+
await this.createPreAppointmentRequirementInstances(after);
|
|
3325
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
|
|
3326
|
+
after,
|
|
3327
|
+
"confirmed" /* CONFIRMED */
|
|
3328
|
+
);
|
|
3329
|
+
if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
|
|
3330
|
+
Logger.info(
|
|
3331
|
+
`[AggService] Sending appointment confirmed email to patient ${patientSensitiveInfo.email}`
|
|
3332
|
+
);
|
|
3333
|
+
const emailData = {
|
|
3334
|
+
appointment: after,
|
|
3335
|
+
recipientProfile: after.patientInfo,
|
|
3336
|
+
recipientRole: "patient"
|
|
3337
|
+
};
|
|
3338
|
+
await this.appointmentMailingService.sendAppointmentConfirmedEmail(
|
|
3339
|
+
emailData
|
|
3340
|
+
);
|
|
3341
|
+
}
|
|
3342
|
+
if ((patientProfile == null ? void 0 : patientProfile.expoTokens) && patientProfile.expoTokens.length > 0) {
|
|
3343
|
+
await this.notificationsAdmin.sendAppointmentConfirmedPush(
|
|
3344
|
+
after,
|
|
3345
|
+
after.patientId,
|
|
3346
|
+
patientProfile.expoTokens,
|
|
3347
|
+
"patient" /* PATIENT */
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
if ((_b = practitionerProfile == null ? void 0 : practitionerProfile.basicInfo) == null ? void 0 : _b.email) {
|
|
3351
|
+
Logger.info(
|
|
3352
|
+
`[AggService] Sending appointment confirmation email to practitioner ${practitionerProfile.basicInfo.email}`
|
|
3353
|
+
);
|
|
3354
|
+
const practitionerEmailData = {
|
|
3355
|
+
appointment: after,
|
|
3356
|
+
recipientProfile: after.practitionerInfo,
|
|
3357
|
+
recipientRole: "practitioner"
|
|
3358
|
+
};
|
|
3359
|
+
await this.appointmentMailingService.sendAppointmentConfirmedEmail(
|
|
3360
|
+
practitionerEmailData
|
|
3138
3361
|
);
|
|
3139
3362
|
}
|
|
3140
3363
|
} else if (after.status === "canceled_clinic" /* CANCELED_CLINIC */ || after.status === "canceled_patient" /* CANCELED_PATIENT */ || after.status === "canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */ || after.status === "no_show" /* NO_SHOW */) {
|
|
@@ -3145,7 +3368,33 @@ var AppointmentAggregationService = class {
|
|
|
3145
3368
|
after,
|
|
3146
3369
|
"cancelledAppointment" /* CANCELLED_APPOINTMENT */
|
|
3147
3370
|
);
|
|
3148
|
-
|
|
3371
|
+
if (patientProfile) {
|
|
3372
|
+
await this.managePatientClinicPractitionerLinks(
|
|
3373
|
+
patientProfile,
|
|
3374
|
+
after.practitionerId,
|
|
3375
|
+
after.clinicBranchId,
|
|
3376
|
+
"cancel",
|
|
3377
|
+
after.status
|
|
3378
|
+
);
|
|
3379
|
+
}
|
|
3380
|
+
const calendarStatus = (status) => {
|
|
3381
|
+
switch (status) {
|
|
3382
|
+
case "no_show" /* NO_SHOW */:
|
|
3383
|
+
return "no_show" /* NO_SHOW */;
|
|
3384
|
+
case "canceled_clinic" /* CANCELED_CLINIC */:
|
|
3385
|
+
return "rejected" /* REJECTED */;
|
|
3386
|
+
case "canceled_patient" /* CANCELED_PATIENT */:
|
|
3387
|
+
return "canceled" /* CANCELED */;
|
|
3388
|
+
case "canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */:
|
|
3389
|
+
return "rejected" /* REJECTED */;
|
|
3390
|
+
default:
|
|
3391
|
+
return "canceled" /* CANCELED */;
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
|
|
3395
|
+
after,
|
|
3396
|
+
calendarStatus(after.status)
|
|
3397
|
+
);
|
|
3149
3398
|
if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
|
|
3150
3399
|
Logger.info(
|
|
3151
3400
|
`[AggService] Sending appointment cancellation email to patient ${patientSensitiveInfo.email}`
|
|
@@ -3161,7 +3410,7 @@ var AppointmentAggregationService = class {
|
|
|
3161
3410
|
// TODO: Properly import types
|
|
3162
3411
|
);
|
|
3163
3412
|
}
|
|
3164
|
-
if ((
|
|
3413
|
+
if ((_c = practitionerProfile == null ? void 0 : practitionerProfile.basicInfo) == null ? void 0 : _c.email) {
|
|
3165
3414
|
Logger.info(
|
|
3166
3415
|
`[AggService] Sending appointment cancellation email to practitioner ${practitionerProfile.basicInfo.email}`
|
|
3167
3416
|
);
|
|
@@ -3179,6 +3428,10 @@ var AppointmentAggregationService = class {
|
|
|
3179
3428
|
} else if (after.status === "completed" /* COMPLETED */) {
|
|
3180
3429
|
Logger.info(`[AggService] Appt ${after.id} status -> COMPLETED.`);
|
|
3181
3430
|
await this.createPostAppointmentRequirementInstances(after);
|
|
3431
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
|
|
3432
|
+
after,
|
|
3433
|
+
"completed" /* COMPLETED */
|
|
3434
|
+
);
|
|
3182
3435
|
if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
|
|
3183
3436
|
Logger.info(
|
|
3184
3437
|
`[AggService] Sending review request email to patient ${patientSensitiveInfo.email}`
|
|
@@ -3203,8 +3456,16 @@ var AppointmentAggregationService = class {
|
|
|
3203
3456
|
// Pass the 'before' state for old requirements
|
|
3204
3457
|
"supersededReschedule" /* SUPERSEDED_RESCHEDULE */
|
|
3205
3458
|
);
|
|
3206
|
-
|
|
3207
|
-
|
|
3459
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsTime(
|
|
3460
|
+
after,
|
|
3461
|
+
{
|
|
3462
|
+
start: after.appointmentStartTime,
|
|
3463
|
+
end: after.appointmentEndTime
|
|
3464
|
+
}
|
|
3465
|
+
);
|
|
3466
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
|
|
3467
|
+
after,
|
|
3468
|
+
"pending" /* PENDING */
|
|
3208
3469
|
);
|
|
3209
3470
|
if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
|
|
3210
3471
|
Logger.info(
|
|
@@ -3229,9 +3490,24 @@ var AppointmentAggregationService = class {
|
|
|
3229
3490
|
}
|
|
3230
3491
|
if (timeChanged && !statusChanged) {
|
|
3231
3492
|
Logger.info(`[AggService] Appointment ${after.id} time changed.`);
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3493
|
+
if (after.status === "confirmed" /* CONFIRMED */) {
|
|
3494
|
+
await this.updateRelatedPatientRequirementInstances(
|
|
3495
|
+
before,
|
|
3496
|
+
"supersededReschedule" /* SUPERSEDED_RESCHEDULE */
|
|
3497
|
+
);
|
|
3498
|
+
await this.createPreAppointmentRequirementInstances(after);
|
|
3499
|
+
await this.calendarAdminService.updateAppointmentCalendarEventsTime(
|
|
3500
|
+
after,
|
|
3501
|
+
{
|
|
3502
|
+
start: after.appointmentStartTime,
|
|
3503
|
+
end: after.appointmentEndTime
|
|
3504
|
+
}
|
|
3505
|
+
);
|
|
3506
|
+
} else {
|
|
3507
|
+
Logger.warn(
|
|
3508
|
+
`[AggService] Independent time change detected for ${after.id} with status ${after.status}. Review implications for requirements and calendar.`
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3235
3511
|
}
|
|
3236
3512
|
Logger.info(
|
|
3237
3513
|
`[AggService] Successfully processed UPDATE for appointment: ${after.id}`
|
|
@@ -3256,9 +3532,19 @@ var AppointmentAggregationService = class {
|
|
|
3256
3532
|
deletedAppointment,
|
|
3257
3533
|
"cancelledAppointment" /* CANCELLED_APPOINTMENT */
|
|
3258
3534
|
);
|
|
3259
|
-
await this.
|
|
3260
|
-
deletedAppointment
|
|
3261
|
-
|
|
3535
|
+
const patientProfile = await this.fetchPatientProfile(
|
|
3536
|
+
deletedAppointment.patientId
|
|
3537
|
+
);
|
|
3538
|
+
if (patientProfile) {
|
|
3539
|
+
await this.managePatientClinicPractitionerLinks(
|
|
3540
|
+
patientProfile,
|
|
3541
|
+
deletedAppointment.practitionerId,
|
|
3542
|
+
deletedAppointment.clinicBranchId,
|
|
3543
|
+
"cancel"
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
3546
|
+
await this.calendarAdminService.deleteAppointmentCalendarEvents(
|
|
3547
|
+
deletedAppointment
|
|
3262
3548
|
);
|
|
3263
3549
|
}
|
|
3264
3550
|
// --- Helper Methods for Aggregation Logic ---
|
|
@@ -3539,18 +3825,47 @@ var AppointmentAggregationService = class {
|
|
|
3539
3825
|
try {
|
|
3540
3826
|
const batch = this.db.batch();
|
|
3541
3827
|
let instancesCreatedCount = 0;
|
|
3828
|
+
let createdInstances = [];
|
|
3829
|
+
Logger.info(
|
|
3830
|
+
`[AggService] Found ${appointment.postProcedureRequirements.length} post-requirements to process: ${JSON.stringify(
|
|
3831
|
+
appointment.postProcedureRequirements.map((r) => {
|
|
3832
|
+
var _a2, _b;
|
|
3833
|
+
return {
|
|
3834
|
+
id: r.id,
|
|
3835
|
+
name: r.name,
|
|
3836
|
+
type: r.type,
|
|
3837
|
+
isActive: r.isActive,
|
|
3838
|
+
hasTimeframe: !!r.timeframe,
|
|
3839
|
+
notifyAtLength: ((_b = (_a2 = r.timeframe) == null ? void 0 : _a2.notifyAt) == null ? void 0 : _b.length) || 0
|
|
3840
|
+
};
|
|
3841
|
+
})
|
|
3842
|
+
)}`
|
|
3843
|
+
);
|
|
3542
3844
|
for (const template of appointment.postProcedureRequirements) {
|
|
3543
|
-
if (!template)
|
|
3845
|
+
if (!template) {
|
|
3846
|
+
Logger.warn(
|
|
3847
|
+
`[AggService] Found null/undefined template in postProcedureRequirements array`
|
|
3848
|
+
);
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3544
3851
|
if (template.type !== "post" /* POST */ || !template.isActive) {
|
|
3545
3852
|
Logger.debug(
|
|
3546
3853
|
`[AggService] Skipping template ${template.id} (${template.name}): not an active POST requirement.`
|
|
3547
3854
|
);
|
|
3548
3855
|
continue;
|
|
3549
3856
|
}
|
|
3857
|
+
if (!template.timeframe || !template.timeframe.notifyAt || template.timeframe.notifyAt.length === 0) {
|
|
3858
|
+
Logger.warn(
|
|
3859
|
+
`[AggService] Template ${template.id} (${template.name}) has no timeframe.notifyAt values. Creating with empty instructions.`
|
|
3860
|
+
);
|
|
3861
|
+
}
|
|
3550
3862
|
Logger.debug(
|
|
3551
|
-
`[AggService] Processing
|
|
3863
|
+
`[AggService] Processing template ${template.id} (${template.name}) for appt ${appointment.id}`
|
|
3552
3864
|
);
|
|
3553
3865
|
const newInstanceRef = this.db.collection(PATIENTS_COLLECTION).doc(appointment.patientId).collection(PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME).doc();
|
|
3866
|
+
Logger.debug(
|
|
3867
|
+
`[AggService] Created doc reference: ${newInstanceRef.path}`
|
|
3868
|
+
);
|
|
3554
3869
|
const instructions = (((_a = template.timeframe) == null ? void 0 : _a.notifyAt) || []).map((notifyAtValue) => {
|
|
3555
3870
|
let dueTime = appointment.appointmentEndTime;
|
|
3556
3871
|
if (template.timeframe && typeof notifyAtValue === "number") {
|
|
@@ -3565,7 +3880,7 @@ var AppointmentAggregationService = class {
|
|
|
3565
3880
|
dueTime = admin10.firestore.Timestamp.fromDate(dueDateTime);
|
|
3566
3881
|
}
|
|
3567
3882
|
const actionableWindowHours = template.importance === "high" ? 1 : template.importance === "medium" ? 4 : 15;
|
|
3568
|
-
|
|
3883
|
+
const instructionObject = {
|
|
3569
3884
|
instructionId: `${template.id}_${notifyAtValue}_${newInstanceRef.id}`.replace(
|
|
3570
3885
|
/[^a-zA-Z0-9_]/g,
|
|
3571
3886
|
"_"
|
|
@@ -3577,36 +3892,142 @@ var AppointmentAggregationService = class {
|
|
|
3577
3892
|
originalNotifyAtValue: notifyAtValue,
|
|
3578
3893
|
originalTimeframeUnit: template.timeframe.unit,
|
|
3579
3894
|
updatedAt: admin10.firestore.Timestamp.now(),
|
|
3580
|
-
// Use current server timestamp
|
|
3581
3895
|
notificationId: void 0,
|
|
3582
3896
|
actionTakenAt: void 0
|
|
3583
3897
|
};
|
|
3898
|
+
return instructionObject;
|
|
3584
3899
|
});
|
|
3585
3900
|
const newInstanceData = {
|
|
3901
|
+
id: newInstanceRef.id,
|
|
3586
3902
|
patientId: appointment.patientId,
|
|
3587
3903
|
appointmentId: appointment.id,
|
|
3588
3904
|
originalRequirementId: template.id,
|
|
3589
3905
|
requirementName: template.name,
|
|
3590
3906
|
requirementDescription: template.description,
|
|
3591
3907
|
requirementType: template.type,
|
|
3592
|
-
// Should be RequirementType.POST
|
|
3593
3908
|
requirementImportance: template.importance,
|
|
3594
3909
|
overallStatus: "active" /* ACTIVE */,
|
|
3595
3910
|
instructions,
|
|
3596
3911
|
createdAt: admin10.firestore.FieldValue.serverTimestamp(),
|
|
3597
3912
|
updatedAt: admin10.firestore.FieldValue.serverTimestamp()
|
|
3598
3913
|
};
|
|
3914
|
+
Logger.debug(
|
|
3915
|
+
`[AggService] Setting data for requirement: ${JSON.stringify({
|
|
3916
|
+
id: newInstanceRef.id,
|
|
3917
|
+
patientId: newInstanceData.patientId,
|
|
3918
|
+
appointmentId: newInstanceData.appointmentId,
|
|
3919
|
+
requirementName: newInstanceData.requirementName,
|
|
3920
|
+
instructionsCount: newInstanceData.instructions.length
|
|
3921
|
+
})}`
|
|
3922
|
+
);
|
|
3599
3923
|
batch.set(newInstanceRef, newInstanceData);
|
|
3924
|
+
createdInstances.push({
|
|
3925
|
+
ref: newInstanceRef,
|
|
3926
|
+
data: newInstanceData
|
|
3927
|
+
});
|
|
3600
3928
|
instancesCreatedCount++;
|
|
3601
3929
|
Logger.debug(
|
|
3602
|
-
`[AggService] Added
|
|
3930
|
+
`[AggService] Added PatientRequirementInstance ${newInstanceRef.id} to batch for template ${template.id}.`
|
|
3603
3931
|
);
|
|
3604
3932
|
}
|
|
3605
3933
|
if (instancesCreatedCount > 0) {
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3934
|
+
try {
|
|
3935
|
+
await batch.commit();
|
|
3936
|
+
Logger.info(
|
|
3937
|
+
`[AggService] Successfully created ${instancesCreatedCount} POST_APPOINTMENT requirement instances for appointment ${appointment.id}.`
|
|
3938
|
+
);
|
|
3939
|
+
try {
|
|
3940
|
+
const verifySnapshot = await this.db.collection(PATIENTS_COLLECTION).doc(appointment.patientId).collection(PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME).where("appointmentId", "==", appointment.id).get();
|
|
3941
|
+
if (verifySnapshot.empty) {
|
|
3942
|
+
Logger.warn(
|
|
3943
|
+
`[AggService] Batch commit reported success but documents not found! Attempting direct creation as fallback...`
|
|
3944
|
+
);
|
|
3945
|
+
const fallbackPromises = createdInstances.map(
|
|
3946
|
+
async ({ ref, data }) => {
|
|
3947
|
+
try {
|
|
3948
|
+
await ref.set(data);
|
|
3949
|
+
Logger.info(
|
|
3950
|
+
`[AggService] Fallback direct creation success for ${ref.id}`
|
|
3951
|
+
);
|
|
3952
|
+
return true;
|
|
3953
|
+
} catch (fallbackError) {
|
|
3954
|
+
Logger.error(
|
|
3955
|
+
`[AggService] Fallback direct creation failed for ${ref.id}:`,
|
|
3956
|
+
fallbackError
|
|
3957
|
+
);
|
|
3958
|
+
return false;
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
);
|
|
3962
|
+
const fallbackResults = await Promise.allSettled(
|
|
3963
|
+
fallbackPromises
|
|
3964
|
+
);
|
|
3965
|
+
const successCount = fallbackResults.filter(
|
|
3966
|
+
(r) => r.status === "fulfilled" && r.value === true
|
|
3967
|
+
).length;
|
|
3968
|
+
if (successCount > 0) {
|
|
3969
|
+
Logger.info(
|
|
3970
|
+
`[AggService] Fallback mechanism created ${successCount} out of ${createdInstances.length} requirements`
|
|
3971
|
+
);
|
|
3972
|
+
} else {
|
|
3973
|
+
Logger.error(
|
|
3974
|
+
`[AggService] Both batch and fallback mechanisms failed to create requirements`
|
|
3975
|
+
);
|
|
3976
|
+
throw new Error(
|
|
3977
|
+
"Failed to create patient requirements through both batch and direct methods"
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3980
|
+
} else {
|
|
3981
|
+
Logger.info(
|
|
3982
|
+
`[AggService] Verification confirmed ${verifySnapshot.size} requirement documents created`
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
} catch (verifyError) {
|
|
3986
|
+
Logger.error(
|
|
3987
|
+
`[AggService] Error during verification of created requirements:`,
|
|
3988
|
+
verifyError
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
} catch (commitError) {
|
|
3992
|
+
Logger.error(
|
|
3993
|
+
`[AggService] Error committing batch for POST_APPOINTMENT requirement instances for appointment ${appointment.id}:`,
|
|
3994
|
+
commitError
|
|
3995
|
+
);
|
|
3996
|
+
Logger.info(`[AggService] Attempting direct creation as fallback...`);
|
|
3997
|
+
const fallbackPromises = createdInstances.map(
|
|
3998
|
+
async ({ ref, data }) => {
|
|
3999
|
+
try {
|
|
4000
|
+
await ref.set(data);
|
|
4001
|
+
Logger.info(
|
|
4002
|
+
`[AggService] Fallback direct creation success for ${ref.id}`
|
|
4003
|
+
);
|
|
4004
|
+
return true;
|
|
4005
|
+
} catch (fallbackError) {
|
|
4006
|
+
Logger.error(
|
|
4007
|
+
`[AggService] Fallback direct creation failed for ${ref.id}:`,
|
|
4008
|
+
fallbackError
|
|
4009
|
+
);
|
|
4010
|
+
return false;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
);
|
|
4014
|
+
const fallbackResults = await Promise.allSettled(fallbackPromises);
|
|
4015
|
+
const successCount = fallbackResults.filter(
|
|
4016
|
+
(r) => r.status === "fulfilled" && r.value === true
|
|
4017
|
+
).length;
|
|
4018
|
+
if (successCount > 0) {
|
|
4019
|
+
Logger.info(
|
|
4020
|
+
`[AggService] Fallback mechanism created ${successCount} out of ${createdInstances.length} requirements`
|
|
4021
|
+
);
|
|
4022
|
+
} else {
|
|
4023
|
+
Logger.error(
|
|
4024
|
+
`[AggService] Both batch and fallback mechanisms failed to create requirements`
|
|
4025
|
+
);
|
|
4026
|
+
throw new Error(
|
|
4027
|
+
"Failed to create patient requirements through both batch and direct methods"
|
|
4028
|
+
);
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
3610
4031
|
} else {
|
|
3611
4032
|
Logger.info(
|
|
3612
4033
|
`[AggService] No new POST_APPOINTMENT requirement instances were prepared for batch commit for appointment ${appointment.id}.`
|
|
@@ -3617,6 +4038,7 @@ var AppointmentAggregationService = class {
|
|
|
3617
4038
|
`[AggService] Error creating POST_APPOINTMENT requirement instances for appointment ${appointment.id}:`,
|
|
3618
4039
|
error
|
|
3619
4040
|
);
|
|
4041
|
+
throw error;
|
|
3620
4042
|
}
|
|
3621
4043
|
}
|
|
3622
4044
|
/**
|
|
@@ -3682,75 +4104,165 @@ var AppointmentAggregationService = class {
|
|
|
3682
4104
|
}
|
|
3683
4105
|
}
|
|
3684
4106
|
/**
|
|
3685
|
-
* Manages
|
|
3686
|
-
*
|
|
3687
|
-
* Removes patientId on appointment cancellation (basic removal, can be enhanced).
|
|
4107
|
+
* Manages relationships between a patient and clinics/practitioners.
|
|
4108
|
+
* Only updates the patient profile with doctorIds and clinicIds.
|
|
3688
4109
|
*
|
|
3689
|
-
* @param {
|
|
3690
|
-
* @param {
|
|
4110
|
+
* @param {PatientProfile} patientProfile - The patient profile to update
|
|
4111
|
+
* @param {string} practitionerId - The practitioner ID
|
|
4112
|
+
* @param {string} clinicId - The clinic ID
|
|
4113
|
+
* @param {"create" | "cancel"} action - 'create' to add IDs, 'cancel' to potentially remove them
|
|
4114
|
+
* @param {AppointmentStatus} [cancelStatus] - The appointment status if action is 'cancel'
|
|
3691
4115
|
* @returns {Promise<void>} A promise that resolves when the operation is complete.
|
|
3692
4116
|
*/
|
|
3693
|
-
async managePatientClinicPractitionerLinks(
|
|
4117
|
+
async managePatientClinicPractitionerLinks(patientProfile, practitionerId, clinicId, action, cancelStatus) {
|
|
3694
4118
|
Logger.info(
|
|
3695
|
-
`[AggService] Managing patient-clinic-practitioner links for
|
|
4119
|
+
`[AggService] Managing patient-clinic-practitioner links for patient ${patientProfile.id}, action: ${action}`
|
|
3696
4120
|
);
|
|
3697
|
-
|
|
4121
|
+
try {
|
|
4122
|
+
if (action === "create") {
|
|
4123
|
+
await this.addPatientLinks(patientProfile, practitionerId, clinicId);
|
|
4124
|
+
} else if (action === "cancel") {
|
|
4125
|
+
await this.removePatientLinksIfNoActiveAppointments(
|
|
4126
|
+
patientProfile,
|
|
4127
|
+
practitionerId,
|
|
4128
|
+
clinicId
|
|
4129
|
+
);
|
|
4130
|
+
}
|
|
4131
|
+
} catch (error) {
|
|
3698
4132
|
Logger.error(
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
patientId: appointment.patientId,
|
|
3702
|
-
practitionerId: appointment.practitionerId,
|
|
3703
|
-
clinicBranchId: appointment.clinicBranchId
|
|
3704
|
-
}
|
|
4133
|
+
`[AggService] Error managing patient-clinic-practitioner links for patient ${patientProfile.id}:`,
|
|
4134
|
+
error
|
|
3705
4135
|
);
|
|
3706
|
-
return;
|
|
3707
4136
|
}
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
);
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Adds practitioner and clinic IDs to the patient profile.
|
|
4140
|
+
*
|
|
4141
|
+
* @param {PatientProfile} patientProfile - The patient profile to update
|
|
4142
|
+
* @param {string} practitionerId - The practitioner ID to add
|
|
4143
|
+
* @param {string} clinicId - The clinic ID to add
|
|
4144
|
+
* @returns {Promise<void>} A promise that resolves when the operation is complete.
|
|
4145
|
+
*/
|
|
4146
|
+
async addPatientLinks(patientProfile, practitionerId, clinicId) {
|
|
4147
|
+
var _a, _b;
|
|
4148
|
+
try {
|
|
4149
|
+
const hasDoctor = ((_a = patientProfile.doctorIds) == null ? void 0 : _a.includes(practitionerId)) || false;
|
|
4150
|
+
const hasClinic = ((_b = patientProfile.clinicIds) == null ? void 0 : _b.includes(clinicId)) || false;
|
|
4151
|
+
if (!hasDoctor || !hasClinic) {
|
|
4152
|
+
const patientRef = this.db.collection(PATIENTS_COLLECTION).doc(patientProfile.id);
|
|
4153
|
+
const updateData = {
|
|
4154
|
+
updatedAt: admin10.firestore.FieldValue.serverTimestamp()
|
|
4155
|
+
};
|
|
4156
|
+
if (!hasDoctor) {
|
|
4157
|
+
Logger.debug(
|
|
4158
|
+
`[AggService] Adding practitioner ${practitionerId} to patient ${patientProfile.id}`
|
|
4159
|
+
);
|
|
4160
|
+
updateData.doctorIds = admin10.firestore.FieldValue.arrayUnion(practitionerId);
|
|
4161
|
+
}
|
|
4162
|
+
if (!hasClinic) {
|
|
4163
|
+
Logger.debug(
|
|
4164
|
+
`[AggService] Adding clinic ${clinicId} to patient ${patientProfile.id}`
|
|
4165
|
+
);
|
|
4166
|
+
updateData.clinicIds = admin10.firestore.FieldValue.arrayUnion(clinicId);
|
|
4167
|
+
}
|
|
4168
|
+
await patientRef.update(updateData);
|
|
4169
|
+
Logger.info(
|
|
4170
|
+
`[AggService] Successfully updated patient ${patientProfile.id} with new links.`
|
|
4171
|
+
);
|
|
4172
|
+
} else {
|
|
4173
|
+
Logger.info(
|
|
4174
|
+
`[AggService] Patient ${patientProfile.id} already has links to both practitioner ${practitionerId} and clinic ${clinicId}.`
|
|
4175
|
+
);
|
|
4176
|
+
}
|
|
4177
|
+
} catch (error) {
|
|
4178
|
+
Logger.error(
|
|
4179
|
+
`[AggService] Error updating patient ${patientProfile.id} with new links:`,
|
|
4180
|
+
error
|
|
3742
4181
|
);
|
|
4182
|
+
throw error;
|
|
3743
4183
|
}
|
|
4184
|
+
}
|
|
4185
|
+
/**
|
|
4186
|
+
* Removes practitioner and clinic IDs from the patient profile if there are no more active appointments.
|
|
4187
|
+
*
|
|
4188
|
+
* @param {PatientProfile} patientProfile - The patient profile to update
|
|
4189
|
+
* @param {string} practitionerId - The practitioner ID to remove
|
|
4190
|
+
* @param {string} clinicId - The clinic ID to remove
|
|
4191
|
+
* @returns {Promise<void>} A promise that resolves when the operation is complete.
|
|
4192
|
+
*/
|
|
4193
|
+
async removePatientLinksIfNoActiveAppointments(patientProfile, practitionerId, clinicId) {
|
|
4194
|
+
var _a, _b;
|
|
3744
4195
|
try {
|
|
3745
|
-
await
|
|
4196
|
+
const activePractitionerAppointments = await this.checkActiveAppointments(
|
|
4197
|
+
patientProfile.id,
|
|
4198
|
+
"practitionerId",
|
|
4199
|
+
practitionerId
|
|
4200
|
+
);
|
|
4201
|
+
const activeClinicAppointments = await this.checkActiveAppointments(
|
|
4202
|
+
patientProfile.id,
|
|
4203
|
+
"clinicBranchId",
|
|
4204
|
+
clinicId
|
|
4205
|
+
);
|
|
3746
4206
|
Logger.info(
|
|
3747
|
-
`[AggService]
|
|
4207
|
+
`[AggService] Active appointment count for patient ${patientProfile.id}: With practitioner ${practitionerId}: ${activePractitionerAppointments}, With clinic ${clinicId}: ${activeClinicAppointments}`
|
|
3748
4208
|
);
|
|
4209
|
+
const patientRef = this.db.collection(PATIENTS_COLLECTION).doc(patientProfile.id);
|
|
4210
|
+
const updateData = {};
|
|
4211
|
+
let updateNeeded = false;
|
|
4212
|
+
if (activePractitionerAppointments === 0 && ((_a = patientProfile.doctorIds) == null ? void 0 : _a.includes(practitionerId))) {
|
|
4213
|
+
Logger.debug(
|
|
4214
|
+
`[AggService] Removing practitioner ${practitionerId} from patient ${patientProfile.id}`
|
|
4215
|
+
);
|
|
4216
|
+
updateData.doctorIds = admin10.firestore.FieldValue.arrayRemove(practitionerId);
|
|
4217
|
+
updateNeeded = true;
|
|
4218
|
+
}
|
|
4219
|
+
if (activeClinicAppointments === 0 && ((_b = patientProfile.clinicIds) == null ? void 0 : _b.includes(clinicId))) {
|
|
4220
|
+
Logger.debug(
|
|
4221
|
+
`[AggService] Removing clinic ${clinicId} from patient ${patientProfile.id}`
|
|
4222
|
+
);
|
|
4223
|
+
updateData.clinicIds = admin10.firestore.FieldValue.arrayRemove(clinicId);
|
|
4224
|
+
updateNeeded = true;
|
|
4225
|
+
}
|
|
4226
|
+
if (updateNeeded) {
|
|
4227
|
+
updateData.updatedAt = admin10.firestore.FieldValue.serverTimestamp();
|
|
4228
|
+
await patientRef.update(updateData);
|
|
4229
|
+
Logger.info(
|
|
4230
|
+
`[AggService] Successfully removed links from patient ${patientProfile.id}`
|
|
4231
|
+
);
|
|
4232
|
+
} else {
|
|
4233
|
+
Logger.info(
|
|
4234
|
+
`[AggService] No links need to be removed from patient ${patientProfile.id}`
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
3749
4237
|
} catch (error) {
|
|
3750
4238
|
Logger.error(
|
|
3751
|
-
`[AggService] Error
|
|
4239
|
+
`[AggService] Error removing links from patient profile:`,
|
|
3752
4240
|
error
|
|
3753
4241
|
);
|
|
4242
|
+
throw error;
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
/**
|
|
4246
|
+
* Checks if there are active appointments between a patient and another entity (practitioner or clinic).
|
|
4247
|
+
*
|
|
4248
|
+
* @param {string} patientId - The patient ID.
|
|
4249
|
+
* @param {"practitionerId" | "clinicBranchId"} entityField - The field to check for the entity ID.
|
|
4250
|
+
* @param {string} entityId - The entity ID (practitioner or clinic).
|
|
4251
|
+
* @returns {Promise<number>} The number of active appointments found.
|
|
4252
|
+
*/
|
|
4253
|
+
async checkActiveAppointments(patientId, entityField, entityId) {
|
|
4254
|
+
try {
|
|
4255
|
+
const inactiveStatuses = [
|
|
4256
|
+
"canceled_clinic" /* CANCELED_CLINIC */,
|
|
4257
|
+
"canceled_patient" /* CANCELED_PATIENT */,
|
|
4258
|
+
"canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */,
|
|
4259
|
+
"no_show" /* NO_SHOW */
|
|
4260
|
+
];
|
|
4261
|
+
const snapshot = await this.db.collection("appointments").where("patientId", "==", patientId).where(entityField, "==", entityId).where("status", "not-in", inactiveStatuses).get();
|
|
4262
|
+
return snapshot.size;
|
|
4263
|
+
} catch (error) {
|
|
4264
|
+
Logger.error(`[AggService] Error checking active appointments:`, error);
|
|
4265
|
+
throw error;
|
|
3754
4266
|
}
|
|
3755
4267
|
}
|
|
3756
4268
|
// --- Data Fetching Helpers (Consider moving to a data access layer or using existing services if available) ---
|