@blackcode_sa/metaestetics-api 1.7.44 → 1.7.45
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.d.mts
CHANGED
|
@@ -6391,6 +6391,15 @@ declare class ProcedureService extends BaseService {
|
|
|
6391
6391
|
* @returns The created procedure
|
|
6392
6392
|
*/
|
|
6393
6393
|
createProcedure(data: CreateProcedureData): Promise<Procedure>;
|
|
6394
|
+
/**
|
|
6395
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
6396
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
6397
|
+
*
|
|
6398
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
6399
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
6400
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
6401
|
+
*/
|
|
6402
|
+
bulkCreateProcedures(baseData: Omit<CreateProcedureData, "practitionerId">, practitionerIds: string[]): Promise<Procedure[]>;
|
|
6394
6403
|
/**
|
|
6395
6404
|
* Gets a procedure by ID
|
|
6396
6405
|
* @param id - The ID of the procedure to get
|
package/dist/index.d.ts
CHANGED
|
@@ -6391,6 +6391,15 @@ declare class ProcedureService extends BaseService {
|
|
|
6391
6391
|
* @returns The created procedure
|
|
6392
6392
|
*/
|
|
6393
6393
|
createProcedure(data: CreateProcedureData): Promise<Procedure>;
|
|
6394
|
+
/**
|
|
6395
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
6396
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
6397
|
+
*
|
|
6398
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
6399
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
6400
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
6401
|
+
*/
|
|
6402
|
+
bulkCreateProcedures(baseData: Omit<CreateProcedureData, "practitionerId">, practitionerIds: string[]): Promise<Procedure[]>;
|
|
6394
6403
|
/**
|
|
6395
6404
|
* Gets a procedure by ID
|
|
6396
6405
|
* @param id - The ID of the procedure to get
|
package/dist/index.js
CHANGED
|
@@ -5594,9 +5594,6 @@ var PractitionerService = class extends BaseService {
|
|
|
5594
5594
|
if (!practitioner) {
|
|
5595
5595
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
5596
5596
|
}
|
|
5597
|
-
if (!practitioner.isActive) {
|
|
5598
|
-
throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
5599
|
-
}
|
|
5600
5597
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
5601
5598
|
if (!clinic) {
|
|
5602
5599
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -9170,6 +9167,149 @@ var ProcedureService = class extends BaseService {
|
|
|
9170
9167
|
const savedDoc = await (0, import_firestore27.getDoc)(procedureRef);
|
|
9171
9168
|
return savedDoc.data();
|
|
9172
9169
|
}
|
|
9170
|
+
/**
|
|
9171
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
9172
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
9173
|
+
*
|
|
9174
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
9175
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
9176
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
9177
|
+
*/
|
|
9178
|
+
async bulkCreateProcedures(baseData, practitionerIds) {
|
|
9179
|
+
var _a;
|
|
9180
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
9181
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
9182
|
+
}
|
|
9183
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
9184
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
9185
|
+
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
9186
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
9187
|
+
this.subcategoryService.getById(
|
|
9188
|
+
validatedData.categoryId,
|
|
9189
|
+
validatedData.subcategoryId
|
|
9190
|
+
),
|
|
9191
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
9192
|
+
this.productService.getById(
|
|
9193
|
+
validatedData.technologyId,
|
|
9194
|
+
validatedData.productId
|
|
9195
|
+
),
|
|
9196
|
+
(0, import_firestore27.getDoc)((0, import_firestore27.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
9197
|
+
]);
|
|
9198
|
+
if (!category || !subcategory || !technology || !product) {
|
|
9199
|
+
throw new Error("One or more required base entities not found");
|
|
9200
|
+
}
|
|
9201
|
+
if (!clinicSnapshot.exists()) {
|
|
9202
|
+
throw new Error(
|
|
9203
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
9204
|
+
);
|
|
9205
|
+
}
|
|
9206
|
+
const clinic = clinicSnapshot.data();
|
|
9207
|
+
let processedPhotos = [];
|
|
9208
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
9209
|
+
const batchId = this.generateId();
|
|
9210
|
+
processedPhotos = await this.processMediaArray(
|
|
9211
|
+
validatedData.photos,
|
|
9212
|
+
batchId,
|
|
9213
|
+
"procedure-photos-batch"
|
|
9214
|
+
);
|
|
9215
|
+
}
|
|
9216
|
+
const practitionersMap = /* @__PURE__ */ new Map();
|
|
9217
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
9218
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
9219
|
+
const practitionersQuery = (0, import_firestore27.query)(
|
|
9220
|
+
(0, import_firestore27.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
9221
|
+
(0, import_firestore27.where)((0, import_firestore27.documentId)(), "in", chunk)
|
|
9222
|
+
);
|
|
9223
|
+
const practitionersSnapshot = await (0, import_firestore27.getDocs)(practitionersQuery);
|
|
9224
|
+
practitionersSnapshot.docs.forEach((doc36) => {
|
|
9225
|
+
practitionersMap.set(doc36.id, doc36.data());
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9228
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
9229
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
9230
|
+
const notFoundIds = practitionerIds.filter(
|
|
9231
|
+
(id) => !foundIds.includes(id)
|
|
9232
|
+
);
|
|
9233
|
+
throw new Error(
|
|
9234
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
9235
|
+
);
|
|
9236
|
+
}
|
|
9237
|
+
const batch = (0, import_firestore27.writeBatch)(this.db);
|
|
9238
|
+
const createdProcedureIds = [];
|
|
9239
|
+
const clinicInfo = {
|
|
9240
|
+
id: clinicSnapshot.id,
|
|
9241
|
+
name: clinic.name,
|
|
9242
|
+
description: clinic.description || "",
|
|
9243
|
+
featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? typeof clinic.featuredPhotos[0] === "string" ? clinic.featuredPhotos[0] : "" : typeof clinic.coverPhoto === "string" ? clinic.coverPhoto : "",
|
|
9244
|
+
location: clinic.location,
|
|
9245
|
+
contactInfo: clinic.contactInfo
|
|
9246
|
+
};
|
|
9247
|
+
for (const practitionerId of practitionerIds) {
|
|
9248
|
+
const practitioner = practitionersMap.get(practitionerId);
|
|
9249
|
+
const doctorInfo = {
|
|
9250
|
+
id: practitioner.id,
|
|
9251
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
9252
|
+
description: practitioner.basicInfo.bio || "",
|
|
9253
|
+
photo: typeof practitioner.basicInfo.profileImageUrl === "string" ? practitioner.basicInfo.profileImageUrl : "",
|
|
9254
|
+
rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
|
|
9255
|
+
services: practitioner.procedures || []
|
|
9256
|
+
};
|
|
9257
|
+
const procedureId = this.generateId();
|
|
9258
|
+
createdProcedureIds.push(procedureId);
|
|
9259
|
+
const procedureRef = (0, import_firestore27.doc)(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
9260
|
+
const newProcedure = {
|
|
9261
|
+
id: procedureId,
|
|
9262
|
+
...validatedData,
|
|
9263
|
+
practitionerId,
|
|
9264
|
+
// Override practitionerId with the correct one
|
|
9265
|
+
photos: processedPhotos,
|
|
9266
|
+
category,
|
|
9267
|
+
subcategory,
|
|
9268
|
+
technology,
|
|
9269
|
+
product,
|
|
9270
|
+
blockingConditions: technology.blockingConditions,
|
|
9271
|
+
contraindications: technology.contraindications || [],
|
|
9272
|
+
treatmentBenefits: technology.benefits,
|
|
9273
|
+
preRequirements: technology.requirements.pre,
|
|
9274
|
+
postRequirements: technology.requirements.post,
|
|
9275
|
+
certificationRequirement: technology.certificationRequirement,
|
|
9276
|
+
documentationTemplates: (technology == null ? void 0 : technology.documentationTemplates) || [],
|
|
9277
|
+
clinicInfo,
|
|
9278
|
+
doctorInfo,
|
|
9279
|
+
// Set specific doctor info
|
|
9280
|
+
reviewInfo: {
|
|
9281
|
+
totalReviews: 0,
|
|
9282
|
+
averageRating: 0,
|
|
9283
|
+
effectivenessOfTreatment: 0,
|
|
9284
|
+
outcomeExplanation: 0,
|
|
9285
|
+
painManagement: 0,
|
|
9286
|
+
followUpCare: 0,
|
|
9287
|
+
valueForMoney: 0,
|
|
9288
|
+
recommendationPercentage: 0
|
|
9289
|
+
},
|
|
9290
|
+
isActive: true
|
|
9291
|
+
};
|
|
9292
|
+
batch.set(procedureRef, {
|
|
9293
|
+
...newProcedure,
|
|
9294
|
+
createdAt: (0, import_firestore27.serverTimestamp)(),
|
|
9295
|
+
updatedAt: (0, import_firestore27.serverTimestamp)()
|
|
9296
|
+
});
|
|
9297
|
+
}
|
|
9298
|
+
await batch.commit();
|
|
9299
|
+
const fetchedProcedures = [];
|
|
9300
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
9301
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
9302
|
+
const q = (0, import_firestore27.query)(
|
|
9303
|
+
(0, import_firestore27.collection)(this.db, PROCEDURES_COLLECTION),
|
|
9304
|
+
(0, import_firestore27.where)((0, import_firestore27.documentId)(), "in", chunk)
|
|
9305
|
+
);
|
|
9306
|
+
const snapshot = await (0, import_firestore27.getDocs)(q);
|
|
9307
|
+
snapshot.forEach((doc36) => {
|
|
9308
|
+
fetchedProcedures.push(doc36.data());
|
|
9309
|
+
});
|
|
9310
|
+
}
|
|
9311
|
+
return fetchedProcedures;
|
|
9312
|
+
}
|
|
9173
9313
|
/**
|
|
9174
9314
|
* Gets a procedure by ID
|
|
9175
9315
|
* @param id - The ID of the procedure to get
|
package/dist/index.mjs
CHANGED
|
@@ -5499,9 +5499,6 @@ var PractitionerService = class extends BaseService {
|
|
|
5499
5499
|
if (!practitioner) {
|
|
5500
5500
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
5501
5501
|
}
|
|
5502
|
-
if (!practitioner.isActive) {
|
|
5503
|
-
throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
5504
|
-
}
|
|
5505
5502
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
5506
5503
|
if (!clinic) {
|
|
5507
5504
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -8903,9 +8900,11 @@ import {
|
|
|
8903
8900
|
setDoc as setDoc14,
|
|
8904
8901
|
deleteDoc as deleteDoc10,
|
|
8905
8902
|
serverTimestamp as serverTimestamp13,
|
|
8903
|
+
writeBatch as writeBatch6,
|
|
8906
8904
|
orderBy as orderBy6,
|
|
8907
8905
|
limit as limit9,
|
|
8908
|
-
startAfter as startAfter8
|
|
8906
|
+
startAfter as startAfter8,
|
|
8907
|
+
documentId as documentId2
|
|
8909
8908
|
} from "firebase/firestore";
|
|
8910
8909
|
|
|
8911
8910
|
// src/types/procedure/index.ts
|
|
@@ -9156,6 +9155,149 @@ var ProcedureService = class extends BaseService {
|
|
|
9156
9155
|
const savedDoc = await getDoc19(procedureRef);
|
|
9157
9156
|
return savedDoc.data();
|
|
9158
9157
|
}
|
|
9158
|
+
/**
|
|
9159
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
9160
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
9161
|
+
*
|
|
9162
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
9163
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
9164
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
9165
|
+
*/
|
|
9166
|
+
async bulkCreateProcedures(baseData, practitionerIds) {
|
|
9167
|
+
var _a;
|
|
9168
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
9169
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
9170
|
+
}
|
|
9171
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
9172
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
9173
|
+
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
9174
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
9175
|
+
this.subcategoryService.getById(
|
|
9176
|
+
validatedData.categoryId,
|
|
9177
|
+
validatedData.subcategoryId
|
|
9178
|
+
),
|
|
9179
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
9180
|
+
this.productService.getById(
|
|
9181
|
+
validatedData.technologyId,
|
|
9182
|
+
validatedData.productId
|
|
9183
|
+
),
|
|
9184
|
+
getDoc19(doc16(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
9185
|
+
]);
|
|
9186
|
+
if (!category || !subcategory || !technology || !product) {
|
|
9187
|
+
throw new Error("One or more required base entities not found");
|
|
9188
|
+
}
|
|
9189
|
+
if (!clinicSnapshot.exists()) {
|
|
9190
|
+
throw new Error(
|
|
9191
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
9192
|
+
);
|
|
9193
|
+
}
|
|
9194
|
+
const clinic = clinicSnapshot.data();
|
|
9195
|
+
let processedPhotos = [];
|
|
9196
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
9197
|
+
const batchId = this.generateId();
|
|
9198
|
+
processedPhotos = await this.processMediaArray(
|
|
9199
|
+
validatedData.photos,
|
|
9200
|
+
batchId,
|
|
9201
|
+
"procedure-photos-batch"
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
9204
|
+
const practitionersMap = /* @__PURE__ */ new Map();
|
|
9205
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
9206
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
9207
|
+
const practitionersQuery = query16(
|
|
9208
|
+
collection16(this.db, PRACTITIONERS_COLLECTION),
|
|
9209
|
+
where16(documentId2(), "in", chunk)
|
|
9210
|
+
);
|
|
9211
|
+
const practitionersSnapshot = await getDocs16(practitionersQuery);
|
|
9212
|
+
practitionersSnapshot.docs.forEach((doc36) => {
|
|
9213
|
+
practitionersMap.set(doc36.id, doc36.data());
|
|
9214
|
+
});
|
|
9215
|
+
}
|
|
9216
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
9217
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
9218
|
+
const notFoundIds = practitionerIds.filter(
|
|
9219
|
+
(id) => !foundIds.includes(id)
|
|
9220
|
+
);
|
|
9221
|
+
throw new Error(
|
|
9222
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
9223
|
+
);
|
|
9224
|
+
}
|
|
9225
|
+
const batch = writeBatch6(this.db);
|
|
9226
|
+
const createdProcedureIds = [];
|
|
9227
|
+
const clinicInfo = {
|
|
9228
|
+
id: clinicSnapshot.id,
|
|
9229
|
+
name: clinic.name,
|
|
9230
|
+
description: clinic.description || "",
|
|
9231
|
+
featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? typeof clinic.featuredPhotos[0] === "string" ? clinic.featuredPhotos[0] : "" : typeof clinic.coverPhoto === "string" ? clinic.coverPhoto : "",
|
|
9232
|
+
location: clinic.location,
|
|
9233
|
+
contactInfo: clinic.contactInfo
|
|
9234
|
+
};
|
|
9235
|
+
for (const practitionerId of practitionerIds) {
|
|
9236
|
+
const practitioner = practitionersMap.get(practitionerId);
|
|
9237
|
+
const doctorInfo = {
|
|
9238
|
+
id: practitioner.id,
|
|
9239
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
9240
|
+
description: practitioner.basicInfo.bio || "",
|
|
9241
|
+
photo: typeof practitioner.basicInfo.profileImageUrl === "string" ? practitioner.basicInfo.profileImageUrl : "",
|
|
9242
|
+
rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
|
|
9243
|
+
services: practitioner.procedures || []
|
|
9244
|
+
};
|
|
9245
|
+
const procedureId = this.generateId();
|
|
9246
|
+
createdProcedureIds.push(procedureId);
|
|
9247
|
+
const procedureRef = doc16(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
9248
|
+
const newProcedure = {
|
|
9249
|
+
id: procedureId,
|
|
9250
|
+
...validatedData,
|
|
9251
|
+
practitionerId,
|
|
9252
|
+
// Override practitionerId with the correct one
|
|
9253
|
+
photos: processedPhotos,
|
|
9254
|
+
category,
|
|
9255
|
+
subcategory,
|
|
9256
|
+
technology,
|
|
9257
|
+
product,
|
|
9258
|
+
blockingConditions: technology.blockingConditions,
|
|
9259
|
+
contraindications: technology.contraindications || [],
|
|
9260
|
+
treatmentBenefits: technology.benefits,
|
|
9261
|
+
preRequirements: technology.requirements.pre,
|
|
9262
|
+
postRequirements: technology.requirements.post,
|
|
9263
|
+
certificationRequirement: technology.certificationRequirement,
|
|
9264
|
+
documentationTemplates: (technology == null ? void 0 : technology.documentationTemplates) || [],
|
|
9265
|
+
clinicInfo,
|
|
9266
|
+
doctorInfo,
|
|
9267
|
+
// Set specific doctor info
|
|
9268
|
+
reviewInfo: {
|
|
9269
|
+
totalReviews: 0,
|
|
9270
|
+
averageRating: 0,
|
|
9271
|
+
effectivenessOfTreatment: 0,
|
|
9272
|
+
outcomeExplanation: 0,
|
|
9273
|
+
painManagement: 0,
|
|
9274
|
+
followUpCare: 0,
|
|
9275
|
+
valueForMoney: 0,
|
|
9276
|
+
recommendationPercentage: 0
|
|
9277
|
+
},
|
|
9278
|
+
isActive: true
|
|
9279
|
+
};
|
|
9280
|
+
batch.set(procedureRef, {
|
|
9281
|
+
...newProcedure,
|
|
9282
|
+
createdAt: serverTimestamp13(),
|
|
9283
|
+
updatedAt: serverTimestamp13()
|
|
9284
|
+
});
|
|
9285
|
+
}
|
|
9286
|
+
await batch.commit();
|
|
9287
|
+
const fetchedProcedures = [];
|
|
9288
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
9289
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
9290
|
+
const q = query16(
|
|
9291
|
+
collection16(this.db, PROCEDURES_COLLECTION),
|
|
9292
|
+
where16(documentId2(), "in", chunk)
|
|
9293
|
+
);
|
|
9294
|
+
const snapshot = await getDocs16(q);
|
|
9295
|
+
snapshot.forEach((doc36) => {
|
|
9296
|
+
fetchedProcedures.push(doc36.data());
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
return fetchedProcedures;
|
|
9300
|
+
}
|
|
9159
9301
|
/**
|
|
9160
9302
|
* Gets a procedure by ID
|
|
9161
9303
|
* @param id - The ID of the procedure to get
|
package/package.json
CHANGED
|
@@ -1228,9 +1228,10 @@ export class PractitionerService extends BaseService {
|
|
|
1228
1228
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1231
|
+
// No need to check for is practitioner active
|
|
1232
|
+
// if (!practitioner.isActive) {
|
|
1233
|
+
// throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
1234
|
+
// }
|
|
1234
1235
|
|
|
1235
1236
|
// Validate that clinic exists
|
|
1236
1237
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
@@ -304,6 +304,187 @@ export class ProcedureService extends BaseService {
|
|
|
304
304
|
return savedDoc.data() as Procedure;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
309
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
310
|
+
*
|
|
311
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
312
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
313
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
314
|
+
*/
|
|
315
|
+
async bulkCreateProcedures(
|
|
316
|
+
baseData: Omit<CreateProcedureData, "practitionerId">,
|
|
317
|
+
practitionerIds: string[]
|
|
318
|
+
): Promise<Procedure[]> {
|
|
319
|
+
// 1. Validation
|
|
320
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
321
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Add a dummy practitionerId for the validation schema to pass
|
|
325
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
326
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
327
|
+
|
|
328
|
+
// 2. Fetch common data once to avoid redundant reads
|
|
329
|
+
const [category, subcategory, technology, product, clinicSnapshot] =
|
|
330
|
+
await Promise.all([
|
|
331
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
332
|
+
this.subcategoryService.getById(
|
|
333
|
+
validatedData.categoryId,
|
|
334
|
+
validatedData.subcategoryId
|
|
335
|
+
),
|
|
336
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
337
|
+
this.productService.getById(
|
|
338
|
+
validatedData.technologyId,
|
|
339
|
+
validatedData.productId
|
|
340
|
+
),
|
|
341
|
+
getDoc(doc(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId)),
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
if (!category || !subcategory || !technology || !product) {
|
|
345
|
+
throw new Error("One or more required base entities not found");
|
|
346
|
+
}
|
|
347
|
+
if (!clinicSnapshot.exists()) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const clinic = clinicSnapshot.data() as Clinic;
|
|
353
|
+
|
|
354
|
+
// 3. Handle media uploads once for efficiency
|
|
355
|
+
let processedPhotos: string[] = [];
|
|
356
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
357
|
+
const batchId = this.generateId(); // Use a single ID for all media in this batch
|
|
358
|
+
processedPhotos = await this.processMediaArray(
|
|
359
|
+
validatedData.photos,
|
|
360
|
+
batchId,
|
|
361
|
+
"procedure-photos-batch"
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 4. Fetch all practitioner data efficiently
|
|
366
|
+
const practitionersMap = new Map<string, Practitioner>();
|
|
367
|
+
// Use 'in' query in chunks of 30, as this is the Firestore limit
|
|
368
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
369
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
370
|
+
const practitionersQuery = query(
|
|
371
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
372
|
+
where(documentId(), "in", chunk)
|
|
373
|
+
);
|
|
374
|
+
const practitionersSnapshot = await getDocs(practitionersQuery);
|
|
375
|
+
practitionersSnapshot.docs.forEach((doc) => {
|
|
376
|
+
practitionersMap.set(doc.id, doc.data() as Practitioner);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Verify all practitioners were found
|
|
381
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
382
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
383
|
+
const notFoundIds = practitionerIds.filter(
|
|
384
|
+
(id) => !foundIds.includes(id)
|
|
385
|
+
);
|
|
386
|
+
throw new Error(
|
|
387
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 5. Use a Firestore batch for atomic creation
|
|
392
|
+
const batch = writeBatch(this.db);
|
|
393
|
+
const createdProcedureIds: string[] = [];
|
|
394
|
+
const clinicInfo = {
|
|
395
|
+
id: clinicSnapshot.id,
|
|
396
|
+
name: clinic.name,
|
|
397
|
+
description: clinic.description || "",
|
|
398
|
+
featuredPhoto:
|
|
399
|
+
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
400
|
+
? typeof clinic.featuredPhotos[0] === "string"
|
|
401
|
+
? clinic.featuredPhotos[0]
|
|
402
|
+
: ""
|
|
403
|
+
: typeof clinic.coverPhoto === "string"
|
|
404
|
+
? clinic.coverPhoto
|
|
405
|
+
: "",
|
|
406
|
+
location: clinic.location,
|
|
407
|
+
contactInfo: clinic.contactInfo,
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
for (const practitionerId of practitionerIds) {
|
|
411
|
+
const practitioner = practitionersMap.get(practitionerId)!;
|
|
412
|
+
|
|
413
|
+
const doctorInfo = {
|
|
414
|
+
id: practitioner.id,
|
|
415
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
416
|
+
description: practitioner.basicInfo.bio || "",
|
|
417
|
+
photo:
|
|
418
|
+
typeof practitioner.basicInfo.profileImageUrl === "string"
|
|
419
|
+
? practitioner.basicInfo.profileImageUrl
|
|
420
|
+
: "",
|
|
421
|
+
rating: practitioner.reviewInfo?.averageRating || 0,
|
|
422
|
+
services: practitioner.procedures || [],
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const procedureId = this.generateId();
|
|
426
|
+
createdProcedureIds.push(procedureId);
|
|
427
|
+
const procedureRef = doc(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
428
|
+
|
|
429
|
+
// Construct the new procedure, reusing common data
|
|
430
|
+
const newProcedure: Omit<Procedure, "createdAt" | "updatedAt"> = {
|
|
431
|
+
id: procedureId,
|
|
432
|
+
...validatedData,
|
|
433
|
+
practitionerId: practitionerId, // Override practitionerId with the correct one
|
|
434
|
+
photos: processedPhotos,
|
|
435
|
+
category,
|
|
436
|
+
subcategory,
|
|
437
|
+
technology,
|
|
438
|
+
product,
|
|
439
|
+
blockingConditions: technology.blockingConditions,
|
|
440
|
+
contraindications: technology.contraindications || [],
|
|
441
|
+
treatmentBenefits: technology.benefits,
|
|
442
|
+
preRequirements: technology.requirements.pre,
|
|
443
|
+
postRequirements: technology.requirements.post,
|
|
444
|
+
certificationRequirement: technology.certificationRequirement,
|
|
445
|
+
documentationTemplates: technology?.documentationTemplates || [],
|
|
446
|
+
clinicInfo,
|
|
447
|
+
doctorInfo, // Set specific doctor info
|
|
448
|
+
reviewInfo: {
|
|
449
|
+
totalReviews: 0,
|
|
450
|
+
averageRating: 0,
|
|
451
|
+
effectivenessOfTreatment: 0,
|
|
452
|
+
outcomeExplanation: 0,
|
|
453
|
+
painManagement: 0,
|
|
454
|
+
followUpCare: 0,
|
|
455
|
+
valueForMoney: 0,
|
|
456
|
+
recommendationPercentage: 0,
|
|
457
|
+
},
|
|
458
|
+
isActive: true,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
batch.set(procedureRef, {
|
|
462
|
+
...newProcedure,
|
|
463
|
+
createdAt: serverTimestamp(),
|
|
464
|
+
updatedAt: serverTimestamp(),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// 6. Commit the atomic batch write
|
|
469
|
+
await batch.commit();
|
|
470
|
+
|
|
471
|
+
// 7. Fetch and return the newly created procedures
|
|
472
|
+
const fetchedProcedures: Procedure[] = [];
|
|
473
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
474
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
475
|
+
const q = query(
|
|
476
|
+
collection(this.db, PROCEDURES_COLLECTION),
|
|
477
|
+
where(documentId(), "in", chunk)
|
|
478
|
+
);
|
|
479
|
+
const snapshot = await getDocs(q);
|
|
480
|
+
snapshot.forEach((doc) => {
|
|
481
|
+
fetchedProcedures.push(doc.data() as Procedure);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return fetchedProcedures;
|
|
486
|
+
}
|
|
487
|
+
|
|
307
488
|
/**
|
|
308
489
|
* Gets a procedure by ID
|
|
309
490
|
* @param id - The ID of the procedure to get
|