@blackcode_sa/metaestetics-api 1.12.59 → 1.12.62

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.
@@ -70,6 +70,7 @@ interface PractitionerReview extends BaseReview {
70
70
  /**
71
71
  * Procedure review interface
72
72
  * @description Full review for a medical procedure
73
+ * Used for both main and extended procedures
73
74
  */
74
75
  interface ProcedureReview extends BaseReview {
75
76
  procedureId: string;
@@ -138,6 +139,7 @@ interface Review {
138
139
  clinicReview?: ClinicReview;
139
140
  practitionerReview?: PractitionerReview;
140
141
  procedureReview?: ProcedureReview;
142
+ extendedProcedureReviews?: ProcedureReview[];
141
143
  overallComment: string;
142
144
  overallRating: number;
143
145
  }
@@ -2002,6 +2004,13 @@ declare enum PatientRequirementOverallStatus {
2002
2004
  SUPERSEDED_RESCHEDULE = "supersededReschedule",// This instance was replaced by a new one due to appointment reschedule.
2003
2005
  FAILED_TO_PROCESS = "failedToProcess"
2004
2006
  }
2007
+ /**
2008
+ * Represents source procedure information for a requirement instance
2009
+ */
2010
+ interface RequirementSourceProcedure {
2011
+ procedureId: string;
2012
+ procedureName: string;
2013
+ }
2005
2014
  /**
2006
2015
  * Represents an instance of a backoffice Requirement, tailored to a specific patient and appointment.
2007
2016
  * This document lives in the patient's subcollection: `patients/{patientId}/patientRequirements/{instanceId}`.
@@ -2017,6 +2026,7 @@ interface PatientRequirementInstance {
2017
2026
  requirementImportance: RequirementImportance;
2018
2027
  overallStatus: PatientRequirementOverallStatus;
2019
2028
  instructions: PatientRequirementInstruction[];
2029
+ sourceProcedures?: RequirementSourceProcedure[];
2020
2030
  createdAt: Timestamp;
2021
2031
  updatedAt: Timestamp;
2022
2032
  }
@@ -2548,11 +2558,35 @@ declare class AppointmentAggregationService {
2548
2558
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
2549
2559
  */
2550
2560
  private createPreAppointmentRequirementInstances;
2561
+ /**
2562
+ * Fetches post-requirements from a procedure document
2563
+ * @param procedureId - The procedure ID to fetch requirements from
2564
+ * @returns Promise resolving to array of post-requirements with source procedure info
2565
+ */
2566
+ private fetchPostRequirementsFromProcedure;
2567
+ /**
2568
+ * Collects all post-requirements from primary and extended procedures
2569
+ * @param appointment - The appointment to collect requirements for
2570
+ * @returns Promise resolving to array of requirements with source procedures
2571
+ */
2572
+ private collectAllPostRequirements;
2573
+ /**
2574
+ * Generates a unique key for a requirement based on ID and timeframe
2575
+ * @param requirement - The requirement to generate a key for
2576
+ * @returns Unique key string
2577
+ */
2578
+ private getRequirementKey;
2579
+ /**
2580
+ * Deduplicates requirements based on requirement ID and timeframe
2581
+ * Merges source procedures when requirements match
2582
+ * @param requirements - Array of requirements with sources
2583
+ * @returns Deduplicated array of requirements
2584
+ */
2585
+ private deduplicateRequirements;
2551
2586
  /**
2552
2587
  * Creates POST_APPOINTMENT PatientRequirementInstance documents for a given appointment.
2553
- * Uses the `appointment.postProcedureRequirements` array.
2554
- * For each active POST requirement template, it constructs a new PatientRequirementInstance document
2555
- * with derived instructions and batch writes them to Firestore under the patient's `patient_requirements` subcollection.
2588
+ * Fetches requirements from primary and extended procedures, deduplicates them,
2589
+ * and creates requirement instances with source procedure tracking.
2556
2590
  *
2557
2591
  * @param {Appointment} appointment - The appointment for which to create post-requirement instances.
2558
2592
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
@@ -3728,4 +3762,4 @@ declare class UserProfileAdminService {
3728
3762
  initializePractitionerRole(userId: string): Promise<User>;
3729
3763
  }
3730
3764
 
3731
- export { type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, type BillingInfo, type BillingTransaction, BillingTransactionType, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, type ClinicAdminNotificationData, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateBillingTransactionData, type DoctorInfo, DocumentManagerAdminService, type ExistingPractitionerInviteEmailData, ExistingPractitionerInviteMailingService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, Logger, NOTIFICATIONS_COLLECTION, type NewMailgunClient$2 as NewMailgunClient, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENTS_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientProfile, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientSensitiveInfo, type PlanDetails, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, type PractitionerInvite, PractitionerInviteAggregationService, type PractitionerInviteEmailData, PractitionerInviteMailingService, PractitionerInviteStatus, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type StripeTransactionData, SubscriptionStatus, type TimeInterval, UserProfileAdminService, UserRole, freeConsultationInfrastructure };
3765
+ export { type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, type BillingInfo, type BillingTransaction, BillingTransactionType, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, type ClinicAdminNotificationData, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateBillingTransactionData, type DoctorInfo, DocumentManagerAdminService, type ExistingPractitionerInviteEmailData, ExistingPractitionerInviteMailingService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, Logger, NOTIFICATIONS_COLLECTION, type NewMailgunClient$2 as NewMailgunClient, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENTS_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientProfile, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientSensitiveInfo, type PlanDetails, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, type PractitionerInvite, PractitionerInviteAggregationService, type PractitionerInviteEmailData, PractitionerInviteMailingService, PractitionerInviteStatus, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureSummaryInfo, type RequirementSourceProcedure, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type StripeTransactionData, SubscriptionStatus, type TimeInterval, UserProfileAdminService, UserRole, freeConsultationInfrastructure };
@@ -70,6 +70,7 @@ interface PractitionerReview extends BaseReview {
70
70
  /**
71
71
  * Procedure review interface
72
72
  * @description Full review for a medical procedure
73
+ * Used for both main and extended procedures
73
74
  */
74
75
  interface ProcedureReview extends BaseReview {
75
76
  procedureId: string;
@@ -138,6 +139,7 @@ interface Review {
138
139
  clinicReview?: ClinicReview;
139
140
  practitionerReview?: PractitionerReview;
140
141
  procedureReview?: ProcedureReview;
142
+ extendedProcedureReviews?: ProcedureReview[];
141
143
  overallComment: string;
142
144
  overallRating: number;
143
145
  }
@@ -2002,6 +2004,13 @@ declare enum PatientRequirementOverallStatus {
2002
2004
  SUPERSEDED_RESCHEDULE = "supersededReschedule",// This instance was replaced by a new one due to appointment reschedule.
2003
2005
  FAILED_TO_PROCESS = "failedToProcess"
2004
2006
  }
2007
+ /**
2008
+ * Represents source procedure information for a requirement instance
2009
+ */
2010
+ interface RequirementSourceProcedure {
2011
+ procedureId: string;
2012
+ procedureName: string;
2013
+ }
2005
2014
  /**
2006
2015
  * Represents an instance of a backoffice Requirement, tailored to a specific patient and appointment.
2007
2016
  * This document lives in the patient's subcollection: `patients/{patientId}/patientRequirements/{instanceId}`.
@@ -2017,6 +2026,7 @@ interface PatientRequirementInstance {
2017
2026
  requirementImportance: RequirementImportance;
2018
2027
  overallStatus: PatientRequirementOverallStatus;
2019
2028
  instructions: PatientRequirementInstruction[];
2029
+ sourceProcedures?: RequirementSourceProcedure[];
2020
2030
  createdAt: Timestamp;
2021
2031
  updatedAt: Timestamp;
2022
2032
  }
@@ -2548,11 +2558,35 @@ declare class AppointmentAggregationService {
2548
2558
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
2549
2559
  */
2550
2560
  private createPreAppointmentRequirementInstances;
2561
+ /**
2562
+ * Fetches post-requirements from a procedure document
2563
+ * @param procedureId - The procedure ID to fetch requirements from
2564
+ * @returns Promise resolving to array of post-requirements with source procedure info
2565
+ */
2566
+ private fetchPostRequirementsFromProcedure;
2567
+ /**
2568
+ * Collects all post-requirements from primary and extended procedures
2569
+ * @param appointment - The appointment to collect requirements for
2570
+ * @returns Promise resolving to array of requirements with source procedures
2571
+ */
2572
+ private collectAllPostRequirements;
2573
+ /**
2574
+ * Generates a unique key for a requirement based on ID and timeframe
2575
+ * @param requirement - The requirement to generate a key for
2576
+ * @returns Unique key string
2577
+ */
2578
+ private getRequirementKey;
2579
+ /**
2580
+ * Deduplicates requirements based on requirement ID and timeframe
2581
+ * Merges source procedures when requirements match
2582
+ * @param requirements - Array of requirements with sources
2583
+ * @returns Deduplicated array of requirements
2584
+ */
2585
+ private deduplicateRequirements;
2551
2586
  /**
2552
2587
  * Creates POST_APPOINTMENT PatientRequirementInstance documents for a given appointment.
2553
- * Uses the `appointment.postProcedureRequirements` array.
2554
- * For each active POST requirement template, it constructs a new PatientRequirementInstance document
2555
- * with derived instructions and batch writes them to Firestore under the patient's `patient_requirements` subcollection.
2588
+ * Fetches requirements from primary and extended procedures, deduplicates them,
2589
+ * and creates requirement instances with source procedure tracking.
2556
2590
  *
2557
2591
  * @param {Appointment} appointment - The appointment for which to create post-requirement instances.
2558
2592
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
@@ -3728,4 +3762,4 @@ declare class UserProfileAdminService {
3728
3762
  initializePractitionerRole(userId: string): Promise<User>;
3729
3763
  }
3730
3764
 
3731
- export { type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, type BillingInfo, type BillingTransaction, BillingTransactionType, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, type ClinicAdminNotificationData, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateBillingTransactionData, type DoctorInfo, DocumentManagerAdminService, type ExistingPractitionerInviteEmailData, ExistingPractitionerInviteMailingService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, Logger, NOTIFICATIONS_COLLECTION, type NewMailgunClient$2 as NewMailgunClient, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENTS_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientProfile, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientSensitiveInfo, type PlanDetails, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, type PractitionerInvite, PractitionerInviteAggregationService, type PractitionerInviteEmailData, PractitionerInviteMailingService, PractitionerInviteStatus, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type StripeTransactionData, SubscriptionStatus, type TimeInterval, UserProfileAdminService, UserRole, freeConsultationInfrastructure };
3765
+ export { type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, type BillingInfo, type BillingTransaction, BillingTransactionType, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, type ClinicAdminNotificationData, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateBillingTransactionData, type DoctorInfo, DocumentManagerAdminService, type ExistingPractitionerInviteEmailData, ExistingPractitionerInviteMailingService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, Logger, NOTIFICATIONS_COLLECTION, type NewMailgunClient$2 as NewMailgunClient, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENTS_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientProfile, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientSensitiveInfo, type PlanDetails, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, type PractitionerInvite, PractitionerInviteAggregationService, type PractitionerInviteEmailData, PractitionerInviteMailingService, PractitionerInviteStatus, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureSummaryInfo, type RequirementSourceProcedure, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type StripeTransactionData, SubscriptionStatus, type TimeInterval, UserProfileAdminService, UserRole, freeConsultationInfrastructure };
@@ -569,6 +569,9 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
569
569
  // src/admin/aggregation/appointment/appointment.aggregation.service.ts
570
570
  var admin6 = __toESM(require("firebase-admin"));
571
571
 
572
+ // src/types/procedure/index.ts
573
+ var PROCEDURES_COLLECTION = "procedures";
574
+
572
575
  // src/admin/requirements/patient-requirements.admin.service.ts
573
576
  var admin3 = __toESM(require("firebase-admin"));
574
577
 
@@ -580,9 +583,6 @@ var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
580
583
  var USER_FORMS_SUBCOLLECTION = "user-forms";
581
584
  var DOCTOR_FORMS_SUBCOLLECTION = "doctor-forms";
582
585
 
583
- // src/types/procedure/index.ts
584
- var PROCEDURES_COLLECTION = "procedures";
585
-
586
586
  // src/types/reviews/index.ts
587
587
  var REVIEWS_COLLECTION = "reviews";
588
588
 
@@ -3193,17 +3193,122 @@ var AppointmentAggregationService = class {
3193
3193
  throw error;
3194
3194
  }
3195
3195
  }
3196
+ /**
3197
+ * Fetches post-requirements from a procedure document
3198
+ * @param procedureId - The procedure ID to fetch requirements from
3199
+ * @returns Promise resolving to array of post-requirements with source procedure info
3200
+ */
3201
+ async fetchPostRequirementsFromProcedure(procedureId) {
3202
+ try {
3203
+ const procedureDoc = await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).get();
3204
+ if (!procedureDoc.exists) {
3205
+ Logger.warn(`[AggService] Procedure ${procedureId} not found when fetching requirements`);
3206
+ return [];
3207
+ }
3208
+ const procedure = procedureDoc.data();
3209
+ const postRequirements = procedure.postRequirements || [];
3210
+ if (postRequirements.length === 0) {
3211
+ return [];
3212
+ }
3213
+ return postRequirements.map((req) => ({
3214
+ requirement: req,
3215
+ sourceProcedures: [
3216
+ {
3217
+ procedureId: procedure.id,
3218
+ procedureName: procedure.name
3219
+ }
3220
+ ]
3221
+ }));
3222
+ } catch (error) {
3223
+ Logger.error(
3224
+ `[AggService] Error fetching post-requirements from procedure ${procedureId}:`,
3225
+ error
3226
+ );
3227
+ return [];
3228
+ }
3229
+ }
3230
+ /**
3231
+ * Collects all post-requirements from primary and extended procedures
3232
+ * @param appointment - The appointment to collect requirements for
3233
+ * @returns Promise resolving to array of requirements with source procedures
3234
+ */
3235
+ async collectAllPostRequirements(appointment) {
3236
+ var _a;
3237
+ const allRequirements = [];
3238
+ if (appointment.procedureId) {
3239
+ const primaryRequirements = await this.fetchPostRequirementsFromProcedure(
3240
+ appointment.procedureId
3241
+ );
3242
+ allRequirements.push(...primaryRequirements);
3243
+ }
3244
+ const extendedProcedures = ((_a = appointment.metadata) == null ? void 0 : _a.extendedProcedures) || [];
3245
+ if (extendedProcedures.length > 0) {
3246
+ Logger.info(
3247
+ `[AggService] Fetching post-requirements from ${extendedProcedures.length} extended procedures`
3248
+ );
3249
+ const extendedRequirementsPromises = extendedProcedures.map(
3250
+ (extProc) => this.fetchPostRequirementsFromProcedure(extProc.procedureId)
3251
+ );
3252
+ const extendedRequirementsArrays = await Promise.all(extendedRequirementsPromises);
3253
+ extendedRequirementsArrays.forEach((reqs) => {
3254
+ allRequirements.push(...reqs);
3255
+ });
3256
+ }
3257
+ return allRequirements;
3258
+ }
3259
+ /**
3260
+ * Generates a unique key for a requirement based on ID and timeframe
3261
+ * @param requirement - The requirement to generate a key for
3262
+ * @returns Unique key string
3263
+ */
3264
+ getRequirementKey(requirement) {
3265
+ var _a, _b, _c;
3266
+ const timeframeSig = JSON.stringify({
3267
+ duration: ((_a = requirement.timeframe) == null ? void 0 : _a.duration) || 0,
3268
+ unit: ((_b = requirement.timeframe) == null ? void 0 : _b.unit) || "",
3269
+ notifyAt: (((_c = requirement.timeframe) == null ? void 0 : _c.notifyAt) || []).slice().sort((a, b) => a - b)
3270
+ });
3271
+ return `${requirement.id}:${timeframeSig}`;
3272
+ }
3273
+ /**
3274
+ * Deduplicates requirements based on requirement ID and timeframe
3275
+ * Merges source procedures when requirements match
3276
+ * @param requirements - Array of requirements with sources
3277
+ * @returns Deduplicated array of requirements
3278
+ */
3279
+ deduplicateRequirements(requirements) {
3280
+ const requirementMap = /* @__PURE__ */ new Map();
3281
+ for (const reqWithSource of requirements) {
3282
+ const key = this.getRequirementKey(reqWithSource.requirement);
3283
+ if (requirementMap.has(key)) {
3284
+ const existing = requirementMap.get(key);
3285
+ const existingProcedureIds = new Set(
3286
+ existing.sourceProcedures.map((sp) => sp.procedureId)
3287
+ );
3288
+ reqWithSource.sourceProcedures.forEach((sp) => {
3289
+ if (!existingProcedureIds.has(sp.procedureId)) {
3290
+ existing.sourceProcedures.push(sp);
3291
+ }
3292
+ });
3293
+ } else {
3294
+ requirementMap.set(key, {
3295
+ requirement: reqWithSource.requirement,
3296
+ sourceProcedures: [...reqWithSource.sourceProcedures]
3297
+ });
3298
+ }
3299
+ }
3300
+ return Array.from(requirementMap.values());
3301
+ }
3196
3302
  /**
3197
3303
  * Creates POST_APPOINTMENT PatientRequirementInstance documents for a given appointment.
3198
- * Uses the `appointment.postProcedureRequirements` array.
3199
- * For each active POST requirement template, it constructs a new PatientRequirementInstance document
3200
- * with derived instructions and batch writes them to Firestore under the patient's `patient_requirements` subcollection.
3304
+ * Fetches requirements from primary and extended procedures, deduplicates them,
3305
+ * and creates requirement instances with source procedure tracking.
3201
3306
  *
3202
3307
  * @param {Appointment} appointment - The appointment for which to create post-requirement instances.
3203
3308
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
3204
3309
  */
3205
3310
  async createPostAppointmentRequirementInstances(appointment) {
3206
- var _a;
3311
+ var _a, _b;
3207
3312
  Logger.info(
3208
3313
  `[AggService] Creating POST-appointment requirement instances for appt: ${appointment.id}, patient: ${appointment.patientId}`
3209
3314
  );
@@ -3213,32 +3318,42 @@ var AppointmentAggregationService = class {
3213
3318
  );
3214
3319
  return;
3215
3320
  }
3216
- if (!appointment.postProcedureRequirements || appointment.postProcedureRequirements.length === 0) {
3321
+ try {
3322
+ const allRequirements = await this.collectAllPostRequirements(appointment);
3323
+ if (allRequirements.length === 0) {
3324
+ Logger.info(
3325
+ `[AggService] No post-requirements found from any procedures for appointment ${appointment.id}. Nothing to create.`
3326
+ );
3327
+ return;
3328
+ }
3329
+ const deduplicatedRequirements = this.deduplicateRequirements(allRequirements);
3217
3330
  Logger.info(
3218
- `[AggService] No postProcedureRequirements found on appointment ${appointment.id}. Nothing to create.`
3331
+ `[AggService] Found ${allRequirements.length} total post-requirements, ${deduplicatedRequirements.length} after deduplication`
3219
3332
  );
3220
- return;
3221
- }
3222
- try {
3223
- const batch = this.db.batch();
3224
- let instancesCreatedCount = 0;
3225
- let createdInstances = [];
3226
3333
  Logger.info(
3227
- `[AggService] Found ${appointment.postProcedureRequirements.length} post-requirements to process: ${JSON.stringify(
3228
- appointment.postProcedureRequirements.map((r) => {
3229
- var _a2, _b;
3334
+ `[AggService] Processing deduplicated post-requirements: ${JSON.stringify(
3335
+ deduplicatedRequirements.map((r) => {
3336
+ var _a2, _b2;
3230
3337
  return {
3231
- id: r.id,
3232
- name: r.name,
3233
- type: r.type,
3234
- isActive: r.isActive,
3235
- hasTimeframe: !!r.timeframe,
3236
- notifyAtLength: ((_b = (_a2 = r.timeframe) == null ? void 0 : _a2.notifyAt) == null ? void 0 : _b.length) || 0
3338
+ id: r.requirement.id,
3339
+ name: r.requirement.name,
3340
+ type: r.requirement.type,
3341
+ isActive: r.requirement.isActive,
3342
+ hasTimeframe: !!r.requirement.timeframe,
3343
+ notifyAtLength: ((_b2 = (_a2 = r.requirement.timeframe) == null ? void 0 : _a2.notifyAt) == null ? void 0 : _b2.length) || 0,
3344
+ sourceProcedures: r.sourceProcedures.map((sp) => ({
3345
+ procedureId: sp.procedureId,
3346
+ procedureName: sp.procedureName
3347
+ }))
3237
3348
  };
3238
3349
  })
3239
3350
  )}`
3240
3351
  );
3241
- for (const template of appointment.postProcedureRequirements) {
3352
+ const batch = this.db.batch();
3353
+ let instancesCreatedCount = 0;
3354
+ let createdInstances = [];
3355
+ for (const reqWithSource of deduplicatedRequirements) {
3356
+ const template = reqWithSource.requirement;
3242
3357
  if (!template) {
3243
3358
  Logger.warn(
3244
3359
  `[AggService] Found null/undefined template in postProcedureRequirements array`
@@ -3301,6 +3416,8 @@ var AppointmentAggregationService = class {
3301
3416
  requirementImportance: template.importance,
3302
3417
  overallStatus: "active" /* ACTIVE */,
3303
3418
  instructions,
3419
+ sourceProcedures: reqWithSource.sourceProcedures,
3420
+ // Track which procedures this requirement comes from
3304
3421
  createdAt: admin6.firestore.FieldValue.serverTimestamp(),
3305
3422
  updatedAt: admin6.firestore.FieldValue.serverTimestamp()
3306
3423
  };
@@ -3310,7 +3427,11 @@ var AppointmentAggregationService = class {
3310
3427
  patientId: newInstanceData.patientId,
3311
3428
  appointmentId: newInstanceData.appointmentId,
3312
3429
  requirementName: newInstanceData.requirementName,
3313
- instructionsCount: newInstanceData.instructions.length
3430
+ instructionsCount: newInstanceData.instructions.length,
3431
+ sourceProcedures: ((_b = newInstanceData.sourceProcedures) == null ? void 0 : _b.map((sp) => ({
3432
+ procedureId: sp.procedureId,
3433
+ procedureName: sp.procedureName
3434
+ }))) || []
3314
3435
  })}`
3315
3436
  );
3316
3437
  batch.set(newInstanceRef, newInstanceData);
@@ -6254,6 +6375,16 @@ var ReviewsAggregationService = class {
6254
6375
  this.updateProcedureReviewInfo(review.procedureReview.procedureId)
6255
6376
  );
6256
6377
  }
6378
+ if (review.extendedProcedureReviews && review.extendedProcedureReviews.length > 0) {
6379
+ console.log(
6380
+ `[ReviewsAggregationService] Processing ${review.extendedProcedureReviews.length} extended procedure reviews`
6381
+ );
6382
+ review.extendedProcedureReviews.forEach((extendedReview) => {
6383
+ updatePromises.push(
6384
+ this.updateProcedureReviewInfo(extendedReview.procedureId)
6385
+ );
6386
+ });
6387
+ }
6257
6388
  await Promise.all(updatePromises);
6258
6389
  console.log(
6259
6390
  `[ReviewsAggregationService] Successfully processed review: ${review.id}`
@@ -6296,6 +6427,20 @@ var ReviewsAggregationService = class {
6296
6427
  )
6297
6428
  );
6298
6429
  }
6430
+ if (review.extendedProcedureReviews && review.extendedProcedureReviews.length > 0) {
6431
+ console.log(
6432
+ `[ReviewsAggregationService] Processing deletion of ${review.extendedProcedureReviews.length} extended procedure reviews`
6433
+ );
6434
+ review.extendedProcedureReviews.forEach((extendedReview) => {
6435
+ updatePromises.push(
6436
+ this.updateProcedureReviewInfo(
6437
+ extendedReview.procedureId,
6438
+ extendedReview,
6439
+ true
6440
+ )
6441
+ );
6442
+ });
6443
+ }
6299
6444
  await Promise.all(updatePromises);
6300
6445
  console.log(
6301
6446
  `[ReviewsAggregationService] Successfully processed deleted review: ${review.id}`
@@ -6513,8 +6658,21 @@ var ReviewsAggregationService = class {
6513
6658
  valueForMoney: 0,
6514
6659
  recommendationPercentage: 0
6515
6660
  };
6516
- const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("procedureReview.procedureId", "==", procedureId).get();
6517
- if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
6661
+ const allReviewsQuery = await this.db.collection(REVIEWS_COLLECTION).get();
6662
+ const reviews = allReviewsQuery.docs.map((doc) => doc.data());
6663
+ const procedureReviews = [];
6664
+ reviews.forEach((review) => {
6665
+ if (review.procedureReview && review.procedureReview.procedureId === procedureId) {
6666
+ procedureReviews.push(review.procedureReview);
6667
+ }
6668
+ if (review.extendedProcedureReviews && review.extendedProcedureReviews.length > 0) {
6669
+ const matchingExtended = review.extendedProcedureReviews.filter(
6670
+ (extReview) => extReview.procedureId === procedureId
6671
+ );
6672
+ procedureReviews.push(...matchingExtended);
6673
+ }
6674
+ });
6675
+ if (procedureReviews.length === 0) {
6518
6676
  const updatedReviewInfo2 = {
6519
6677
  totalReviews: 0,
6520
6678
  averageRating: 0,
@@ -6534,8 +6692,6 @@ var ReviewsAggregationService = class {
6534
6692
  );
6535
6693
  return updatedReviewInfo2;
6536
6694
  }
6537
- const reviews = reviewsQuery.docs.map((doc) => doc.data());
6538
- const procedureReviews = reviews.map((review) => review.procedureReview).filter((review) => review !== void 0);
6539
6695
  let totalRating = 0;
6540
6696
  let totalEffectivenessOfTreatment = 0;
6541
6697
  let totalOutcomeExplanation = 0;
@@ -6629,10 +6785,16 @@ var ReviewsAggregationService = class {
6629
6785
  if (review.procedureReview) {
6630
6786
  review.procedureReview.isVerified = true;
6631
6787
  }
6788
+ if (review.extendedProcedureReviews && review.extendedProcedureReviews.length > 0) {
6789
+ review.extendedProcedureReviews.forEach((extReview) => {
6790
+ extReview.isVerified = true;
6791
+ });
6792
+ }
6632
6793
  batch.update(reviewRef, {
6633
6794
  clinicReview: review.clinicReview,
6634
6795
  practitionerReview: review.practitionerReview,
6635
6796
  procedureReview: review.procedureReview,
6797
+ extendedProcedureReviews: review.extendedProcedureReviews,
6636
6798
  updatedAt: admin13.firestore.FieldValue.serverTimestamp()
6637
6799
  });
6638
6800
  await batch.commit();