@blackcode_sa/metaestetics-api 1.13.20 → 1.13.21
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/index.js
CHANGED
|
@@ -7838,7 +7838,7 @@ var contraindicationSchema = import_zod6.z.object({
|
|
|
7838
7838
|
notes: import_zod6.z.string().optional().nullable(),
|
|
7839
7839
|
isActive: import_zod6.z.boolean()
|
|
7840
7840
|
});
|
|
7841
|
-
var
|
|
7841
|
+
var baseMedicationSchema = import_zod6.z.object({
|
|
7842
7842
|
name: import_zod6.z.string().min(1),
|
|
7843
7843
|
dosage: import_zod6.z.string().min(1),
|
|
7844
7844
|
frequency: import_zod6.z.string().min(1),
|
|
@@ -7846,6 +7846,24 @@ var medicationSchema = import_zod6.z.object({
|
|
|
7846
7846
|
endDate: timestampSchema.optional().nullable(),
|
|
7847
7847
|
prescribedBy: import_zod6.z.string().optional().nullable()
|
|
7848
7848
|
});
|
|
7849
|
+
var medicationSchema = baseMedicationSchema.refine(
|
|
7850
|
+
(data) => {
|
|
7851
|
+
if (!data.endDate) {
|
|
7852
|
+
return true;
|
|
7853
|
+
}
|
|
7854
|
+
if (!data.startDate) {
|
|
7855
|
+
return false;
|
|
7856
|
+
}
|
|
7857
|
+
const startDate = data.startDate.toDate();
|
|
7858
|
+
const endDate = data.endDate.toDate();
|
|
7859
|
+
return endDate >= startDate;
|
|
7860
|
+
},
|
|
7861
|
+
{
|
|
7862
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
7863
|
+
path: ["endDate"]
|
|
7864
|
+
// This will attach the error to the endDate field
|
|
7865
|
+
}
|
|
7866
|
+
);
|
|
7849
7867
|
var patientMedicalInfoSchema = import_zod6.z.object({
|
|
7850
7868
|
patientId: import_zod6.z.string(),
|
|
7851
7869
|
vitalStats: vitalStatsSchema,
|
|
@@ -7881,9 +7899,26 @@ var updateContraindicationSchema = contraindicationSchema.partial().extend({
|
|
|
7881
7899
|
contraindicationIndex: import_zod6.z.number().min(0)
|
|
7882
7900
|
});
|
|
7883
7901
|
var addMedicationSchema = medicationSchema;
|
|
7884
|
-
var updateMedicationSchema =
|
|
7902
|
+
var updateMedicationSchema = baseMedicationSchema.partial().extend({
|
|
7885
7903
|
medicationIndex: import_zod6.z.number().min(0)
|
|
7886
|
-
})
|
|
7904
|
+
}).refine(
|
|
7905
|
+
(data) => {
|
|
7906
|
+
if (!data.endDate) {
|
|
7907
|
+
return true;
|
|
7908
|
+
}
|
|
7909
|
+
if (!data.startDate) {
|
|
7910
|
+
return false;
|
|
7911
|
+
}
|
|
7912
|
+
const startDate = data.startDate.toDate();
|
|
7913
|
+
const endDate = data.endDate.toDate();
|
|
7914
|
+
return endDate >= startDate;
|
|
7915
|
+
},
|
|
7916
|
+
{
|
|
7917
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
7918
|
+
path: ["endDate"]
|
|
7919
|
+
// This will attach the error to the endDate field
|
|
7920
|
+
}
|
|
7921
|
+
);
|
|
7887
7922
|
|
|
7888
7923
|
// src/validations/patient.schema.ts
|
|
7889
7924
|
var locationDataSchema = import_zod7.z.object({
|
|
@@ -12315,6 +12350,9 @@ var PractitionerService = class extends BaseService {
|
|
|
12315
12350
|
*/
|
|
12316
12351
|
async EnableFreeConsultation(practitionerId, clinicId) {
|
|
12317
12352
|
try {
|
|
12353
|
+
console.log(
|
|
12354
|
+
`[EnableFreeConsultation] Starting for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12355
|
+
);
|
|
12318
12356
|
await this.ensureFreeConsultationInfrastructure();
|
|
12319
12357
|
const practitioner = await this.getPractitioner(practitionerId);
|
|
12320
12358
|
if (!practitioner) {
|
|
@@ -12330,32 +12368,83 @@ var PractitionerService = class extends BaseService {
|
|
|
12330
12368
|
);
|
|
12331
12369
|
}
|
|
12332
12370
|
const [activeProcedures, inactiveProcedures] = await Promise.all([
|
|
12333
|
-
this.getProcedureService().getProceduresByPractitioner(
|
|
12371
|
+
this.getProcedureService().getProceduresByPractitioner(
|
|
12372
|
+
practitionerId,
|
|
12373
|
+
void 0,
|
|
12374
|
+
// clinicBranchId
|
|
12375
|
+
false
|
|
12376
|
+
// excludeDraftPractitioners - allow draft practitioners
|
|
12377
|
+
),
|
|
12334
12378
|
this.getProcedureService().getInactiveProceduresByPractitioner(
|
|
12335
12379
|
practitionerId
|
|
12336
12380
|
)
|
|
12337
12381
|
]);
|
|
12338
12382
|
const allProcedures = [...activeProcedures, ...inactiveProcedures];
|
|
12339
|
-
const
|
|
12383
|
+
const existingConsultations = allProcedures.filter(
|
|
12340
12384
|
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
|
|
12341
12385
|
);
|
|
12386
|
+
console.log(
|
|
12387
|
+
`[EnableFreeConsultation] Found ${existingConsultations.length} existing free consultation(s)`
|
|
12388
|
+
);
|
|
12389
|
+
if (existingConsultations.length > 1) {
|
|
12390
|
+
console.warn(
|
|
12391
|
+
`[EnableFreeConsultation] WARNING: Found ${existingConsultations.length} duplicate free consultations for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12392
|
+
);
|
|
12393
|
+
for (let i = 1; i < existingConsultations.length; i++) {
|
|
12394
|
+
console.log(
|
|
12395
|
+
`[EnableFreeConsultation] Deactivating duplicate consultation ${existingConsultations[i].id}`
|
|
12396
|
+
);
|
|
12397
|
+
await this.getProcedureService().deactivateProcedure(
|
|
12398
|
+
existingConsultations[i].id
|
|
12399
|
+
);
|
|
12400
|
+
}
|
|
12401
|
+
}
|
|
12402
|
+
const existingConsultation = existingConsultations[0];
|
|
12342
12403
|
if (existingConsultation) {
|
|
12343
12404
|
if (existingConsultation.isActive) {
|
|
12344
12405
|
console.log(
|
|
12345
|
-
`Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12406
|
+
`[EnableFreeConsultation] Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12346
12407
|
);
|
|
12347
12408
|
return;
|
|
12348
12409
|
} else {
|
|
12410
|
+
console.log(
|
|
12411
|
+
`[EnableFreeConsultation] Reactivating existing consultation ${existingConsultation.id}`
|
|
12412
|
+
);
|
|
12349
12413
|
await this.getProcedureService().updateProcedure(
|
|
12350
12414
|
existingConsultation.id,
|
|
12351
12415
|
{ isActive: true }
|
|
12352
12416
|
);
|
|
12353
12417
|
console.log(
|
|
12354
|
-
`Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12418
|
+
`[EnableFreeConsultation] Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12355
12419
|
);
|
|
12356
12420
|
return;
|
|
12357
12421
|
}
|
|
12358
12422
|
}
|
|
12423
|
+
console.log(
|
|
12424
|
+
`[EnableFreeConsultation] Final race condition check before creating new procedure`
|
|
12425
|
+
);
|
|
12426
|
+
const finalCheckProcedures = await this.getProcedureService().getProceduresByPractitioner(
|
|
12427
|
+
practitionerId,
|
|
12428
|
+
void 0,
|
|
12429
|
+
// clinicBranchId
|
|
12430
|
+
false
|
|
12431
|
+
// excludeDraftPractitioners - allow draft practitioners
|
|
12432
|
+
);
|
|
12433
|
+
const raceConditionCheck = finalCheckProcedures.find(
|
|
12434
|
+
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
|
|
12435
|
+
);
|
|
12436
|
+
if (raceConditionCheck) {
|
|
12437
|
+
console.log(
|
|
12438
|
+
`[EnableFreeConsultation] Race condition detected! Procedure was created by another request. Using existing procedure ${raceConditionCheck.id}`
|
|
12439
|
+
);
|
|
12440
|
+
if (!raceConditionCheck.isActive) {
|
|
12441
|
+
await this.getProcedureService().updateProcedure(
|
|
12442
|
+
raceConditionCheck.id,
|
|
12443
|
+
{ isActive: true }
|
|
12444
|
+
);
|
|
12445
|
+
}
|
|
12446
|
+
return;
|
|
12447
|
+
}
|
|
12359
12448
|
const consultationData = {
|
|
12360
12449
|
name: "Free Consultation",
|
|
12361
12450
|
nameLower: "free consultation",
|
|
@@ -12375,15 +12464,18 @@ var PractitionerService = class extends BaseService {
|
|
|
12375
12464
|
photos: []
|
|
12376
12465
|
// No photos for consultation
|
|
12377
12466
|
};
|
|
12467
|
+
console.log(
|
|
12468
|
+
`[EnableFreeConsultation] Creating new free consultation procedure`
|
|
12469
|
+
);
|
|
12378
12470
|
await this.getProcedureService().createConsultationProcedure(
|
|
12379
12471
|
consultationData
|
|
12380
12472
|
);
|
|
12381
12473
|
console.log(
|
|
12382
|
-
`
|
|
12474
|
+
`[EnableFreeConsultation] Successfully created free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12383
12475
|
);
|
|
12384
12476
|
} catch (error) {
|
|
12385
12477
|
console.error(
|
|
12386
|
-
`Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
12478
|
+
`[EnableFreeConsultation] Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
12387
12479
|
error
|
|
12388
12480
|
);
|
|
12389
12481
|
throw error;
|
|
@@ -12479,17 +12571,40 @@ var PractitionerService = class extends BaseService {
|
|
|
12479
12571
|
);
|
|
12480
12572
|
}
|
|
12481
12573
|
const existingProcedures = await this.getProcedureService().getProceduresByPractitioner(
|
|
12482
|
-
practitionerId
|
|
12574
|
+
practitionerId,
|
|
12575
|
+
void 0,
|
|
12576
|
+
// clinicBranchId (optional)
|
|
12577
|
+
false
|
|
12578
|
+
// excludeDraftPractitioners - must be false to find procedures for draft practitioners
|
|
12579
|
+
);
|
|
12580
|
+
console.log(
|
|
12581
|
+
`[DisableFreeConsultation] Found ${existingProcedures.length} procedures for practitioner ${practitionerId}`
|
|
12483
12582
|
);
|
|
12484
12583
|
const freeConsultation = existingProcedures.find(
|
|
12485
12584
|
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId && procedure.isActive
|
|
12486
12585
|
);
|
|
12487
12586
|
if (!freeConsultation) {
|
|
12488
12587
|
console.log(
|
|
12489
|
-
`No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12588
|
+
`[DisableFreeConsultation] No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12589
|
+
);
|
|
12590
|
+
console.log(
|
|
12591
|
+
`[DisableFreeConsultation] Existing procedures:`,
|
|
12592
|
+
existingProcedures.map((p) => {
|
|
12593
|
+
var _a;
|
|
12594
|
+
return {
|
|
12595
|
+
id: p.id,
|
|
12596
|
+
name: p.name,
|
|
12597
|
+
technologyId: (_a = p.technology) == null ? void 0 : _a.id,
|
|
12598
|
+
clinicBranchId: p.clinicBranchId,
|
|
12599
|
+
isActive: p.isActive
|
|
12600
|
+
};
|
|
12601
|
+
})
|
|
12490
12602
|
);
|
|
12491
12603
|
return;
|
|
12492
12604
|
}
|
|
12605
|
+
console.log(
|
|
12606
|
+
`[DisableFreeConsultation] Found free consultation procedure ${freeConsultation.id}, deactivating...`
|
|
12607
|
+
);
|
|
12493
12608
|
await this.getProcedureService().deactivateProcedure(freeConsultation.id);
|
|
12494
12609
|
console.log(
|
|
12495
12610
|
`Free consultation disabled for practitioner ${practitionerId} in clinic ${clinicId}`
|
package/dist/index.mjs
CHANGED
|
@@ -7778,7 +7778,7 @@ var contraindicationSchema = z6.object({
|
|
|
7778
7778
|
notes: z6.string().optional().nullable(),
|
|
7779
7779
|
isActive: z6.boolean()
|
|
7780
7780
|
});
|
|
7781
|
-
var
|
|
7781
|
+
var baseMedicationSchema = z6.object({
|
|
7782
7782
|
name: z6.string().min(1),
|
|
7783
7783
|
dosage: z6.string().min(1),
|
|
7784
7784
|
frequency: z6.string().min(1),
|
|
@@ -7786,6 +7786,24 @@ var medicationSchema = z6.object({
|
|
|
7786
7786
|
endDate: timestampSchema.optional().nullable(),
|
|
7787
7787
|
prescribedBy: z6.string().optional().nullable()
|
|
7788
7788
|
});
|
|
7789
|
+
var medicationSchema = baseMedicationSchema.refine(
|
|
7790
|
+
(data) => {
|
|
7791
|
+
if (!data.endDate) {
|
|
7792
|
+
return true;
|
|
7793
|
+
}
|
|
7794
|
+
if (!data.startDate) {
|
|
7795
|
+
return false;
|
|
7796
|
+
}
|
|
7797
|
+
const startDate = data.startDate.toDate();
|
|
7798
|
+
const endDate = data.endDate.toDate();
|
|
7799
|
+
return endDate >= startDate;
|
|
7800
|
+
},
|
|
7801
|
+
{
|
|
7802
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
7803
|
+
path: ["endDate"]
|
|
7804
|
+
// This will attach the error to the endDate field
|
|
7805
|
+
}
|
|
7806
|
+
);
|
|
7789
7807
|
var patientMedicalInfoSchema = z6.object({
|
|
7790
7808
|
patientId: z6.string(),
|
|
7791
7809
|
vitalStats: vitalStatsSchema,
|
|
@@ -7821,9 +7839,26 @@ var updateContraindicationSchema = contraindicationSchema.partial().extend({
|
|
|
7821
7839
|
contraindicationIndex: z6.number().min(0)
|
|
7822
7840
|
});
|
|
7823
7841
|
var addMedicationSchema = medicationSchema;
|
|
7824
|
-
var updateMedicationSchema =
|
|
7842
|
+
var updateMedicationSchema = baseMedicationSchema.partial().extend({
|
|
7825
7843
|
medicationIndex: z6.number().min(0)
|
|
7826
|
-
})
|
|
7844
|
+
}).refine(
|
|
7845
|
+
(data) => {
|
|
7846
|
+
if (!data.endDate) {
|
|
7847
|
+
return true;
|
|
7848
|
+
}
|
|
7849
|
+
if (!data.startDate) {
|
|
7850
|
+
return false;
|
|
7851
|
+
}
|
|
7852
|
+
const startDate = data.startDate.toDate();
|
|
7853
|
+
const endDate = data.endDate.toDate();
|
|
7854
|
+
return endDate >= startDate;
|
|
7855
|
+
},
|
|
7856
|
+
{
|
|
7857
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
7858
|
+
path: ["endDate"]
|
|
7859
|
+
// This will attach the error to the endDate field
|
|
7860
|
+
}
|
|
7861
|
+
);
|
|
7827
7862
|
|
|
7828
7863
|
// src/validations/patient.schema.ts
|
|
7829
7864
|
var locationDataSchema = z7.object({
|
|
@@ -12338,6 +12373,9 @@ var PractitionerService = class extends BaseService {
|
|
|
12338
12373
|
*/
|
|
12339
12374
|
async EnableFreeConsultation(practitionerId, clinicId) {
|
|
12340
12375
|
try {
|
|
12376
|
+
console.log(
|
|
12377
|
+
`[EnableFreeConsultation] Starting for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12378
|
+
);
|
|
12341
12379
|
await this.ensureFreeConsultationInfrastructure();
|
|
12342
12380
|
const practitioner = await this.getPractitioner(practitionerId);
|
|
12343
12381
|
if (!practitioner) {
|
|
@@ -12353,32 +12391,83 @@ var PractitionerService = class extends BaseService {
|
|
|
12353
12391
|
);
|
|
12354
12392
|
}
|
|
12355
12393
|
const [activeProcedures, inactiveProcedures] = await Promise.all([
|
|
12356
|
-
this.getProcedureService().getProceduresByPractitioner(
|
|
12394
|
+
this.getProcedureService().getProceduresByPractitioner(
|
|
12395
|
+
practitionerId,
|
|
12396
|
+
void 0,
|
|
12397
|
+
// clinicBranchId
|
|
12398
|
+
false
|
|
12399
|
+
// excludeDraftPractitioners - allow draft practitioners
|
|
12400
|
+
),
|
|
12357
12401
|
this.getProcedureService().getInactiveProceduresByPractitioner(
|
|
12358
12402
|
practitionerId
|
|
12359
12403
|
)
|
|
12360
12404
|
]);
|
|
12361
12405
|
const allProcedures = [...activeProcedures, ...inactiveProcedures];
|
|
12362
|
-
const
|
|
12406
|
+
const existingConsultations = allProcedures.filter(
|
|
12363
12407
|
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
|
|
12364
12408
|
);
|
|
12409
|
+
console.log(
|
|
12410
|
+
`[EnableFreeConsultation] Found ${existingConsultations.length} existing free consultation(s)`
|
|
12411
|
+
);
|
|
12412
|
+
if (existingConsultations.length > 1) {
|
|
12413
|
+
console.warn(
|
|
12414
|
+
`[EnableFreeConsultation] WARNING: Found ${existingConsultations.length} duplicate free consultations for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12415
|
+
);
|
|
12416
|
+
for (let i = 1; i < existingConsultations.length; i++) {
|
|
12417
|
+
console.log(
|
|
12418
|
+
`[EnableFreeConsultation] Deactivating duplicate consultation ${existingConsultations[i].id}`
|
|
12419
|
+
);
|
|
12420
|
+
await this.getProcedureService().deactivateProcedure(
|
|
12421
|
+
existingConsultations[i].id
|
|
12422
|
+
);
|
|
12423
|
+
}
|
|
12424
|
+
}
|
|
12425
|
+
const existingConsultation = existingConsultations[0];
|
|
12365
12426
|
if (existingConsultation) {
|
|
12366
12427
|
if (existingConsultation.isActive) {
|
|
12367
12428
|
console.log(
|
|
12368
|
-
`Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12429
|
+
`[EnableFreeConsultation] Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12369
12430
|
);
|
|
12370
12431
|
return;
|
|
12371
12432
|
} else {
|
|
12433
|
+
console.log(
|
|
12434
|
+
`[EnableFreeConsultation] Reactivating existing consultation ${existingConsultation.id}`
|
|
12435
|
+
);
|
|
12372
12436
|
await this.getProcedureService().updateProcedure(
|
|
12373
12437
|
existingConsultation.id,
|
|
12374
12438
|
{ isActive: true }
|
|
12375
12439
|
);
|
|
12376
12440
|
console.log(
|
|
12377
|
-
`Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12441
|
+
`[EnableFreeConsultation] Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12378
12442
|
);
|
|
12379
12443
|
return;
|
|
12380
12444
|
}
|
|
12381
12445
|
}
|
|
12446
|
+
console.log(
|
|
12447
|
+
`[EnableFreeConsultation] Final race condition check before creating new procedure`
|
|
12448
|
+
);
|
|
12449
|
+
const finalCheckProcedures = await this.getProcedureService().getProceduresByPractitioner(
|
|
12450
|
+
practitionerId,
|
|
12451
|
+
void 0,
|
|
12452
|
+
// clinicBranchId
|
|
12453
|
+
false
|
|
12454
|
+
// excludeDraftPractitioners - allow draft practitioners
|
|
12455
|
+
);
|
|
12456
|
+
const raceConditionCheck = finalCheckProcedures.find(
|
|
12457
|
+
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
|
|
12458
|
+
);
|
|
12459
|
+
if (raceConditionCheck) {
|
|
12460
|
+
console.log(
|
|
12461
|
+
`[EnableFreeConsultation] Race condition detected! Procedure was created by another request. Using existing procedure ${raceConditionCheck.id}`
|
|
12462
|
+
);
|
|
12463
|
+
if (!raceConditionCheck.isActive) {
|
|
12464
|
+
await this.getProcedureService().updateProcedure(
|
|
12465
|
+
raceConditionCheck.id,
|
|
12466
|
+
{ isActive: true }
|
|
12467
|
+
);
|
|
12468
|
+
}
|
|
12469
|
+
return;
|
|
12470
|
+
}
|
|
12382
12471
|
const consultationData = {
|
|
12383
12472
|
name: "Free Consultation",
|
|
12384
12473
|
nameLower: "free consultation",
|
|
@@ -12398,15 +12487,18 @@ var PractitionerService = class extends BaseService {
|
|
|
12398
12487
|
photos: []
|
|
12399
12488
|
// No photos for consultation
|
|
12400
12489
|
};
|
|
12490
|
+
console.log(
|
|
12491
|
+
`[EnableFreeConsultation] Creating new free consultation procedure`
|
|
12492
|
+
);
|
|
12401
12493
|
await this.getProcedureService().createConsultationProcedure(
|
|
12402
12494
|
consultationData
|
|
12403
12495
|
);
|
|
12404
12496
|
console.log(
|
|
12405
|
-
`
|
|
12497
|
+
`[EnableFreeConsultation] Successfully created free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12406
12498
|
);
|
|
12407
12499
|
} catch (error) {
|
|
12408
12500
|
console.error(
|
|
12409
|
-
`Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
12501
|
+
`[EnableFreeConsultation] Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
12410
12502
|
error
|
|
12411
12503
|
);
|
|
12412
12504
|
throw error;
|
|
@@ -12502,17 +12594,40 @@ var PractitionerService = class extends BaseService {
|
|
|
12502
12594
|
);
|
|
12503
12595
|
}
|
|
12504
12596
|
const existingProcedures = await this.getProcedureService().getProceduresByPractitioner(
|
|
12505
|
-
practitionerId
|
|
12597
|
+
practitionerId,
|
|
12598
|
+
void 0,
|
|
12599
|
+
// clinicBranchId (optional)
|
|
12600
|
+
false
|
|
12601
|
+
// excludeDraftPractitioners - must be false to find procedures for draft practitioners
|
|
12602
|
+
);
|
|
12603
|
+
console.log(
|
|
12604
|
+
`[DisableFreeConsultation] Found ${existingProcedures.length} procedures for practitioner ${practitionerId}`
|
|
12506
12605
|
);
|
|
12507
12606
|
const freeConsultation = existingProcedures.find(
|
|
12508
12607
|
(procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId && procedure.isActive
|
|
12509
12608
|
);
|
|
12510
12609
|
if (!freeConsultation) {
|
|
12511
12610
|
console.log(
|
|
12512
|
-
`No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12611
|
+
`[DisableFreeConsultation] No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
12612
|
+
);
|
|
12613
|
+
console.log(
|
|
12614
|
+
`[DisableFreeConsultation] Existing procedures:`,
|
|
12615
|
+
existingProcedures.map((p) => {
|
|
12616
|
+
var _a;
|
|
12617
|
+
return {
|
|
12618
|
+
id: p.id,
|
|
12619
|
+
name: p.name,
|
|
12620
|
+
technologyId: (_a = p.technology) == null ? void 0 : _a.id,
|
|
12621
|
+
clinicBranchId: p.clinicBranchId,
|
|
12622
|
+
isActive: p.isActive
|
|
12623
|
+
};
|
|
12624
|
+
})
|
|
12513
12625
|
);
|
|
12514
12626
|
return;
|
|
12515
12627
|
}
|
|
12628
|
+
console.log(
|
|
12629
|
+
`[DisableFreeConsultation] Found free consultation procedure ${freeConsultation.id}, deactivating...`
|
|
12630
|
+
);
|
|
12516
12631
|
await this.getProcedureService().deactivateProcedure(freeConsultation.id);
|
|
12517
12632
|
console.log(
|
|
12518
12633
|
`Free consultation disabled for practitioner ${practitionerId} in clinic ${clinicId}`
|
package/package.json
CHANGED
|
@@ -1546,6 +1546,10 @@ export class PractitionerService extends BaseService {
|
|
|
1546
1546
|
clinicId: string
|
|
1547
1547
|
): Promise<void> {
|
|
1548
1548
|
try {
|
|
1549
|
+
console.log(
|
|
1550
|
+
`[EnableFreeConsultation] Starting for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1551
|
+
);
|
|
1552
|
+
|
|
1549
1553
|
// First, ensure the free consultation infrastructure exists
|
|
1550
1554
|
await this.ensureFreeConsultationInfrastructure();
|
|
1551
1555
|
|
|
@@ -1573,9 +1577,15 @@ export class PractitionerService extends BaseService {
|
|
|
1573
1577
|
);
|
|
1574
1578
|
}
|
|
1575
1579
|
|
|
1576
|
-
//
|
|
1580
|
+
// CRITICAL: Double-check for existing procedures to prevent race conditions
|
|
1581
|
+
// Fetch procedures again right before creation/update
|
|
1582
|
+
// IMPORTANT: Pass false for excludeDraftPractitioners to work with draft practitioners
|
|
1577
1583
|
const [activeProcedures, inactiveProcedures] = await Promise.all([
|
|
1578
|
-
this.getProcedureService().getProceduresByPractitioner(
|
|
1584
|
+
this.getProcedureService().getProceduresByPractitioner(
|
|
1585
|
+
practitionerId,
|
|
1586
|
+
undefined, // clinicBranchId
|
|
1587
|
+
false // excludeDraftPractitioners - allow draft practitioners
|
|
1588
|
+
),
|
|
1579
1589
|
this.getProcedureService().getInactiveProceduresByPractitioner(
|
|
1580
1590
|
practitionerId
|
|
1581
1591
|
),
|
|
@@ -1585,31 +1595,86 @@ export class PractitionerService extends BaseService {
|
|
|
1585
1595
|
const allProcedures = [...activeProcedures, ...inactiveProcedures];
|
|
1586
1596
|
|
|
1587
1597
|
// Check if free consultation already exists (active or inactive)
|
|
1588
|
-
const
|
|
1598
|
+
const existingConsultations = allProcedures.filter(
|
|
1589
1599
|
(procedure) =>
|
|
1590
1600
|
procedure.technology.id === "free-consultation-tech" &&
|
|
1591
1601
|
procedure.clinicBranchId === clinicId
|
|
1592
1602
|
);
|
|
1593
1603
|
|
|
1604
|
+
console.log(
|
|
1605
|
+
`[EnableFreeConsultation] Found ${existingConsultations.length} existing free consultation(s)`
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
// If multiple consultations exist, log a warning and clean up duplicates
|
|
1609
|
+
if (existingConsultations.length > 1) {
|
|
1610
|
+
console.warn(
|
|
1611
|
+
`[EnableFreeConsultation] WARNING: Found ${existingConsultations.length} duplicate free consultations for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1612
|
+
);
|
|
1613
|
+
// Keep the first one, deactivate the rest
|
|
1614
|
+
for (let i = 1; i < existingConsultations.length; i++) {
|
|
1615
|
+
console.log(
|
|
1616
|
+
`[EnableFreeConsultation] Deactivating duplicate consultation ${existingConsultations[i].id}`
|
|
1617
|
+
);
|
|
1618
|
+
await this.getProcedureService().deactivateProcedure(
|
|
1619
|
+
existingConsultations[i].id
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
const existingConsultation = existingConsultations[0];
|
|
1625
|
+
|
|
1594
1626
|
if (existingConsultation) {
|
|
1595
1627
|
if (existingConsultation.isActive) {
|
|
1596
1628
|
console.log(
|
|
1597
|
-
`Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1629
|
+
`[EnableFreeConsultation] Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1598
1630
|
);
|
|
1599
1631
|
return;
|
|
1600
1632
|
} else {
|
|
1601
1633
|
// Reactivate the existing disabled consultation
|
|
1634
|
+
console.log(
|
|
1635
|
+
`[EnableFreeConsultation] Reactivating existing consultation ${existingConsultation.id}`
|
|
1636
|
+
);
|
|
1602
1637
|
await this.getProcedureService().updateProcedure(
|
|
1603
1638
|
existingConsultation.id,
|
|
1604
1639
|
{ isActive: true }
|
|
1605
1640
|
);
|
|
1606
1641
|
console.log(
|
|
1607
|
-
`Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1642
|
+
`[EnableFreeConsultation] Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1608
1643
|
);
|
|
1609
1644
|
return;
|
|
1610
1645
|
}
|
|
1611
1646
|
}
|
|
1612
1647
|
|
|
1648
|
+
// Final check before creating - race condition guard
|
|
1649
|
+
// Fetch one more time to ensure no procedure was created in parallel
|
|
1650
|
+
console.log(
|
|
1651
|
+
`[EnableFreeConsultation] Final race condition check before creating new procedure`
|
|
1652
|
+
);
|
|
1653
|
+
const finalCheckProcedures =
|
|
1654
|
+
await this.getProcedureService().getProceduresByPractitioner(
|
|
1655
|
+
practitionerId,
|
|
1656
|
+
undefined, // clinicBranchId
|
|
1657
|
+
false // excludeDraftPractitioners - allow draft practitioners
|
|
1658
|
+
);
|
|
1659
|
+
const raceConditionCheck = finalCheckProcedures.find(
|
|
1660
|
+
(procedure) =>
|
|
1661
|
+
procedure.technology.id === "free-consultation-tech" &&
|
|
1662
|
+
procedure.clinicBranchId === clinicId
|
|
1663
|
+
);
|
|
1664
|
+
|
|
1665
|
+
if (raceConditionCheck) {
|
|
1666
|
+
console.log(
|
|
1667
|
+
`[EnableFreeConsultation] Race condition detected! Procedure was created by another request. Using existing procedure ${raceConditionCheck.id}`
|
|
1668
|
+
);
|
|
1669
|
+
if (!raceConditionCheck.isActive) {
|
|
1670
|
+
await this.getProcedureService().updateProcedure(
|
|
1671
|
+
raceConditionCheck.id,
|
|
1672
|
+
{ isActive: true }
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1613
1678
|
// Create procedure data for free consultation (without productId or productsMetadata)
|
|
1614
1679
|
const consultationData: Omit<CreateProcedureData, "productId"> = {
|
|
1615
1680
|
name: "Free Consultation",
|
|
@@ -1631,16 +1696,19 @@ export class PractitionerService extends BaseService {
|
|
|
1631
1696
|
};
|
|
1632
1697
|
|
|
1633
1698
|
// Create the consultation procedure using the special method
|
|
1699
|
+
console.log(
|
|
1700
|
+
`[EnableFreeConsultation] Creating new free consultation procedure`
|
|
1701
|
+
);
|
|
1634
1702
|
await this.getProcedureService().createConsultationProcedure(
|
|
1635
1703
|
consultationData
|
|
1636
1704
|
);
|
|
1637
1705
|
|
|
1638
1706
|
console.log(
|
|
1639
|
-
`
|
|
1707
|
+
`[EnableFreeConsultation] Successfully created free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1640
1708
|
);
|
|
1641
1709
|
} catch (error) {
|
|
1642
1710
|
console.error(
|
|
1643
|
-
`Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
1711
|
+
`[EnableFreeConsultation] Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
|
|
1644
1712
|
error
|
|
1645
1713
|
);
|
|
1646
1714
|
throw error;
|
|
@@ -1764,10 +1832,18 @@ export class PractitionerService extends BaseService {
|
|
|
1764
1832
|
|
|
1765
1833
|
// Find the free consultation procedure for this practitioner in this clinic
|
|
1766
1834
|
// Use the more specific search by technology ID instead of name
|
|
1835
|
+
// IMPORTANT: Pass false for excludeDraftPractitioners to allow disabling for draft practitioners
|
|
1767
1836
|
const existingProcedures =
|
|
1768
1837
|
await this.getProcedureService().getProceduresByPractitioner(
|
|
1769
|
-
practitionerId
|
|
1838
|
+
practitionerId,
|
|
1839
|
+
undefined, // clinicBranchId (optional)
|
|
1840
|
+
false // excludeDraftPractitioners - must be false to find procedures for draft practitioners
|
|
1770
1841
|
);
|
|
1842
|
+
|
|
1843
|
+
console.log(
|
|
1844
|
+
`[DisableFreeConsultation] Found ${existingProcedures.length} procedures for practitioner ${practitionerId}`
|
|
1845
|
+
);
|
|
1846
|
+
|
|
1771
1847
|
const freeConsultation = existingProcedures.find(
|
|
1772
1848
|
(procedure) =>
|
|
1773
1849
|
procedure.technology.id === "free-consultation-tech" &&
|
|
@@ -1777,10 +1853,24 @@ export class PractitionerService extends BaseService {
|
|
|
1777
1853
|
|
|
1778
1854
|
if (!freeConsultation) {
|
|
1779
1855
|
console.log(
|
|
1780
|
-
`No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1856
|
+
`[DisableFreeConsultation] No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
|
|
1857
|
+
);
|
|
1858
|
+
console.log(
|
|
1859
|
+
`[DisableFreeConsultation] Existing procedures:`,
|
|
1860
|
+
existingProcedures.map(p => ({
|
|
1861
|
+
id: p.id,
|
|
1862
|
+
name: p.name,
|
|
1863
|
+
technologyId: p.technology?.id,
|
|
1864
|
+
clinicBranchId: p.clinicBranchId,
|
|
1865
|
+
isActive: p.isActive
|
|
1866
|
+
}))
|
|
1781
1867
|
);
|
|
1782
1868
|
return;
|
|
1783
1869
|
}
|
|
1870
|
+
|
|
1871
|
+
console.log(
|
|
1872
|
+
`[DisableFreeConsultation] Found free consultation procedure ${freeConsultation.id}, deactivating...`
|
|
1873
|
+
);
|
|
1784
1874
|
|
|
1785
1875
|
// Deactivate the consultation procedure
|
|
1786
1876
|
await this.getProcedureService().deactivateProcedure(freeConsultation.id);
|
|
@@ -63,7 +63,8 @@ export const contraindicationSchema = z.object({
|
|
|
63
63
|
isActive: z.boolean(),
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
// Base medication schema without refinement (for update operations that need .partial())
|
|
67
|
+
const baseMedicationSchema = z.object({
|
|
67
68
|
name: z.string().min(1),
|
|
68
69
|
dosage: z.string().min(1),
|
|
69
70
|
frequency: z.string().min(1),
|
|
@@ -72,6 +73,33 @@ export const medicationSchema = z.object({
|
|
|
72
73
|
prescribedBy: z.string().optional().nullable(),
|
|
73
74
|
});
|
|
74
75
|
|
|
76
|
+
// Medication schema with date validation refinement
|
|
77
|
+
export const medicationSchema = baseMedicationSchema.refine(
|
|
78
|
+
(data) => {
|
|
79
|
+
// If either date is not provided, skip validation (both are optional)
|
|
80
|
+
// However, if endDate is provided, startDate must also be provided
|
|
81
|
+
if (!data.endDate) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If endDate exists but startDate doesn't, this is invalid
|
|
86
|
+
if (!data.startDate) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Both dates must be Timestamp objects with toDate method
|
|
91
|
+
const startDate = data.startDate.toDate();
|
|
92
|
+
const endDate = data.endDate.toDate();
|
|
93
|
+
|
|
94
|
+
// End date must be >= start date
|
|
95
|
+
return endDate >= startDate;
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
99
|
+
path: ["endDate"], // This will attach the error to the endDate field
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
75
103
|
export const patientMedicalInfoSchema = z.object({
|
|
76
104
|
patientId: z.string(),
|
|
77
105
|
vitalStats: vitalStatsSchema,
|
|
@@ -120,6 +148,30 @@ export const updateContraindicationSchema = contraindicationSchema
|
|
|
120
148
|
});
|
|
121
149
|
|
|
122
150
|
export const addMedicationSchema = medicationSchema;
|
|
123
|
-
export const updateMedicationSchema =
|
|
151
|
+
export const updateMedicationSchema = baseMedicationSchema.partial().extend({
|
|
124
152
|
medicationIndex: z.number().min(0),
|
|
125
|
-
})
|
|
153
|
+
}).refine(
|
|
154
|
+
(data) => {
|
|
155
|
+
// If either date is not provided, skip validation (both are optional)
|
|
156
|
+
// However, if endDate is provided, startDate must also be provided
|
|
157
|
+
if (!data.endDate) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If endDate exists but startDate doesn't, this is invalid
|
|
162
|
+
if (!data.startDate) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Both dates must be Timestamp objects with toDate method
|
|
167
|
+
const startDate = data.startDate.toDate();
|
|
168
|
+
const endDate = data.endDate.toDate();
|
|
169
|
+
|
|
170
|
+
// End date must be >= start date
|
|
171
|
+
return endDate >= startDate;
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
message: "End date requires a start date and must be equal to or after start date",
|
|
175
|
+
path: ["endDate"], // This will attach the error to the endDate field
|
|
176
|
+
}
|
|
177
|
+
);
|