@blackcode_sa/metaestetics-api 1.13.4 → 1.13.6
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 +35 -29
- package/dist/admin/index.d.ts +35 -29
- package/dist/admin/index.js +217 -1
- package/dist/admin/index.mjs +217 -1
- package/dist/index.d.mts +16 -29
- package/dist/index.d.ts +16 -29
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +3 -1
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +265 -2
- package/src/types/clinic/index.ts +16 -7
- package/src/validations/clinic.schema.ts +1 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
Practitioner,
|
|
6
6
|
} from "../../../types/practitioner"; // Corrected path
|
|
7
7
|
import { PROCEDURES_COLLECTION } from "../../../types/procedure"; // Added procedure collection
|
|
8
|
-
import { CLINIC_GROUPS_COLLECTION, ClinicGroup } from "../../../types/clinic"; // Added clinic group collection
|
|
8
|
+
import { CLINIC_GROUPS_COLLECTION, ClinicGroup, CLINICS_COLLECTION } from "../../../types/clinic"; // Added clinic group collection
|
|
9
9
|
import { ClinicLocation } from "../../../types/clinic"; // Added ClinicLocation type
|
|
10
10
|
import { CALENDAR_COLLECTION } from "../../../types/calendar"; // Added calendar collection
|
|
11
11
|
import { PATIENTS_COLLECTION } from "../../../types/patient"; // Added patient collection
|
|
@@ -430,10 +430,12 @@ export class ClinicAggregationService {
|
|
|
430
430
|
* Removes clinic from practitioners when a clinic is deleted.
|
|
431
431
|
* @param practitionerIds IDs of practitioners associated with the clinic.
|
|
432
432
|
* @param clinicId The ID of the deleted clinic.
|
|
433
|
+
* @param checkAndDeactivateDoctors If true, checks if doctors have remaining active clinics and deactivates them if not.
|
|
433
434
|
*/
|
|
434
435
|
async removeClinicFromPractitioners(
|
|
435
436
|
practitionerIds: string[],
|
|
436
|
-
clinicId: string
|
|
437
|
+
clinicId: string,
|
|
438
|
+
checkAndDeactivateDoctors: boolean = false
|
|
437
439
|
): Promise<void> {
|
|
438
440
|
if (!practitionerIds || practitionerIds.length === 0) {
|
|
439
441
|
console.log(
|
|
@@ -443,6 +445,7 @@ export class ClinicAggregationService {
|
|
|
443
445
|
}
|
|
444
446
|
|
|
445
447
|
const batch = this.db.batch();
|
|
448
|
+
const practitionersToCheck: string[] = [];
|
|
446
449
|
|
|
447
450
|
console.log(
|
|
448
451
|
`[ClinicAggregationService] Starting batch removal of Clinic (ID: ${clinicId}) from ${practitionerIds.length} practitioners.`
|
|
@@ -474,6 +477,11 @@ export class ClinicAggregationService {
|
|
|
474
477
|
clinicsInfo: filteredClinicsInfo,
|
|
475
478
|
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
476
479
|
});
|
|
480
|
+
|
|
481
|
+
// If we need to check for remaining active clinics, collect practitioner IDs
|
|
482
|
+
if (checkAndDeactivateDoctors) {
|
|
483
|
+
practitionersToCheck.push(practitionerId);
|
|
484
|
+
}
|
|
477
485
|
}
|
|
478
486
|
}
|
|
479
487
|
|
|
@@ -482,6 +490,13 @@ export class ClinicAggregationService {
|
|
|
482
490
|
console.log(
|
|
483
491
|
`[ClinicAggregationService] Successfully removed Clinic (ID: ${clinicId}) from ${practitionerIds.length} practitioners.`
|
|
484
492
|
);
|
|
493
|
+
|
|
494
|
+
// Check and deactivate doctors with no remaining active clinics
|
|
495
|
+
if (checkAndDeactivateDoctors && practitionersToCheck.length > 0) {
|
|
496
|
+
await this.checkAndDeactivateDoctorsWithNoActiveClinics(
|
|
497
|
+
practitionersToCheck
|
|
498
|
+
);
|
|
499
|
+
}
|
|
485
500
|
} catch (error) {
|
|
486
501
|
console.error(
|
|
487
502
|
`[ClinicAggregationService] Error committing batch removal for Clinic (ID: ${clinicId}) from practitioners:`,
|
|
@@ -491,6 +506,194 @@ export class ClinicAggregationService {
|
|
|
491
506
|
}
|
|
492
507
|
}
|
|
493
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Checks if practitioners have any remaining active clinics and deactivates them if not.
|
|
511
|
+
* @param practitionerIds IDs of practitioners to check
|
|
512
|
+
*/
|
|
513
|
+
async checkAndDeactivateDoctorsWithNoActiveClinics(
|
|
514
|
+
practitionerIds: string[]
|
|
515
|
+
): Promise<void> {
|
|
516
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
console.log(
|
|
521
|
+
`[ClinicAggregationService] Checking ${practitionerIds.length} practitioners for remaining active clinics.`
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
const batch = this.db.batch();
|
|
525
|
+
let deactivationCount = 0;
|
|
526
|
+
|
|
527
|
+
for (const practitionerId of practitionerIds) {
|
|
528
|
+
const practitionerRef = this.db
|
|
529
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
530
|
+
.doc(practitionerId);
|
|
531
|
+
const practitionerDoc = await practitionerRef.get();
|
|
532
|
+
|
|
533
|
+
if (!practitionerDoc.exists) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const practitionerData = practitionerDoc.data() as Practitioner;
|
|
538
|
+
const clinicIds = practitionerData?.clinics || [];
|
|
539
|
+
|
|
540
|
+
if (clinicIds.length === 0) {
|
|
541
|
+
// No clinics remaining, deactivate the doctor
|
|
542
|
+
console.log(
|
|
543
|
+
`[ClinicAggregationService] Practitioner ${practitionerId} has no remaining clinics. Deactivating.`
|
|
544
|
+
);
|
|
545
|
+
batch.update(practitionerRef, {
|
|
546
|
+
isActive: false,
|
|
547
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
548
|
+
});
|
|
549
|
+
deactivationCount++;
|
|
550
|
+
} else {
|
|
551
|
+
// Check if any of the remaining clinics are active
|
|
552
|
+
const clinicRefs = clinicIds.map((id: string) =>
|
|
553
|
+
this.db.collection(CLINICS_COLLECTION).doc(id)
|
|
554
|
+
);
|
|
555
|
+
const clinicDocs = await Promise.all(
|
|
556
|
+
clinicRefs.map((ref) => ref.get())
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
const hasActiveClinic = clinicDocs.some(
|
|
560
|
+
(doc) => doc.exists && (doc.data() as any)?.isActive === true
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
if (!hasActiveClinic) {
|
|
564
|
+
// No active clinics remaining, deactivate the doctor
|
|
565
|
+
console.log(
|
|
566
|
+
`[ClinicAggregationService] Practitioner ${practitionerId} has no remaining active clinics. Deactivating.`
|
|
567
|
+
);
|
|
568
|
+
batch.update(practitionerRef, {
|
|
569
|
+
isActive: false,
|
|
570
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
571
|
+
});
|
|
572
|
+
deactivationCount++;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (deactivationCount > 0) {
|
|
578
|
+
try {
|
|
579
|
+
await batch.commit();
|
|
580
|
+
console.log(
|
|
581
|
+
`[ClinicAggregationService] Successfully deactivated ${deactivationCount} practitioners with no remaining active clinics.`
|
|
582
|
+
);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
console.error(
|
|
585
|
+
`[ClinicAggregationService] Error deactivating practitioners:`,
|
|
586
|
+
error
|
|
587
|
+
);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
} else {
|
|
591
|
+
console.log(
|
|
592
|
+
`[ClinicAggregationService] All practitioners have remaining active clinics. No deactivations needed.`
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Checks if practitioners were deactivated because they had no active clinics,
|
|
599
|
+
* and reactivates them if this clinic was their only clinic.
|
|
600
|
+
* @param practitionerIds IDs of practitioners associated with the reactivated clinic
|
|
601
|
+
* @param clinicId ID of the reactivated clinic
|
|
602
|
+
*/
|
|
603
|
+
async checkAndReactivateDoctorsForClinic(
|
|
604
|
+
practitionerIds: string[],
|
|
605
|
+
clinicId: string
|
|
606
|
+
): Promise<void> {
|
|
607
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
console.log(
|
|
612
|
+
`[ClinicAggregationService] Checking ${practitionerIds.length} practitioners for reactivation after clinic ${clinicId} was reactivated.`
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
const batch = this.db.batch();
|
|
616
|
+
let reactivationCount = 0;
|
|
617
|
+
|
|
618
|
+
for (const practitionerId of practitionerIds) {
|
|
619
|
+
const practitionerRef = this.db
|
|
620
|
+
.collection(PRACTITIONERS_COLLECTION)
|
|
621
|
+
.doc(practitionerId);
|
|
622
|
+
const practitionerDoc = await practitionerRef.get();
|
|
623
|
+
|
|
624
|
+
if (!practitionerDoc.exists) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const practitionerData = practitionerDoc.data() as Practitioner;
|
|
629
|
+
|
|
630
|
+
// Check if practitioner is inactive
|
|
631
|
+
if (practitionerData?.isActive === false) {
|
|
632
|
+
// Check if this clinic is in their clinics list (meaning it was their clinic)
|
|
633
|
+
const clinicIds = practitionerData?.clinics || [];
|
|
634
|
+
if (clinicIds.includes(clinicId)) {
|
|
635
|
+
// Check if they have any other active clinics
|
|
636
|
+
const otherClinicIds = clinicIds.filter((id: string) => id !== clinicId);
|
|
637
|
+
|
|
638
|
+
if (otherClinicIds.length === 0) {
|
|
639
|
+
// This was their only clinic, reactivate them
|
|
640
|
+
console.log(
|
|
641
|
+
`[ClinicAggregationService] Practitioner ${practitionerId} was deactivated because clinic ${clinicId} was their only clinic. Reactivating.`
|
|
642
|
+
);
|
|
643
|
+
batch.update(practitionerRef, {
|
|
644
|
+
isActive: true,
|
|
645
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
646
|
+
});
|
|
647
|
+
reactivationCount++;
|
|
648
|
+
} else {
|
|
649
|
+
// Check if any other clinics are active
|
|
650
|
+
const otherClinicRefs = otherClinicIds.map((id: string) =>
|
|
651
|
+
this.db.collection(CLINICS_COLLECTION).doc(id)
|
|
652
|
+
);
|
|
653
|
+
const otherClinicDocs = await Promise.all(
|
|
654
|
+
otherClinicRefs.map((ref) => ref.get())
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
const hasOtherActiveClinic = otherClinicDocs.some(
|
|
658
|
+
(doc) => doc.exists && (doc.data() as any)?.isActive === true
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
if (!hasOtherActiveClinic) {
|
|
662
|
+
// All their other clinics are also inactive, so this clinic reactivation should reactivate them
|
|
663
|
+
console.log(
|
|
664
|
+
`[ClinicAggregationService] Practitioner ${practitionerId} has no other active clinics. Reactivating because clinic ${clinicId} was reactivated.`
|
|
665
|
+
);
|
|
666
|
+
batch.update(practitionerRef, {
|
|
667
|
+
isActive: true,
|
|
668
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
669
|
+
});
|
|
670
|
+
reactivationCount++;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (reactivationCount > 0) {
|
|
678
|
+
try {
|
|
679
|
+
await batch.commit();
|
|
680
|
+
console.log(
|
|
681
|
+
`[ClinicAggregationService] Successfully reactivated ${reactivationCount} practitioners.`
|
|
682
|
+
);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
console.error(
|
|
685
|
+
`[ClinicAggregationService] Error reactivating practitioners:`,
|
|
686
|
+
error
|
|
687
|
+
);
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
} else {
|
|
691
|
+
console.log(
|
|
692
|
+
`[ClinicAggregationService] No practitioners needed reactivation.`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
494
697
|
/**
|
|
495
698
|
* Inactivates all procedures associated with a deleted clinic
|
|
496
699
|
* @param procedureIds IDs of procedures associated with the clinic
|
|
@@ -534,6 +737,66 @@ export class ClinicAggregationService {
|
|
|
534
737
|
}
|
|
535
738
|
}
|
|
536
739
|
|
|
740
|
+
/**
|
|
741
|
+
* Reactivates all procedures associated with a reactivated clinic
|
|
742
|
+
* Only reactivates procedures that still exist (haven't been deleted)
|
|
743
|
+
* @param procedureIds IDs of procedures associated with the clinic
|
|
744
|
+
*/
|
|
745
|
+
async reactivateProceduresForClinic(procedureIds: string[]): Promise<void> {
|
|
746
|
+
if (!procedureIds || procedureIds.length === 0) {
|
|
747
|
+
console.log(
|
|
748
|
+
"[ClinicAggregationService] No procedure IDs provided for reactivation. Skipping."
|
|
749
|
+
);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const batch = this.db.batch();
|
|
754
|
+
let reactivationCount = 0;
|
|
755
|
+
|
|
756
|
+
console.log(
|
|
757
|
+
`[ClinicAggregationService] Starting reactivation of ${procedureIds.length} procedures.`
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
for (const procedureId of procedureIds) {
|
|
761
|
+
const procedureRef = this.db
|
|
762
|
+
.collection(PROCEDURES_COLLECTION)
|
|
763
|
+
.doc(procedureId);
|
|
764
|
+
|
|
765
|
+
// Check if procedure still exists (hasn't been deleted)
|
|
766
|
+
const procedureDoc = await procedureRef.get();
|
|
767
|
+
if (procedureDoc.exists) {
|
|
768
|
+
batch.update(procedureRef, {
|
|
769
|
+
isActive: true,
|
|
770
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
771
|
+
});
|
|
772
|
+
reactivationCount++;
|
|
773
|
+
} else {
|
|
774
|
+
console.log(
|
|
775
|
+
`[ClinicAggregationService] Procedure ${procedureId} no longer exists. Skipping reactivation.`
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (reactivationCount > 0) {
|
|
781
|
+
try {
|
|
782
|
+
await batch.commit();
|
|
783
|
+
console.log(
|
|
784
|
+
`[ClinicAggregationService] Successfully reactivated ${reactivationCount} procedures.`
|
|
785
|
+
);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error(
|
|
788
|
+
`[ClinicAggregationService] Error committing batch reactivation of procedures:`,
|
|
789
|
+
error
|
|
790
|
+
);
|
|
791
|
+
throw error;
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
console.log(
|
|
795
|
+
`[ClinicAggregationService] No procedures to reactivate (all may have been deleted).`
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
537
800
|
/**
|
|
538
801
|
* Removes clinic from clinic group when a clinic is deleted
|
|
539
802
|
* @param clinicGroupId ID of the parent clinic group
|
|
@@ -37,6 +37,15 @@ export interface ClinicLocation {
|
|
|
37
37
|
tz?: string | null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Interface for a break period during working hours
|
|
42
|
+
*/
|
|
43
|
+
export interface Break {
|
|
44
|
+
title?: string;
|
|
45
|
+
start: string;
|
|
46
|
+
end: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* Interface for working hours
|
|
42
51
|
*/
|
|
@@ -44,37 +53,37 @@ export interface WorkingHours {
|
|
|
44
53
|
monday: {
|
|
45
54
|
open: string;
|
|
46
55
|
close: string;
|
|
47
|
-
breaks?:
|
|
56
|
+
breaks?: Break[];
|
|
48
57
|
} | null;
|
|
49
58
|
tuesday: {
|
|
50
59
|
open: string;
|
|
51
60
|
close: string;
|
|
52
|
-
breaks?:
|
|
61
|
+
breaks?: Break[];
|
|
53
62
|
} | null;
|
|
54
63
|
wednesday: {
|
|
55
64
|
open: string;
|
|
56
65
|
close: string;
|
|
57
|
-
breaks?:
|
|
66
|
+
breaks?: Break[];
|
|
58
67
|
} | null;
|
|
59
68
|
thursday: {
|
|
60
69
|
open: string;
|
|
61
70
|
close: string;
|
|
62
|
-
breaks?:
|
|
71
|
+
breaks?: Break[];
|
|
63
72
|
} | null;
|
|
64
73
|
friday: {
|
|
65
74
|
open: string;
|
|
66
75
|
close: string;
|
|
67
|
-
breaks?:
|
|
76
|
+
breaks?: Break[];
|
|
68
77
|
} | null;
|
|
69
78
|
saturday: {
|
|
70
79
|
open: string;
|
|
71
80
|
close: string;
|
|
72
|
-
breaks?:
|
|
81
|
+
breaks?: Break[];
|
|
73
82
|
} | null;
|
|
74
83
|
sunday: {
|
|
75
84
|
open: string;
|
|
76
85
|
close: string;
|
|
77
|
-
breaks?:
|
|
86
|
+
breaks?: Break[];
|
|
78
87
|
} | null;
|
|
79
88
|
}
|
|
80
89
|
|