@blackcode_sa/metaestetics-api 1.12.65 → 1.12.67
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 +2 -0
- package/dist/admin/index.d.ts +2 -0
- package/dist/admin/index.js +45 -4
- package/dist/admin/index.mjs +45 -4
- package/dist/backoffice/index.d.mts +33 -0
- package/dist/backoffice/index.d.ts +33 -0
- package/dist/backoffice/index.js +63 -0
- package/dist/backoffice/index.mjs +63 -0
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +116 -11
- package/dist/index.mjs +116 -11
- package/package.json +119 -119
- package/src/__mocks__/firstore.ts +10 -10
- package/src/admin/aggregation/README.md +79 -79
- package/src/admin/aggregation/appointment/README.md +128 -128
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1844 -1844
- package/src/admin/aggregation/appointment/index.ts +1 -1
- package/src/admin/aggregation/clinic/README.md +52 -52
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
- package/src/admin/aggregation/clinic/index.ts +1 -1
- package/src/admin/aggregation/forms/README.md +13 -13
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
- package/src/admin/aggregation/forms/index.ts +1 -1
- package/src/admin/aggregation/index.ts +8 -8
- package/src/admin/aggregation/patient/README.md +27 -27
- package/src/admin/aggregation/patient/index.ts +1 -1
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
- package/src/admin/aggregation/practitioner/README.md +42 -42
- package/src/admin/aggregation/practitioner/index.ts +1 -1
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
- package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
- package/src/admin/aggregation/procedure/README.md +43 -43
- package/src/admin/aggregation/procedure/index.ts +1 -1
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
- package/src/admin/aggregation/reviews/index.ts +1 -1
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +689 -641
- package/src/admin/booking/README.md +125 -125
- package/src/admin/booking/booking.admin.ts +1037 -1037
- package/src/admin/booking/booking.calculator.ts +712 -712
- package/src/admin/booking/booking.types.ts +59 -59
- package/src/admin/booking/index.ts +3 -3
- package/src/admin/booking/timezones-problem.md +185 -185
- package/src/admin/calendar/README.md +7 -7
- package/src/admin/calendar/calendar.admin.service.ts +345 -345
- package/src/admin/calendar/index.ts +1 -1
- package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
- package/src/admin/documentation-templates/index.ts +1 -1
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
- package/src/admin/free-consultation/index.ts +1 -1
- package/src/admin/index.ts +75 -75
- package/src/admin/logger/index.ts +78 -78
- package/src/admin/mailing/README.md +95 -95
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
- package/src/admin/mailing/appointment/index.ts +1 -1
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
- package/src/admin/mailing/base.mailing.service.ts +208 -208
- package/src/admin/mailing/index.ts +3 -3
- package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
- package/src/admin/mailing/practitionerInvite/index.ts +2 -2
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
- package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
- package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
- package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
- package/src/admin/notifications/index.ts +1 -1
- package/src/admin/notifications/notifications.admin.ts +710 -710
- package/src/admin/requirements/README.md +128 -128
- package/src/admin/requirements/index.ts +1 -1
- package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
- package/src/admin/users/index.ts +1 -1
- package/src/admin/users/user-profile.admin.ts +405 -405
- package/src/backoffice/constants/certification.constants.ts +13 -13
- package/src/backoffice/constants/index.ts +1 -1
- package/src/backoffice/errors/backoffice.errors.ts +181 -181
- package/src/backoffice/errors/index.ts +1 -1
- package/src/backoffice/expo-safe/README.md +26 -26
- package/src/backoffice/expo-safe/index.ts +41 -41
- package/src/backoffice/index.ts +5 -5
- package/src/backoffice/services/FIXES_README.md +102 -102
- package/src/backoffice/services/README.md +40 -40
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +341 -318
- package/src/backoffice/services/constants.service.ts +385 -385
- package/src/backoffice/services/documentation-template.service.ts +202 -202
- package/src/backoffice/services/index.ts +10 -10
- package/src/backoffice/services/migrate-products.ts +116 -116
- package/src/backoffice/services/product.service.ts +553 -553
- package/src/backoffice/services/requirement.service.ts +235 -235
- package/src/backoffice/services/subcategory.service.ts +417 -395
- package/src/backoffice/services/technology.service.ts +1104 -1083
- package/src/backoffice/types/README.md +12 -12
- package/src/backoffice/types/admin-constants.types.ts +69 -69
- package/src/backoffice/types/brand.types.ts +29 -29
- package/src/backoffice/types/category.types.ts +67 -62
- package/src/backoffice/types/documentation-templates.types.ts +28 -28
- package/src/backoffice/types/index.ts +10 -10
- package/src/backoffice/types/procedure-product.types.ts +38 -38
- package/src/backoffice/types/product.types.ts +240 -240
- package/src/backoffice/types/requirement.types.ts +63 -63
- package/src/backoffice/types/static/README.md +18 -18
- package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
- package/src/backoffice/types/static/certification.types.ts +37 -37
- package/src/backoffice/types/static/contraindication.types.ts +19 -19
- package/src/backoffice/types/static/index.ts +6 -6
- package/src/backoffice/types/static/pricing.types.ts +16 -16
- package/src/backoffice/types/static/procedure-family.types.ts +14 -14
- package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
- package/src/backoffice/types/subcategory.types.ts +34 -34
- package/src/backoffice/types/technology.types.ts +168 -163
- package/src/backoffice/validations/index.ts +1 -1
- package/src/backoffice/validations/schemas.ts +164 -164
- package/src/config/__mocks__/firebase.ts +99 -99
- package/src/config/firebase.ts +78 -78
- package/src/config/index.ts +9 -9
- package/src/errors/auth.error.ts +6 -6
- package/src/errors/auth.errors.ts +200 -200
- package/src/errors/clinic.errors.ts +32 -32
- package/src/errors/firebase.errors.ts +47 -47
- package/src/errors/user.errors.ts +99 -99
- package/src/index.backup.ts +407 -407
- package/src/index.ts +6 -6
- package/src/locales/en.ts +31 -31
- package/src/recommender/admin/index.ts +1 -1
- package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
- package/src/recommender/front/index.ts +1 -1
- package/src/recommender/front/services/onboarding.service.ts +5 -5
- package/src/recommender/front/services/recommender.service.ts +3 -3
- package/src/recommender/index.ts +1 -1
- package/src/services/PATIENTAUTH.MD +197 -197
- package/src/services/README.md +106 -106
- package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
- package/src/services/__tests__/auth/auth.setup.ts +293 -293
- package/src/services/__tests__/auth.service.test.ts +346 -346
- package/src/services/__tests__/base.service.test.ts +77 -77
- package/src/services/__tests__/user.service.test.ts +528 -528
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2505 -2505
- package/src/services/appointment/index.ts +1 -1
- package/src/services/appointment/utils/appointment.utils.ts +552 -552
- package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
- package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
- package/src/services/appointment/utils/zone-management.utils.ts +353 -353
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
- package/src/services/auth/auth.service.ts +989 -989
- package/src/services/auth/auth.v2.service.ts +961 -961
- package/src/services/auth/index.ts +7 -7
- package/src/services/auth/utils/error.utils.ts +90 -90
- package/src/services/auth/utils/firebase.utils.ts +49 -49
- package/src/services/auth/utils/index.ts +21 -21
- package/src/services/auth/utils/practitioner.utils.ts +125 -125
- package/src/services/base.service.ts +41 -41
- package/src/services/calendar/calendar.service.ts +1077 -1077
- package/src/services/calendar/calendar.v2.service.ts +1683 -1683
- package/src/services/calendar/calendar.v3.service.ts +313 -313
- package/src/services/calendar/externalCalendar.service.ts +178 -178
- package/src/services/calendar/index.ts +5 -5
- package/src/services/calendar/synced-calendars.service.ts +743 -743
- package/src/services/calendar/utils/appointment.utils.ts +265 -265
- package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
- package/src/services/calendar/utils/clinic.utils.ts +237 -237
- package/src/services/calendar/utils/docs.utils.ts +157 -157
- package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
- package/src/services/calendar/utils/index.ts +8 -8
- package/src/services/calendar/utils/patient.utils.ts +198 -198
- package/src/services/calendar/utils/practitioner.utils.ts +221 -221
- package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
- package/src/services/clinic/README.md +204 -204
- package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
- package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
- package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
- package/src/services/clinic/billing-transactions.service.ts +217 -217
- package/src/services/clinic/clinic-admin.service.ts +202 -202
- package/src/services/clinic/clinic-group.service.ts +310 -310
- package/src/services/clinic/clinic.service.ts +708 -708
- package/src/services/clinic/index.ts +5 -5
- package/src/services/clinic/practitioner-invite.service.ts +519 -519
- package/src/services/clinic/utils/admin.utils.ts +551 -551
- package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
- package/src/services/clinic/utils/clinic.utils.ts +949 -949
- package/src/services/clinic/utils/filter.utils.d.ts +23 -23
- package/src/services/clinic/utils/filter.utils.ts +446 -446
- package/src/services/clinic/utils/index.ts +11 -11
- package/src/services/clinic/utils/photos.utils.ts +188 -188
- package/src/services/clinic/utils/search.utils.ts +84 -84
- package/src/services/clinic/utils/tag.utils.ts +124 -124
- package/src/services/documentation-templates/documentation-template.service.ts +537 -537
- package/src/services/documentation-templates/filled-document.service.ts +587 -587
- package/src/services/documentation-templates/index.ts +2 -2
- package/src/services/index.ts +13 -13
- package/src/services/media/index.ts +1 -1
- package/src/services/media/media.service.ts +418 -418
- package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/notification.service.ts +215 -215
- package/src/services/patient/README.md +48 -48
- package/src/services/patient/To-Do.md +43 -43
- package/src/services/patient/__tests__/patient.service.test.ts +294 -294
- package/src/services/patient/index.ts +2 -2
- package/src/services/patient/patient.service.ts +883 -883
- package/src/services/patient/patientRequirements.service.ts +285 -285
- package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
- package/src/services/patient/utils/clinic.utils.ts +80 -80
- package/src/services/patient/utils/docs.utils.ts +142 -142
- package/src/services/patient/utils/index.ts +9 -9
- package/src/services/patient/utils/location.utils.ts +126 -126
- package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
- package/src/services/patient/utils/medical.utils.ts +458 -458
- package/src/services/patient/utils/practitioner.utils.ts +260 -260
- package/src/services/patient/utils/profile.utils.ts +510 -510
- package/src/services/patient/utils/sensitive.utils.ts +260 -260
- package/src/services/patient/utils/token.utils.ts +211 -211
- package/src/services/practitioner/README.md +145 -145
- package/src/services/practitioner/index.ts +1 -1
- package/src/services/practitioner/practitioner.service.ts +1742 -1742
- package/src/services/procedure/README.md +163 -163
- package/src/services/procedure/index.ts +1 -1
- package/src/services/procedure/procedure.service.ts +1715 -1715
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +683 -636
- package/src/services/user/index.ts +1 -1
- package/src/services/user/user.service.ts +489 -489
- package/src/services/user/user.v2.service.ts +466 -466
- package/src/types/appointment/index.ts +480 -480
- package/src/types/calendar/index.ts +258 -258
- package/src/types/calendar/synced-calendar.types.ts +66 -66
- package/src/types/clinic/index.ts +489 -489
- package/src/types/clinic/practitioner-invite.types.ts +91 -91
- package/src/types/clinic/preferences.types.ts +159 -159
- package/src/types/clinic/to-do +3 -3
- package/src/types/documentation-templates/index.ts +308 -308
- package/src/types/index.ts +44 -44
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +265 -265
- package/src/types/patient/aesthetic-analysis.types.ts +66 -66
- package/src/types/patient/allergies.ts +58 -58
- package/src/types/patient/index.ts +275 -275
- package/src/types/patient/medical-info.types.ts +152 -152
- package/src/types/patient/patient-requirements.ts +92 -92
- package/src/types/patient/token.types.ts +61 -61
- package/src/types/practitioner/index.ts +206 -206
- package/src/types/procedure/index.ts +181 -181
- package/src/types/profile/index.ts +39 -39
- package/src/types/reviews/index.ts +132 -130
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +493 -493
- package/src/validations/common.schema.ts +25 -25
- package/src/validations/documentation-templates/index.ts +1 -1
- package/src/validations/documentation-templates/template.schema.ts +220 -220
- package/src/validations/documentation-templates.schema.ts +10 -10
- package/src/validations/index.ts +20 -20
- package/src/validations/media.schema.ts +10 -10
- package/src/validations/notification.schema.ts +90 -90
- package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
- package/src/validations/patient/medical-info.schema.ts +125 -125
- package/src/validations/patient/patient-requirements.schema.ts +84 -84
- package/src/validations/patient/token.schema.ts +29 -29
- package/src/validations/patient.schema.ts +217 -217
- package/src/validations/practitioner.schema.ts +222 -222
- package/src/validations/procedure-product.schema.ts +41 -41
- package/src/validations/procedure.schema.ts +124 -124
- package/src/validations/profile-info.schema.ts +41 -41
- package/src/validations/reviews.schema.ts +195 -189
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,702 +1,702 @@
|
|
|
1
|
-
import * as admin from "firebase-admin";
|
|
2
|
-
import { PRACTITIONERS_COLLECTION } from "../../../types/practitioner";
|
|
3
|
-
import { PROCEDURES_COLLECTION } from "../../../types/procedure";
|
|
4
|
-
import { CLINICS_COLLECTION } from "../../../types/clinic";
|
|
5
|
-
import { ProcedureSummaryInfo } from "../../../types/procedure";
|
|
6
|
-
|
|
7
|
-
const CALENDAR_SUBCOLLECTION_ID = "calendar";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @class ProcedureAggregationService
|
|
11
|
-
* @description Handles aggregation tasks related to procedure data updates/deletions.
|
|
12
|
-
*/
|
|
13
|
-
export class ProcedureAggregationService {
|
|
14
|
-
private db: admin.firestore.Firestore;
|
|
15
|
-
|
|
16
|
-
constructor(firestore?: admin.firestore.Firestore) {
|
|
17
|
-
this.db = firestore || admin.firestore();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Adds procedure information to a practitioner when a new procedure is created
|
|
22
|
-
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
23
|
-
* @param procedureSummary - Summary information about the procedure
|
|
24
|
-
* @returns {Promise<void>}
|
|
25
|
-
*/
|
|
26
|
-
async addProcedureToPractitioner(
|
|
27
|
-
practitionerId: string,
|
|
28
|
-
procedureSummary: ProcedureSummaryInfo
|
|
29
|
-
): Promise<void> {
|
|
30
|
-
if (!practitionerId || !procedureSummary) {
|
|
31
|
-
console.log(
|
|
32
|
-
"[ProcedureAggregationService] Missing practitionerId or procedureSummary for adding procedure to practitioner. Skipping."
|
|
33
|
-
);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const procedureId = procedureSummary.id;
|
|
38
|
-
const clinicId = procedureSummary.clinicId;
|
|
39
|
-
const isFreeConsultation =
|
|
40
|
-
procedureSummary.technologyName === "free-consultation-tech";
|
|
41
|
-
|
|
42
|
-
console.log(
|
|
43
|
-
`[ProcedureAggregationService] Adding procedure ${procedureId} to practitioner ${practitionerId}. ${
|
|
44
|
-
isFreeConsultation ? "(Free Consultation)" : ""
|
|
45
|
-
}`
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const practitionerRef = this.db
|
|
49
|
-
.collection(PRACTITIONERS_COLLECTION)
|
|
50
|
-
.doc(practitionerId);
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
// Prepare the basic update data
|
|
54
|
-
const updateData: any = {
|
|
55
|
-
procedureIds: admin.firestore.FieldValue.arrayUnion(procedureId),
|
|
56
|
-
proceduresInfo: admin.firestore.FieldValue.arrayUnion(procedureSummary),
|
|
57
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// If this is a free consultation, we need to update the freeConsultations map
|
|
61
|
-
if (isFreeConsultation) {
|
|
62
|
-
await this.db.runTransaction(async (transaction) => {
|
|
63
|
-
const practitionerDoc = await transaction.get(practitionerRef);
|
|
64
|
-
if (!practitionerDoc.exists) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Practitioner ${practitionerId} does not exist for adding free consultation`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const practitionerData = practitionerDoc.data();
|
|
71
|
-
const currentFreeConsultations =
|
|
72
|
-
practitionerData?.freeConsultations || {};
|
|
73
|
-
|
|
74
|
-
// Check if there's already a free consultation for this clinic
|
|
75
|
-
if (currentFreeConsultations[clinicId]) {
|
|
76
|
-
console.log(
|
|
77
|
-
`[ProcedureAggregationService] Warning: Clinic ${clinicId} already has a free consultation ${currentFreeConsultations[clinicId]}. Replacing with new one ${procedureId}.`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Set the single procedure ID for this clinic (replaces any existing one)
|
|
82
|
-
const updatedFreeConsultations = {
|
|
83
|
-
...currentFreeConsultations,
|
|
84
|
-
[clinicId]: procedureId,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Apply all updates including freeConsultations
|
|
88
|
-
transaction.update(practitionerRef, {
|
|
89
|
-
...updateData,
|
|
90
|
-
freeConsultations: updatedFreeConsultations,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
console.log(
|
|
94
|
-
`[ProcedureAggregationService] Set free consultation ${procedureId} for practitioner ${practitionerId} at clinic ${clinicId}`
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
} else {
|
|
98
|
-
// For regular procedures, just do the standard update
|
|
99
|
-
await practitionerRef.update(updateData);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log(
|
|
103
|
-
`[ProcedureAggregationService] Successfully added procedure ${procedureId} to practitioner ${practitionerId}.`
|
|
104
|
-
);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error(
|
|
107
|
-
`[ProcedureAggregationService] Error adding procedure ${procedureId} to practitioner ${practitionerId}:`,
|
|
108
|
-
error
|
|
109
|
-
);
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Adds procedure information to a clinic when a new procedure is created
|
|
116
|
-
* @param clinicId - ID of the clinic where the procedure is performed
|
|
117
|
-
* @param procedureSummary - Summary information about the procedure
|
|
118
|
-
* @returns {Promise<void>}
|
|
119
|
-
*/
|
|
120
|
-
async addProcedureToClinic(
|
|
121
|
-
clinicId: string,
|
|
122
|
-
procedureSummary: ProcedureSummaryInfo
|
|
123
|
-
): Promise<void> {
|
|
124
|
-
if (!clinicId || !procedureSummary) {
|
|
125
|
-
console.log(
|
|
126
|
-
"[ProcedureAggregationService] Missing clinicId or procedureSummary for adding procedure to clinic. Skipping."
|
|
127
|
-
);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const procedureId = procedureSummary.id;
|
|
132
|
-
console.log(
|
|
133
|
-
`[ProcedureAggregationService] Adding procedure ${procedureId} to clinic ${clinicId}.`
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
await clinicRef.update({
|
|
140
|
-
procedures: admin.firestore.FieldValue.arrayUnion(procedureId),
|
|
141
|
-
proceduresInfo: admin.firestore.FieldValue.arrayUnion(procedureSummary),
|
|
142
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
console.log(
|
|
146
|
-
`[ProcedureAggregationService] Successfully added procedure ${procedureId} to clinic ${clinicId}.`
|
|
147
|
-
);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
console.error(
|
|
150
|
-
`[ProcedureAggregationService] Error adding procedure ${procedureId} to clinic ${clinicId}:`,
|
|
151
|
-
error
|
|
152
|
-
);
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Updates procedure information in a practitioner document
|
|
159
|
-
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
160
|
-
* @param procedureSummary - Updated summary information about the procedure
|
|
161
|
-
* @returns {Promise<void>}
|
|
162
|
-
*/
|
|
163
|
-
async updateProcedureInfoInPractitioner(
|
|
164
|
-
practitionerId: string,
|
|
165
|
-
procedureSummary: ProcedureSummaryInfo
|
|
166
|
-
): Promise<void> {
|
|
167
|
-
if (!practitionerId || !procedureSummary) {
|
|
168
|
-
console.log(
|
|
169
|
-
"[ProcedureAggregationService] Missing practitionerId or procedureSummary for updating procedure in practitioner. Skipping."
|
|
170
|
-
);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const procedureId = procedureSummary.id;
|
|
175
|
-
console.log(
|
|
176
|
-
`[ProcedureAggregationService] Updating procedure ${procedureId} info in practitioner ${practitionerId}.`
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const practitionerRef = this.db
|
|
180
|
-
.collection(PRACTITIONERS_COLLECTION)
|
|
181
|
-
.doc(practitionerId);
|
|
182
|
-
|
|
183
|
-
// This requires a transaction to avoid race conditions
|
|
184
|
-
try {
|
|
185
|
-
await this.db.runTransaction(async (transaction) => {
|
|
186
|
-
const practitionerDoc = await transaction.get(practitionerRef);
|
|
187
|
-
if (!practitionerDoc.exists) {
|
|
188
|
-
throw new Error(
|
|
189
|
-
`Practitioner ${practitionerId} does not exist for procedure update`
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const practitionerData = practitionerDoc.data();
|
|
194
|
-
if (!practitionerData) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`Practitioner ${practitionerId} data is empty for procedure update`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Get current procedures info array
|
|
201
|
-
const proceduresInfo = practitionerData.proceduresInfo || [];
|
|
202
|
-
|
|
203
|
-
// Remove the old procedure summary
|
|
204
|
-
const updatedProceduresInfo = proceduresInfo.filter(
|
|
205
|
-
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
// Add the updated procedure summary
|
|
209
|
-
updatedProceduresInfo.push(procedureSummary);
|
|
210
|
-
|
|
211
|
-
// Update the practitioner document
|
|
212
|
-
transaction.update(practitionerRef, {
|
|
213
|
-
proceduresInfo: updatedProceduresInfo,
|
|
214
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
console.log(
|
|
219
|
-
`[ProcedureAggregationService] Successfully updated procedure ${procedureId} info in practitioner ${practitionerId}.`
|
|
220
|
-
);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.error(
|
|
223
|
-
`[ProcedureAggregationService] Error updating procedure ${procedureId} info in practitioner ${practitionerId}:`,
|
|
224
|
-
error
|
|
225
|
-
);
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Updates procedure information in a clinic document
|
|
232
|
-
* @param clinicId - ID of the clinic where the procedure is performed
|
|
233
|
-
* @param procedureSummary - Updated summary information about the procedure
|
|
234
|
-
* @returns {Promise<void>}
|
|
235
|
-
*/
|
|
236
|
-
async updateProcedureInfoInClinic(
|
|
237
|
-
clinicId: string,
|
|
238
|
-
procedureSummary: ProcedureSummaryInfo
|
|
239
|
-
): Promise<void> {
|
|
240
|
-
if (!clinicId || !procedureSummary) {
|
|
241
|
-
console.log(
|
|
242
|
-
"[ProcedureAggregationService] Missing clinicId or procedureSummary for updating procedure in clinic. Skipping."
|
|
243
|
-
);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const procedureId = procedureSummary.id;
|
|
248
|
-
console.log(
|
|
249
|
-
`[ProcedureAggregationService] Updating procedure ${procedureId} info in clinic ${clinicId}.`
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
253
|
-
|
|
254
|
-
// This requires a transaction to avoid race conditions
|
|
255
|
-
try {
|
|
256
|
-
await this.db.runTransaction(async (transaction) => {
|
|
257
|
-
const clinicDoc = await transaction.get(clinicRef);
|
|
258
|
-
if (!clinicDoc.exists) {
|
|
259
|
-
throw new Error(
|
|
260
|
-
`Clinic ${clinicId} does not exist for procedure update`
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const clinicData = clinicDoc.data();
|
|
265
|
-
if (!clinicData) {
|
|
266
|
-
throw new Error(
|
|
267
|
-
`Clinic ${clinicId} data is empty for procedure update`
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Get current procedures info array
|
|
272
|
-
const proceduresInfo = clinicData.proceduresInfo || [];
|
|
273
|
-
|
|
274
|
-
// Remove the old procedure summary
|
|
275
|
-
const updatedProceduresInfo = proceduresInfo.filter(
|
|
276
|
-
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// Add the updated procedure summary
|
|
280
|
-
updatedProceduresInfo.push(procedureSummary);
|
|
281
|
-
|
|
282
|
-
// Update the clinic document
|
|
283
|
-
transaction.update(clinicRef, {
|
|
284
|
-
proceduresInfo: updatedProceduresInfo,
|
|
285
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
console.log(
|
|
290
|
-
`[ProcedureAggregationService] Successfully updated procedure ${procedureId} info in clinic ${clinicId}.`
|
|
291
|
-
);
|
|
292
|
-
} catch (error) {
|
|
293
|
-
console.error(
|
|
294
|
-
`[ProcedureAggregationService] Error updating procedure ${procedureId} info in clinic ${clinicId}:`,
|
|
295
|
-
error
|
|
296
|
-
);
|
|
297
|
-
throw error;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Updates procedure information in calendar events
|
|
303
|
-
* @param procedureId - ID of the procedure
|
|
304
|
-
* @param procedureInfo - Updated procedure information
|
|
305
|
-
* @returns {Promise<void>}
|
|
306
|
-
*/
|
|
307
|
-
async updateProcedureInfoInCalendarEvents(
|
|
308
|
-
procedureId: string,
|
|
309
|
-
procedureInfo: any
|
|
310
|
-
): Promise<void> {
|
|
311
|
-
if (!procedureId || !procedureInfo) {
|
|
312
|
-
console.log(
|
|
313
|
-
"[ProcedureAggregationService] Missing procedureId or procedureInfo for calendar update. Skipping."
|
|
314
|
-
);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
console.log(
|
|
319
|
-
`[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to update procedure info.`
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
const now = admin.firestore.Timestamp.now();
|
|
323
|
-
// Use a Collection Group query
|
|
324
|
-
const calendarEventsQuery = this.db
|
|
325
|
-
.collectionGroup(CALENDAR_SUBCOLLECTION_ID)
|
|
326
|
-
.where("procedureId", "==", procedureId)
|
|
327
|
-
.where("eventTime.start", ">", now);
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
const snapshot = await calendarEventsQuery.get();
|
|
331
|
-
if (snapshot.empty) {
|
|
332
|
-
console.log(
|
|
333
|
-
`[ProcedureAggregationService] No upcoming calendar events found for procedure ${procedureId}. No procedure info updates needed.`
|
|
334
|
-
);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const batch = this.db.batch();
|
|
339
|
-
snapshot.docs.forEach((doc) => {
|
|
340
|
-
console.log(
|
|
341
|
-
`[ProcedureAggregationService] Updating procedure info for calendar event ${doc.ref.path}`
|
|
342
|
-
);
|
|
343
|
-
batch.update(doc.ref, {
|
|
344
|
-
procedureInfo: procedureInfo,
|
|
345
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
await batch.commit();
|
|
350
|
-
console.log(
|
|
351
|
-
`[ProcedureAggregationService] Successfully updated procedure info in ${snapshot.size} upcoming calendar events for procedure ${procedureId}.`
|
|
352
|
-
);
|
|
353
|
-
} catch (error) {
|
|
354
|
-
console.error(
|
|
355
|
-
`[ProcedureAggregationService] Error updating procedure info in calendar events for procedure ${procedureId}:`,
|
|
356
|
-
error
|
|
357
|
-
);
|
|
358
|
-
throw error;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Cancels all upcoming calendar events for a procedure
|
|
364
|
-
* @param procedureId - ID of the procedure
|
|
365
|
-
* @returns {Promise<void>}
|
|
366
|
-
*/
|
|
367
|
-
async cancelUpcomingCalendarEventsForProcedure(
|
|
368
|
-
procedureId: string
|
|
369
|
-
): Promise<void> {
|
|
370
|
-
if (!procedureId) {
|
|
371
|
-
console.log(
|
|
372
|
-
"[ProcedureAggregationService] Missing procedureId for canceling calendar events. Skipping."
|
|
373
|
-
);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
console.log(
|
|
378
|
-
`[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to cancel.`
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
const now = admin.firestore.Timestamp.now();
|
|
382
|
-
// Use a Collection Group query
|
|
383
|
-
const calendarEventsQuery = this.db
|
|
384
|
-
.collectionGroup(CALENDAR_SUBCOLLECTION_ID)
|
|
385
|
-
.where("procedureId", "==", procedureId)
|
|
386
|
-
.where("eventTime.start", ">", now);
|
|
387
|
-
|
|
388
|
-
try {
|
|
389
|
-
const snapshot = await calendarEventsQuery.get();
|
|
390
|
-
if (snapshot.empty) {
|
|
391
|
-
console.log(
|
|
392
|
-
`[ProcedureAggregationService] No upcoming calendar events found for procedure ${procedureId}. No events to cancel.`
|
|
393
|
-
);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const batch = this.db.batch();
|
|
398
|
-
snapshot.docs.forEach((doc) => {
|
|
399
|
-
console.log(
|
|
400
|
-
`[ProcedureAggregationService] Canceling calendar event ${doc.ref.path}`
|
|
401
|
-
);
|
|
402
|
-
batch.update(doc.ref, {
|
|
403
|
-
status: "CANCELED",
|
|
404
|
-
cancelReason: "Procedure deleted or inactivated",
|
|
405
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
await batch.commit();
|
|
410
|
-
console.log(
|
|
411
|
-
`[ProcedureAggregationService] Successfully canceled ${snapshot.size} upcoming calendar events for procedure ${procedureId}.`
|
|
412
|
-
);
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error(
|
|
415
|
-
`[ProcedureAggregationService] Error canceling calendar events for procedure ${procedureId}:`,
|
|
416
|
-
error
|
|
417
|
-
);
|
|
418
|
-
throw error;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Removes procedure from a practitioner when a procedure is deleted or inactivated
|
|
424
|
-
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
425
|
-
* @param procedureId - ID of the procedure
|
|
426
|
-
* @param clinicId - Optional clinic ID for free consultation removal
|
|
427
|
-
* @returns {Promise<void>}
|
|
428
|
-
*/
|
|
429
|
-
async removeProcedureFromPractitioner(
|
|
430
|
-
practitionerId: string,
|
|
431
|
-
procedureId: string,
|
|
432
|
-
clinicId?: string
|
|
433
|
-
): Promise<void> {
|
|
434
|
-
if (!practitionerId || !procedureId) {
|
|
435
|
-
console.log(
|
|
436
|
-
"[ProcedureAggregationService] Missing practitionerId or procedureId for removing procedure from practitioner. Skipping."
|
|
437
|
-
);
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
console.log(
|
|
442
|
-
`[ProcedureAggregationService] Removing procedure ${procedureId} from practitioner ${practitionerId}.`
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
const practitionerRef = this.db
|
|
446
|
-
.collection(PRACTITIONERS_COLLECTION)
|
|
447
|
-
.doc(practitionerId);
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
await this.db.runTransaction(async (transaction) => {
|
|
451
|
-
const practitionerDoc = await transaction.get(practitionerRef);
|
|
452
|
-
if (!practitionerDoc.exists) {
|
|
453
|
-
throw new Error(
|
|
454
|
-
`Practitioner ${practitionerId} does not exist for procedure removal`
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const practitionerData = practitionerDoc.data();
|
|
459
|
-
if (!practitionerData) {
|
|
460
|
-
throw new Error(
|
|
461
|
-
`Practitioner ${practitionerId} data is empty for procedure removal`
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Get current procedures info array
|
|
466
|
-
const proceduresInfo = practitionerData.proceduresInfo || [];
|
|
467
|
-
|
|
468
|
-
// Find the procedure being removed to check if it's a free consultation
|
|
469
|
-
const procedureBeingRemoved = proceduresInfo.find(
|
|
470
|
-
(p: any) => p.id === procedureId
|
|
471
|
-
);
|
|
472
|
-
|
|
473
|
-
const isFreeConsultation =
|
|
474
|
-
procedureBeingRemoved?.technologyName === "free-consultation-tech";
|
|
475
|
-
const procedureClinicId = clinicId || procedureBeingRemoved?.clinicId;
|
|
476
|
-
|
|
477
|
-
// Remove the procedure summary
|
|
478
|
-
const updatedProceduresInfo = proceduresInfo.filter(
|
|
479
|
-
(p: any) => p.id !== procedureId
|
|
480
|
-
);
|
|
481
|
-
|
|
482
|
-
// Prepare update data
|
|
483
|
-
const updateData: any = {
|
|
484
|
-
procedureIds: admin.firestore.FieldValue.arrayRemove(procedureId),
|
|
485
|
-
proceduresInfo: updatedProceduresInfo,
|
|
486
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
// If this is a free consultation, also remove it from the freeConsultations map
|
|
490
|
-
if (isFreeConsultation && procedureClinicId) {
|
|
491
|
-
const currentFreeConsultations =
|
|
492
|
-
practitionerData.freeConsultations || {};
|
|
493
|
-
|
|
494
|
-
// Check if this clinic's free consultation matches the procedure being removed
|
|
495
|
-
if (currentFreeConsultations[procedureClinicId] === procedureId) {
|
|
496
|
-
// Remove the clinic entry from the freeConsultations map
|
|
497
|
-
const updatedFreeConsultations = { ...currentFreeConsultations };
|
|
498
|
-
delete updatedFreeConsultations[procedureClinicId];
|
|
499
|
-
|
|
500
|
-
updateData.freeConsultations = updatedFreeConsultations;
|
|
501
|
-
|
|
502
|
-
console.log(
|
|
503
|
-
`[ProcedureAggregationService] Removed free consultation ${procedureId} from practitioner ${practitionerId} for clinic ${procedureClinicId}`
|
|
504
|
-
);
|
|
505
|
-
} else {
|
|
506
|
-
console.log(
|
|
507
|
-
`[ProcedureAggregationService] Free consultation mismatch for clinic ${procedureClinicId}: expected ${procedureId}, found ${currentFreeConsultations[procedureClinicId]}`
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Update the practitioner document
|
|
513
|
-
transaction.update(practitionerRef, updateData);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
console.log(
|
|
517
|
-
`[ProcedureAggregationService] Successfully removed procedure ${procedureId} from practitioner ${practitionerId}.`
|
|
518
|
-
);
|
|
519
|
-
} catch (error) {
|
|
520
|
-
console.error(
|
|
521
|
-
`[ProcedureAggregationService] Error removing procedure ${procedureId} from practitioner ${practitionerId}:`,
|
|
522
|
-
error
|
|
523
|
-
);
|
|
524
|
-
throw error;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Removes procedure from a clinic when a procedure is deleted or inactivated
|
|
530
|
-
* @param clinicId - ID of the clinic where the procedure is performed
|
|
531
|
-
* @param procedureId - ID of the procedure
|
|
532
|
-
* @returns {Promise<void>}
|
|
533
|
-
*/
|
|
534
|
-
async removeProcedureFromClinic(
|
|
535
|
-
clinicId: string,
|
|
536
|
-
procedureId: string
|
|
537
|
-
): Promise<void> {
|
|
538
|
-
if (!clinicId || !procedureId) {
|
|
539
|
-
console.log(
|
|
540
|
-
"[ProcedureAggregationService] Missing clinicId or procedureId for removing procedure from clinic. Skipping."
|
|
541
|
-
);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
console.log(
|
|
546
|
-
`[ProcedureAggregationService] Removing procedure ${procedureId} from clinic ${clinicId}.`
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
550
|
-
|
|
551
|
-
try {
|
|
552
|
-
await this.db.runTransaction(async (transaction) => {
|
|
553
|
-
const clinicDoc = await transaction.get(clinicRef);
|
|
554
|
-
if (!clinicDoc.exists) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
`Clinic ${clinicId} does not exist for procedure removal`
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const clinicData = clinicDoc.data();
|
|
561
|
-
if (!clinicData) {
|
|
562
|
-
throw new Error(
|
|
563
|
-
`Clinic ${clinicId} data is empty for procedure removal`
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// Get current procedures info array
|
|
568
|
-
const proceduresInfo = clinicData.proceduresInfo || [];
|
|
569
|
-
|
|
570
|
-
// Remove the procedure summary
|
|
571
|
-
const updatedProceduresInfo = proceduresInfo.filter(
|
|
572
|
-
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
573
|
-
);
|
|
574
|
-
|
|
575
|
-
// Update the clinic document
|
|
576
|
-
transaction.update(clinicRef, {
|
|
577
|
-
procedures: admin.firestore.FieldValue.arrayRemove(procedureId),
|
|
578
|
-
proceduresInfo: updatedProceduresInfo,
|
|
579
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
console.log(
|
|
584
|
-
`[ProcedureAggregationService] Successfully removed procedure ${procedureId} from clinic ${clinicId}.`
|
|
585
|
-
);
|
|
586
|
-
} catch (error) {
|
|
587
|
-
console.error(
|
|
588
|
-
`[ProcedureAggregationService] Error removing procedure ${procedureId} from clinic ${clinicId}:`,
|
|
589
|
-
error
|
|
590
|
-
);
|
|
591
|
-
throw error;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Handles procedure status changes (activation/deactivation) specifically for free consultations
|
|
597
|
-
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
598
|
-
* @param procedureId - ID of the procedure
|
|
599
|
-
* @param clinicId - ID of the clinic where the procedure is performed
|
|
600
|
-
* @param isActive - New active status of the procedure
|
|
601
|
-
* @param technologyName - Technology name of the procedure (to check if it's a free consultation)
|
|
602
|
-
* @returns {Promise<void>}
|
|
603
|
-
*/
|
|
604
|
-
async handleFreeConsultationStatusChange(
|
|
605
|
-
practitionerId: string,
|
|
606
|
-
procedureId: string,
|
|
607
|
-
clinicId: string,
|
|
608
|
-
isActive: boolean,
|
|
609
|
-
technologyName: string
|
|
610
|
-
): Promise<void> {
|
|
611
|
-
if (!practitionerId || !procedureId || !clinicId) {
|
|
612
|
-
console.log(
|
|
613
|
-
"[ProcedureAggregationService] Missing required parameters for handling free consultation status change. Skipping."
|
|
614
|
-
);
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
// Only handle free consultations
|
|
619
|
-
if (technologyName !== "free-consultation-tech") {
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
console.log(
|
|
624
|
-
`[ProcedureAggregationService] Handling free consultation status change: procedure ${procedureId} for practitioner ${practitionerId} in clinic ${clinicId}. New status: ${
|
|
625
|
-
isActive ? "active" : "inactive"
|
|
626
|
-
}`
|
|
627
|
-
);
|
|
628
|
-
|
|
629
|
-
const practitionerRef = this.db
|
|
630
|
-
.collection(PRACTITIONERS_COLLECTION)
|
|
631
|
-
.doc(practitionerId);
|
|
632
|
-
|
|
633
|
-
try {
|
|
634
|
-
await this.db.runTransaction(async (transaction) => {
|
|
635
|
-
const practitionerDoc = await transaction.get(practitionerRef);
|
|
636
|
-
if (!practitionerDoc.exists) {
|
|
637
|
-
throw new Error(
|
|
638
|
-
`Practitioner ${practitionerId} does not exist for free consultation status change`
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
const practitionerData = practitionerDoc.data();
|
|
643
|
-
if (!practitionerData) {
|
|
644
|
-
throw new Error(
|
|
645
|
-
`Practitioner ${practitionerId} data is empty for free consultation status change`
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
const currentFreeConsultations =
|
|
650
|
-
practitionerData.freeConsultations || {};
|
|
651
|
-
let updatedFreeConsultations = { ...currentFreeConsultations };
|
|
652
|
-
|
|
653
|
-
if (isActive) {
|
|
654
|
-
// If procedure is being activated, set it as the free consultation for this clinic
|
|
655
|
-
if (
|
|
656
|
-
currentFreeConsultations[clinicId] &&
|
|
657
|
-
currentFreeConsultations[clinicId] !== procedureId
|
|
658
|
-
) {
|
|
659
|
-
console.log(
|
|
660
|
-
`[ProcedureAggregationService] Warning: Clinic ${clinicId} already has a different free consultation ${currentFreeConsultations[clinicId]}. Replacing with ${procedureId}.`
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
updatedFreeConsultations[clinicId] = procedureId;
|
|
665
|
-
|
|
666
|
-
console.log(
|
|
667
|
-
`[ProcedureAggregationService] Set free consultation ${procedureId} for practitioner ${practitionerId} at clinic ${clinicId} (activated)`
|
|
668
|
-
);
|
|
669
|
-
} else {
|
|
670
|
-
// If procedure is being deactivated, remove it from the free consultations map
|
|
671
|
-
if (currentFreeConsultations[clinicId] === procedureId) {
|
|
672
|
-
delete updatedFreeConsultations[clinicId];
|
|
673
|
-
|
|
674
|
-
console.log(
|
|
675
|
-
`[ProcedureAggregationService] Removed free consultation ${procedureId} from practitioner ${practitionerId} for clinic ${clinicId} (deactivated)`
|
|
676
|
-
);
|
|
677
|
-
} else {
|
|
678
|
-
console.log(
|
|
679
|
-
`[ProcedureAggregationService] Free consultation mismatch for clinic ${clinicId}: expected ${procedureId}, found ${currentFreeConsultations[clinicId]}`
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Update the practitioner document with the new freeConsultations map
|
|
685
|
-
transaction.update(practitionerRef, {
|
|
686
|
-
freeConsultations: updatedFreeConsultations,
|
|
687
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
console.log(
|
|
692
|
-
`[ProcedureAggregationService] Successfully handled free consultation status change for procedure ${procedureId} of practitioner ${practitionerId}.`
|
|
693
|
-
);
|
|
694
|
-
} catch (error) {
|
|
695
|
-
console.error(
|
|
696
|
-
`[ProcedureAggregationService] Error handling free consultation status change for procedure ${procedureId} of practitioner ${practitionerId}:`,
|
|
697
|
-
error
|
|
698
|
-
);
|
|
699
|
-
throw error;
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
1
|
+
import * as admin from "firebase-admin";
|
|
2
|
+
import { PRACTITIONERS_COLLECTION } from "../../../types/practitioner";
|
|
3
|
+
import { PROCEDURES_COLLECTION } from "../../../types/procedure";
|
|
4
|
+
import { CLINICS_COLLECTION } from "../../../types/clinic";
|
|
5
|
+
import { ProcedureSummaryInfo } from "../../../types/procedure";
|
|
6
|
+
|
|
7
|
+
const CALENDAR_SUBCOLLECTION_ID = "calendar";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @class ProcedureAggregationService
|
|
11
|
+
* @description Handles aggregation tasks related to procedure data updates/deletions.
|
|
12
|
+
*/
|
|
13
|
+
export class ProcedureAggregationService {
|
|
14
|
+
private db: admin.firestore.Firestore;
|
|
15
|
+
|
|
16
|
+
constructor(firestore?: admin.firestore.Firestore) {
|
|
17
|
+
this.db = firestore || admin.firestore();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Adds procedure information to a practitioner when a new procedure is created
|
|
22
|
+
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
23
|
+
* @param procedureSummary - Summary information about the procedure
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
*/
|
|
26
|
+
async addProcedureToPractitioner(
|
|
27
|
+
practitionerId: string,
|
|
28
|
+
procedureSummary: ProcedureSummaryInfo
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
if (!practitionerId || !procedureSummary) {
|
|
31
|
+
console.log(
|
|
32
|
+
"[ProcedureAggregationService] Missing practitionerId or procedureSummary for adding procedure to practitioner. Skipping."
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const procedureId = procedureSummary.id;
|
|
38
|
+
const clinicId = procedureSummary.clinicId;
|
|
39
|
+
const isFreeConsultation =
|
|
40
|
+
procedureSummary.technologyName === "free-consultation-tech";
|
|
41
|
+
|
|
42
|
+
console.log(
|
|
43
|
+
`[ProcedureAggregationService] Adding procedure ${procedureId} to practitioner ${practitionerId}. ${
|
|
44
|
+
isFreeConsultation ? "(Free Consultation)" : ""
|
|
45
|
+
}`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const practitionerRef = this.db
|
|
49
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
50
|
+
.doc(practitionerId);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Prepare the basic update data
|
|
54
|
+
const updateData: any = {
|
|
55
|
+
procedureIds: admin.firestore.FieldValue.arrayUnion(procedureId),
|
|
56
|
+
proceduresInfo: admin.firestore.FieldValue.arrayUnion(procedureSummary),
|
|
57
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// If this is a free consultation, we need to update the freeConsultations map
|
|
61
|
+
if (isFreeConsultation) {
|
|
62
|
+
await this.db.runTransaction(async (transaction) => {
|
|
63
|
+
const practitionerDoc = await transaction.get(practitionerRef);
|
|
64
|
+
if (!practitionerDoc.exists) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Practitioner ${practitionerId} does not exist for adding free consultation`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const practitionerData = practitionerDoc.data();
|
|
71
|
+
const currentFreeConsultations =
|
|
72
|
+
practitionerData?.freeConsultations || {};
|
|
73
|
+
|
|
74
|
+
// Check if there's already a free consultation for this clinic
|
|
75
|
+
if (currentFreeConsultations[clinicId]) {
|
|
76
|
+
console.log(
|
|
77
|
+
`[ProcedureAggregationService] Warning: Clinic ${clinicId} already has a free consultation ${currentFreeConsultations[clinicId]}. Replacing with new one ${procedureId}.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Set the single procedure ID for this clinic (replaces any existing one)
|
|
82
|
+
const updatedFreeConsultations = {
|
|
83
|
+
...currentFreeConsultations,
|
|
84
|
+
[clinicId]: procedureId,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Apply all updates including freeConsultations
|
|
88
|
+
transaction.update(practitionerRef, {
|
|
89
|
+
...updateData,
|
|
90
|
+
freeConsultations: updatedFreeConsultations,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
`[ProcedureAggregationService] Set free consultation ${procedureId} for practitioner ${practitionerId} at clinic ${clinicId}`
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
// For regular procedures, just do the standard update
|
|
99
|
+
await practitionerRef.update(updateData);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(
|
|
103
|
+
`[ProcedureAggregationService] Successfully added procedure ${procedureId} to practitioner ${practitionerId}.`
|
|
104
|
+
);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(
|
|
107
|
+
`[ProcedureAggregationService] Error adding procedure ${procedureId} to practitioner ${practitionerId}:`,
|
|
108
|
+
error
|
|
109
|
+
);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Adds procedure information to a clinic when a new procedure is created
|
|
116
|
+
* @param clinicId - ID of the clinic where the procedure is performed
|
|
117
|
+
* @param procedureSummary - Summary information about the procedure
|
|
118
|
+
* @returns {Promise<void>}
|
|
119
|
+
*/
|
|
120
|
+
async addProcedureToClinic(
|
|
121
|
+
clinicId: string,
|
|
122
|
+
procedureSummary: ProcedureSummaryInfo
|
|
123
|
+
): Promise<void> {
|
|
124
|
+
if (!clinicId || !procedureSummary) {
|
|
125
|
+
console.log(
|
|
126
|
+
"[ProcedureAggregationService] Missing clinicId or procedureSummary for adding procedure to clinic. Skipping."
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const procedureId = procedureSummary.id;
|
|
132
|
+
console.log(
|
|
133
|
+
`[ProcedureAggregationService] Adding procedure ${procedureId} to clinic ${clinicId}.`
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await clinicRef.update({
|
|
140
|
+
procedures: admin.firestore.FieldValue.arrayUnion(procedureId),
|
|
141
|
+
proceduresInfo: admin.firestore.FieldValue.arrayUnion(procedureSummary),
|
|
142
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log(
|
|
146
|
+
`[ProcedureAggregationService] Successfully added procedure ${procedureId} to clinic ${clinicId}.`
|
|
147
|
+
);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(
|
|
150
|
+
`[ProcedureAggregationService] Error adding procedure ${procedureId} to clinic ${clinicId}:`,
|
|
151
|
+
error
|
|
152
|
+
);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Updates procedure information in a practitioner document
|
|
159
|
+
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
160
|
+
* @param procedureSummary - Updated summary information about the procedure
|
|
161
|
+
* @returns {Promise<void>}
|
|
162
|
+
*/
|
|
163
|
+
async updateProcedureInfoInPractitioner(
|
|
164
|
+
practitionerId: string,
|
|
165
|
+
procedureSummary: ProcedureSummaryInfo
|
|
166
|
+
): Promise<void> {
|
|
167
|
+
if (!practitionerId || !procedureSummary) {
|
|
168
|
+
console.log(
|
|
169
|
+
"[ProcedureAggregationService] Missing practitionerId or procedureSummary for updating procedure in practitioner. Skipping."
|
|
170
|
+
);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const procedureId = procedureSummary.id;
|
|
175
|
+
console.log(
|
|
176
|
+
`[ProcedureAggregationService] Updating procedure ${procedureId} info in practitioner ${practitionerId}.`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const practitionerRef = this.db
|
|
180
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
181
|
+
.doc(practitionerId);
|
|
182
|
+
|
|
183
|
+
// This requires a transaction to avoid race conditions
|
|
184
|
+
try {
|
|
185
|
+
await this.db.runTransaction(async (transaction) => {
|
|
186
|
+
const practitionerDoc = await transaction.get(practitionerRef);
|
|
187
|
+
if (!practitionerDoc.exists) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Practitioner ${practitionerId} does not exist for procedure update`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const practitionerData = practitionerDoc.data();
|
|
194
|
+
if (!practitionerData) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Practitioner ${practitionerId} data is empty for procedure update`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Get current procedures info array
|
|
201
|
+
const proceduresInfo = practitionerData.proceduresInfo || [];
|
|
202
|
+
|
|
203
|
+
// Remove the old procedure summary
|
|
204
|
+
const updatedProceduresInfo = proceduresInfo.filter(
|
|
205
|
+
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Add the updated procedure summary
|
|
209
|
+
updatedProceduresInfo.push(procedureSummary);
|
|
210
|
+
|
|
211
|
+
// Update the practitioner document
|
|
212
|
+
transaction.update(practitionerRef, {
|
|
213
|
+
proceduresInfo: updatedProceduresInfo,
|
|
214
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
console.log(
|
|
219
|
+
`[ProcedureAggregationService] Successfully updated procedure ${procedureId} info in practitioner ${practitionerId}.`
|
|
220
|
+
);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error(
|
|
223
|
+
`[ProcedureAggregationService] Error updating procedure ${procedureId} info in practitioner ${practitionerId}:`,
|
|
224
|
+
error
|
|
225
|
+
);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Updates procedure information in a clinic document
|
|
232
|
+
* @param clinicId - ID of the clinic where the procedure is performed
|
|
233
|
+
* @param procedureSummary - Updated summary information about the procedure
|
|
234
|
+
* @returns {Promise<void>}
|
|
235
|
+
*/
|
|
236
|
+
async updateProcedureInfoInClinic(
|
|
237
|
+
clinicId: string,
|
|
238
|
+
procedureSummary: ProcedureSummaryInfo
|
|
239
|
+
): Promise<void> {
|
|
240
|
+
if (!clinicId || !procedureSummary) {
|
|
241
|
+
console.log(
|
|
242
|
+
"[ProcedureAggregationService] Missing clinicId or procedureSummary for updating procedure in clinic. Skipping."
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const procedureId = procedureSummary.id;
|
|
248
|
+
console.log(
|
|
249
|
+
`[ProcedureAggregationService] Updating procedure ${procedureId} info in clinic ${clinicId}.`
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
253
|
+
|
|
254
|
+
// This requires a transaction to avoid race conditions
|
|
255
|
+
try {
|
|
256
|
+
await this.db.runTransaction(async (transaction) => {
|
|
257
|
+
const clinicDoc = await transaction.get(clinicRef);
|
|
258
|
+
if (!clinicDoc.exists) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Clinic ${clinicId} does not exist for procedure update`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const clinicData = clinicDoc.data();
|
|
265
|
+
if (!clinicData) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Clinic ${clinicId} data is empty for procedure update`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Get current procedures info array
|
|
272
|
+
const proceduresInfo = clinicData.proceduresInfo || [];
|
|
273
|
+
|
|
274
|
+
// Remove the old procedure summary
|
|
275
|
+
const updatedProceduresInfo = proceduresInfo.filter(
|
|
276
|
+
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// Add the updated procedure summary
|
|
280
|
+
updatedProceduresInfo.push(procedureSummary);
|
|
281
|
+
|
|
282
|
+
// Update the clinic document
|
|
283
|
+
transaction.update(clinicRef, {
|
|
284
|
+
proceduresInfo: updatedProceduresInfo,
|
|
285
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
console.log(
|
|
290
|
+
`[ProcedureAggregationService] Successfully updated procedure ${procedureId} info in clinic ${clinicId}.`
|
|
291
|
+
);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error(
|
|
294
|
+
`[ProcedureAggregationService] Error updating procedure ${procedureId} info in clinic ${clinicId}:`,
|
|
295
|
+
error
|
|
296
|
+
);
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Updates procedure information in calendar events
|
|
303
|
+
* @param procedureId - ID of the procedure
|
|
304
|
+
* @param procedureInfo - Updated procedure information
|
|
305
|
+
* @returns {Promise<void>}
|
|
306
|
+
*/
|
|
307
|
+
async updateProcedureInfoInCalendarEvents(
|
|
308
|
+
procedureId: string,
|
|
309
|
+
procedureInfo: any
|
|
310
|
+
): Promise<void> {
|
|
311
|
+
if (!procedureId || !procedureInfo) {
|
|
312
|
+
console.log(
|
|
313
|
+
"[ProcedureAggregationService] Missing procedureId or procedureInfo for calendar update. Skipping."
|
|
314
|
+
);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log(
|
|
319
|
+
`[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to update procedure info.`
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const now = admin.firestore.Timestamp.now();
|
|
323
|
+
// Use a Collection Group query
|
|
324
|
+
const calendarEventsQuery = this.db
|
|
325
|
+
.collectionGroup(CALENDAR_SUBCOLLECTION_ID)
|
|
326
|
+
.where("procedureId", "==", procedureId)
|
|
327
|
+
.where("eventTime.start", ">", now);
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const snapshot = await calendarEventsQuery.get();
|
|
331
|
+
if (snapshot.empty) {
|
|
332
|
+
console.log(
|
|
333
|
+
`[ProcedureAggregationService] No upcoming calendar events found for procedure ${procedureId}. No procedure info updates needed.`
|
|
334
|
+
);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const batch = this.db.batch();
|
|
339
|
+
snapshot.docs.forEach((doc) => {
|
|
340
|
+
console.log(
|
|
341
|
+
`[ProcedureAggregationService] Updating procedure info for calendar event ${doc.ref.path}`
|
|
342
|
+
);
|
|
343
|
+
batch.update(doc.ref, {
|
|
344
|
+
procedureInfo: procedureInfo,
|
|
345
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await batch.commit();
|
|
350
|
+
console.log(
|
|
351
|
+
`[ProcedureAggregationService] Successfully updated procedure info in ${snapshot.size} upcoming calendar events for procedure ${procedureId}.`
|
|
352
|
+
);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error(
|
|
355
|
+
`[ProcedureAggregationService] Error updating procedure info in calendar events for procedure ${procedureId}:`,
|
|
356
|
+
error
|
|
357
|
+
);
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Cancels all upcoming calendar events for a procedure
|
|
364
|
+
* @param procedureId - ID of the procedure
|
|
365
|
+
* @returns {Promise<void>}
|
|
366
|
+
*/
|
|
367
|
+
async cancelUpcomingCalendarEventsForProcedure(
|
|
368
|
+
procedureId: string
|
|
369
|
+
): Promise<void> {
|
|
370
|
+
if (!procedureId) {
|
|
371
|
+
console.log(
|
|
372
|
+
"[ProcedureAggregationService] Missing procedureId for canceling calendar events. Skipping."
|
|
373
|
+
);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log(
|
|
378
|
+
`[ProcedureAggregationService] Querying upcoming calendar events for procedure ${procedureId} to cancel.`
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
const now = admin.firestore.Timestamp.now();
|
|
382
|
+
// Use a Collection Group query
|
|
383
|
+
const calendarEventsQuery = this.db
|
|
384
|
+
.collectionGroup(CALENDAR_SUBCOLLECTION_ID)
|
|
385
|
+
.where("procedureId", "==", procedureId)
|
|
386
|
+
.where("eventTime.start", ">", now);
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const snapshot = await calendarEventsQuery.get();
|
|
390
|
+
if (snapshot.empty) {
|
|
391
|
+
console.log(
|
|
392
|
+
`[ProcedureAggregationService] No upcoming calendar events found for procedure ${procedureId}. No events to cancel.`
|
|
393
|
+
);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const batch = this.db.batch();
|
|
398
|
+
snapshot.docs.forEach((doc) => {
|
|
399
|
+
console.log(
|
|
400
|
+
`[ProcedureAggregationService] Canceling calendar event ${doc.ref.path}`
|
|
401
|
+
);
|
|
402
|
+
batch.update(doc.ref, {
|
|
403
|
+
status: "CANCELED",
|
|
404
|
+
cancelReason: "Procedure deleted or inactivated",
|
|
405
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await batch.commit();
|
|
410
|
+
console.log(
|
|
411
|
+
`[ProcedureAggregationService] Successfully canceled ${snapshot.size} upcoming calendar events for procedure ${procedureId}.`
|
|
412
|
+
);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error(
|
|
415
|
+
`[ProcedureAggregationService] Error canceling calendar events for procedure ${procedureId}:`,
|
|
416
|
+
error
|
|
417
|
+
);
|
|
418
|
+
throw error;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Removes procedure from a practitioner when a procedure is deleted or inactivated
|
|
424
|
+
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
425
|
+
* @param procedureId - ID of the procedure
|
|
426
|
+
* @param clinicId - Optional clinic ID for free consultation removal
|
|
427
|
+
* @returns {Promise<void>}
|
|
428
|
+
*/
|
|
429
|
+
async removeProcedureFromPractitioner(
|
|
430
|
+
practitionerId: string,
|
|
431
|
+
procedureId: string,
|
|
432
|
+
clinicId?: string
|
|
433
|
+
): Promise<void> {
|
|
434
|
+
if (!practitionerId || !procedureId) {
|
|
435
|
+
console.log(
|
|
436
|
+
"[ProcedureAggregationService] Missing practitionerId or procedureId for removing procedure from practitioner. Skipping."
|
|
437
|
+
);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
console.log(
|
|
442
|
+
`[ProcedureAggregationService] Removing procedure ${procedureId} from practitioner ${practitionerId}.`
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const practitionerRef = this.db
|
|
446
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
447
|
+
.doc(practitionerId);
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
await this.db.runTransaction(async (transaction) => {
|
|
451
|
+
const practitionerDoc = await transaction.get(practitionerRef);
|
|
452
|
+
if (!practitionerDoc.exists) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
`Practitioner ${practitionerId} does not exist for procedure removal`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const practitionerData = practitionerDoc.data();
|
|
459
|
+
if (!practitionerData) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`Practitioner ${practitionerId} data is empty for procedure removal`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Get current procedures info array
|
|
466
|
+
const proceduresInfo = practitionerData.proceduresInfo || [];
|
|
467
|
+
|
|
468
|
+
// Find the procedure being removed to check if it's a free consultation
|
|
469
|
+
const procedureBeingRemoved = proceduresInfo.find(
|
|
470
|
+
(p: any) => p.id === procedureId
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const isFreeConsultation =
|
|
474
|
+
procedureBeingRemoved?.technologyName === "free-consultation-tech";
|
|
475
|
+
const procedureClinicId = clinicId || procedureBeingRemoved?.clinicId;
|
|
476
|
+
|
|
477
|
+
// Remove the procedure summary
|
|
478
|
+
const updatedProceduresInfo = proceduresInfo.filter(
|
|
479
|
+
(p: any) => p.id !== procedureId
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Prepare update data
|
|
483
|
+
const updateData: any = {
|
|
484
|
+
procedureIds: admin.firestore.FieldValue.arrayRemove(procedureId),
|
|
485
|
+
proceduresInfo: updatedProceduresInfo,
|
|
486
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// If this is a free consultation, also remove it from the freeConsultations map
|
|
490
|
+
if (isFreeConsultation && procedureClinicId) {
|
|
491
|
+
const currentFreeConsultations =
|
|
492
|
+
practitionerData.freeConsultations || {};
|
|
493
|
+
|
|
494
|
+
// Check if this clinic's free consultation matches the procedure being removed
|
|
495
|
+
if (currentFreeConsultations[procedureClinicId] === procedureId) {
|
|
496
|
+
// Remove the clinic entry from the freeConsultations map
|
|
497
|
+
const updatedFreeConsultations = { ...currentFreeConsultations };
|
|
498
|
+
delete updatedFreeConsultations[procedureClinicId];
|
|
499
|
+
|
|
500
|
+
updateData.freeConsultations = updatedFreeConsultations;
|
|
501
|
+
|
|
502
|
+
console.log(
|
|
503
|
+
`[ProcedureAggregationService] Removed free consultation ${procedureId} from practitioner ${practitionerId} for clinic ${procedureClinicId}`
|
|
504
|
+
);
|
|
505
|
+
} else {
|
|
506
|
+
console.log(
|
|
507
|
+
`[ProcedureAggregationService] Free consultation mismatch for clinic ${procedureClinicId}: expected ${procedureId}, found ${currentFreeConsultations[procedureClinicId]}`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Update the practitioner document
|
|
513
|
+
transaction.update(practitionerRef, updateData);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
console.log(
|
|
517
|
+
`[ProcedureAggregationService] Successfully removed procedure ${procedureId} from practitioner ${practitionerId}.`
|
|
518
|
+
);
|
|
519
|
+
} catch (error) {
|
|
520
|
+
console.error(
|
|
521
|
+
`[ProcedureAggregationService] Error removing procedure ${procedureId} from practitioner ${practitionerId}:`,
|
|
522
|
+
error
|
|
523
|
+
);
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Removes procedure from a clinic when a procedure is deleted or inactivated
|
|
530
|
+
* @param clinicId - ID of the clinic where the procedure is performed
|
|
531
|
+
* @param procedureId - ID of the procedure
|
|
532
|
+
* @returns {Promise<void>}
|
|
533
|
+
*/
|
|
534
|
+
async removeProcedureFromClinic(
|
|
535
|
+
clinicId: string,
|
|
536
|
+
procedureId: string
|
|
537
|
+
): Promise<void> {
|
|
538
|
+
if (!clinicId || !procedureId) {
|
|
539
|
+
console.log(
|
|
540
|
+
"[ProcedureAggregationService] Missing clinicId or procedureId for removing procedure from clinic. Skipping."
|
|
541
|
+
);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
console.log(
|
|
546
|
+
`[ProcedureAggregationService] Removing procedure ${procedureId} from clinic ${clinicId}.`
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
const clinicRef = this.db.collection(CLINICS_COLLECTION).doc(clinicId);
|
|
550
|
+
|
|
551
|
+
try {
|
|
552
|
+
await this.db.runTransaction(async (transaction) => {
|
|
553
|
+
const clinicDoc = await transaction.get(clinicRef);
|
|
554
|
+
if (!clinicDoc.exists) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
`Clinic ${clinicId} does not exist for procedure removal`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const clinicData = clinicDoc.data();
|
|
561
|
+
if (!clinicData) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`Clinic ${clinicId} data is empty for procedure removal`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Get current procedures info array
|
|
568
|
+
const proceduresInfo = clinicData.proceduresInfo || [];
|
|
569
|
+
|
|
570
|
+
// Remove the procedure summary
|
|
571
|
+
const updatedProceduresInfo = proceduresInfo.filter(
|
|
572
|
+
(p: ProcedureSummaryInfo) => p.id !== procedureId
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
// Update the clinic document
|
|
576
|
+
transaction.update(clinicRef, {
|
|
577
|
+
procedures: admin.firestore.FieldValue.arrayRemove(procedureId),
|
|
578
|
+
proceduresInfo: updatedProceduresInfo,
|
|
579
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
console.log(
|
|
584
|
+
`[ProcedureAggregationService] Successfully removed procedure ${procedureId} from clinic ${clinicId}.`
|
|
585
|
+
);
|
|
586
|
+
} catch (error) {
|
|
587
|
+
console.error(
|
|
588
|
+
`[ProcedureAggregationService] Error removing procedure ${procedureId} from clinic ${clinicId}:`,
|
|
589
|
+
error
|
|
590
|
+
);
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Handles procedure status changes (activation/deactivation) specifically for free consultations
|
|
597
|
+
* @param practitionerId - ID of the practitioner who performs the procedure
|
|
598
|
+
* @param procedureId - ID of the procedure
|
|
599
|
+
* @param clinicId - ID of the clinic where the procedure is performed
|
|
600
|
+
* @param isActive - New active status of the procedure
|
|
601
|
+
* @param technologyName - Technology name of the procedure (to check if it's a free consultation)
|
|
602
|
+
* @returns {Promise<void>}
|
|
603
|
+
*/
|
|
604
|
+
async handleFreeConsultationStatusChange(
|
|
605
|
+
practitionerId: string,
|
|
606
|
+
procedureId: string,
|
|
607
|
+
clinicId: string,
|
|
608
|
+
isActive: boolean,
|
|
609
|
+
technologyName: string
|
|
610
|
+
): Promise<void> {
|
|
611
|
+
if (!practitionerId || !procedureId || !clinicId) {
|
|
612
|
+
console.log(
|
|
613
|
+
"[ProcedureAggregationService] Missing required parameters for handling free consultation status change. Skipping."
|
|
614
|
+
);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Only handle free consultations
|
|
619
|
+
if (technologyName !== "free-consultation-tech") {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
console.log(
|
|
624
|
+
`[ProcedureAggregationService] Handling free consultation status change: procedure ${procedureId} for practitioner ${practitionerId} in clinic ${clinicId}. New status: ${
|
|
625
|
+
isActive ? "active" : "inactive"
|
|
626
|
+
}`
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
const practitionerRef = this.db
|
|
630
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
631
|
+
.doc(practitionerId);
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
await this.db.runTransaction(async (transaction) => {
|
|
635
|
+
const practitionerDoc = await transaction.get(practitionerRef);
|
|
636
|
+
if (!practitionerDoc.exists) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
`Practitioner ${practitionerId} does not exist for free consultation status change`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const practitionerData = practitionerDoc.data();
|
|
643
|
+
if (!practitionerData) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
`Practitioner ${practitionerId} data is empty for free consultation status change`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const currentFreeConsultations =
|
|
650
|
+
practitionerData.freeConsultations || {};
|
|
651
|
+
let updatedFreeConsultations = { ...currentFreeConsultations };
|
|
652
|
+
|
|
653
|
+
if (isActive) {
|
|
654
|
+
// If procedure is being activated, set it as the free consultation for this clinic
|
|
655
|
+
if (
|
|
656
|
+
currentFreeConsultations[clinicId] &&
|
|
657
|
+
currentFreeConsultations[clinicId] !== procedureId
|
|
658
|
+
) {
|
|
659
|
+
console.log(
|
|
660
|
+
`[ProcedureAggregationService] Warning: Clinic ${clinicId} already has a different free consultation ${currentFreeConsultations[clinicId]}. Replacing with ${procedureId}.`
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
updatedFreeConsultations[clinicId] = procedureId;
|
|
665
|
+
|
|
666
|
+
console.log(
|
|
667
|
+
`[ProcedureAggregationService] Set free consultation ${procedureId} for practitioner ${practitionerId} at clinic ${clinicId} (activated)`
|
|
668
|
+
);
|
|
669
|
+
} else {
|
|
670
|
+
// If procedure is being deactivated, remove it from the free consultations map
|
|
671
|
+
if (currentFreeConsultations[clinicId] === procedureId) {
|
|
672
|
+
delete updatedFreeConsultations[clinicId];
|
|
673
|
+
|
|
674
|
+
console.log(
|
|
675
|
+
`[ProcedureAggregationService] Removed free consultation ${procedureId} from practitioner ${practitionerId} for clinic ${clinicId} (deactivated)`
|
|
676
|
+
);
|
|
677
|
+
} else {
|
|
678
|
+
console.log(
|
|
679
|
+
`[ProcedureAggregationService] Free consultation mismatch for clinic ${clinicId}: expected ${procedureId}, found ${currentFreeConsultations[clinicId]}`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Update the practitioner document with the new freeConsultations map
|
|
685
|
+
transaction.update(practitionerRef, {
|
|
686
|
+
freeConsultations: updatedFreeConsultations,
|
|
687
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
console.log(
|
|
692
|
+
`[ProcedureAggregationService] Successfully handled free consultation status change for procedure ${procedureId} of practitioner ${practitionerId}.`
|
|
693
|
+
);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
console.error(
|
|
696
|
+
`[ProcedureAggregationService] Error handling free consultation status change for procedure ${procedureId} of practitioner ${practitionerId}:`,
|
|
697
|
+
error
|
|
698
|
+
);
|
|
699
|
+
throw error;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|