@blackcode_sa/metaestetics-api 1.12.45 → 1.12.47
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 +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +3 -26
- package/dist/admin/index.mjs +3 -26
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +53 -48
- package/dist/index.mjs +53 -48
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +21 -17
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +4 -31
- package/src/services/appointment/utils/appointment.utils.ts +2 -2
- package/src/services/appointment/utils/extended-procedure.utils.ts +82 -53
- package/src/services/appointment/utils/recommended-procedure.utils.ts +8 -6
- package/src/services/appointment/utils/zone-management.utils.ts +7 -7
- package/src/services/practitioner/practitioner.service.ts +2 -10
- package/src/services/procedure/procedure.service.ts +22 -22
- package/src/types/procedure/index.ts +5 -5
- package/src/validations/appointment.schema.ts +60 -53
- package/src/validations/procedure.schema.ts +4 -4
package/dist/index.mjs
CHANGED
|
@@ -529,7 +529,8 @@ var recommendedProcedureTimeframeSchema = z3.object({
|
|
|
529
529
|
});
|
|
530
530
|
var recommendedProcedureSchema = z3.object({
|
|
531
531
|
procedure: extendedProcedureInfoSchema,
|
|
532
|
-
note: z3.string().
|
|
532
|
+
note: z3.string().max(MAX_STRING_LENGTH_LONG, "Note too long"),
|
|
533
|
+
// Note is now optional (no min length)
|
|
533
534
|
timeframe: recommendedProcedureTimeframeSchema
|
|
534
535
|
});
|
|
535
536
|
var appointmentMetadataSchema = z3.object({
|
|
@@ -1452,7 +1453,7 @@ function calculateItemSubtotal(item) {
|
|
|
1452
1453
|
const price = item.price || 0;
|
|
1453
1454
|
return price * quantity;
|
|
1454
1455
|
}
|
|
1455
|
-
function calculateFinalBilling(zonesData, taxRate = 0.
|
|
1456
|
+
function calculateFinalBilling(zonesData, taxRate = 0.081) {
|
|
1456
1457
|
let subtotalAll = 0;
|
|
1457
1458
|
Object.values(zonesData).forEach((items) => {
|
|
1458
1459
|
items.forEach((item) => {
|
|
@@ -1516,7 +1517,7 @@ async function addItemToZoneUtil(db, appointmentId, zoneId, item) {
|
|
|
1516
1517
|
updatedAt: now
|
|
1517
1518
|
};
|
|
1518
1519
|
zonesData[zoneId].push(itemWithSubtotal);
|
|
1519
|
-
const finalbilling = calculateFinalBilling(zonesData);
|
|
1520
|
+
const finalbilling = calculateFinalBilling(zonesData, 0.081);
|
|
1520
1521
|
const appointmentRef = doc3(db, APPOINTMENTS_COLLECTION, appointmentId);
|
|
1521
1522
|
await updateDoc3(appointmentRef, {
|
|
1522
1523
|
"metadata.zonesData": zonesData,
|
|
@@ -1540,7 +1541,7 @@ async function removeItemFromZoneUtil(db, appointmentId, zoneId, itemIndex) {
|
|
|
1540
1541
|
if (items.length === 0) {
|
|
1541
1542
|
delete metadata.zonesData[zoneId];
|
|
1542
1543
|
}
|
|
1543
|
-
const finalbilling = calculateFinalBilling(metadata.zonesData);
|
|
1544
|
+
const finalbilling = calculateFinalBilling(metadata.zonesData, 0.081);
|
|
1544
1545
|
const appointmentRef = doc3(db, APPOINTMENTS_COLLECTION, appointmentId);
|
|
1545
1546
|
await updateDoc3(appointmentRef, {
|
|
1546
1547
|
"metadata.zonesData": metadata.zonesData,
|
|
@@ -1577,7 +1578,7 @@ async function updateZoneItemUtil(db, appointmentId, zoneId, itemIndex, updates)
|
|
|
1577
1578
|
itemIndex,
|
|
1578
1579
|
newSubtotal: items[itemIndex].subtotal
|
|
1579
1580
|
});
|
|
1580
|
-
const finalbilling = calculateFinalBilling(metadata.zonesData);
|
|
1581
|
+
const finalbilling = calculateFinalBilling(metadata.zonesData, 0.081);
|
|
1581
1582
|
const appointmentRef = doc3(db, APPOINTMENTS_COLLECTION, appointmentId);
|
|
1582
1583
|
await updateDoc3(appointmentRef, {
|
|
1583
1584
|
"metadata.zonesData": metadata.zonesData,
|
|
@@ -1739,7 +1740,7 @@ async function aggregateProductsFromProcedure(db, procedureId, existingProducts)
|
|
|
1739
1740
|
}
|
|
1740
1741
|
const procedureData = procedureSnap.data();
|
|
1741
1742
|
const productsMetadata = procedureData.productsMetadata || [];
|
|
1742
|
-
const newProducts = productsMetadata.map((pp) => {
|
|
1743
|
+
const newProducts = productsMetadata.filter((pp) => pp && pp.product).map((pp) => {
|
|
1743
1744
|
const product = pp.product;
|
|
1744
1745
|
return {
|
|
1745
1746
|
productId: product.id,
|
|
@@ -1792,7 +1793,7 @@ async function createExtendedProcedureInfo(db, procedureId) {
|
|
|
1792
1793
|
// Access embedded technology
|
|
1793
1794
|
procedureTechnologyName: data.technology.name,
|
|
1794
1795
|
// Access embedded technology
|
|
1795
|
-
procedureProducts: (data.productsMetadata || []).map((pp) => ({
|
|
1796
|
+
procedureProducts: (data.productsMetadata || []).filter((pp) => pp && pp.product).map((pp) => ({
|
|
1796
1797
|
productId: pp.product.id,
|
|
1797
1798
|
// Access embedded product
|
|
1798
1799
|
productName: pp.product.name,
|
|
@@ -1808,9 +1809,7 @@ async function addExtendedProcedureUtil(db, appointmentId, procedureId) {
|
|
|
1808
1809
|
var _a;
|
|
1809
1810
|
const appointment = await getAppointmentOrThrow(db, appointmentId);
|
|
1810
1811
|
const metadata = initializeMetadata(appointment);
|
|
1811
|
-
const existingProcedure = (_a = metadata.extendedProcedures) == null ? void 0 : _a.find(
|
|
1812
|
-
(p) => p.procedureId === procedureId
|
|
1813
|
-
);
|
|
1812
|
+
const existingProcedure = (_a = metadata.extendedProcedures) == null ? void 0 : _a.find((p) => p.procedureId === procedureId);
|
|
1814
1813
|
if (existingProcedure) {
|
|
1815
1814
|
throw new Error(`Procedure ${procedureId} is already added to this appointment`);
|
|
1816
1815
|
}
|
|
@@ -1841,7 +1840,10 @@ async function addExtendedProcedureUtil(db, appointmentId, procedureId) {
|
|
|
1841
1840
|
);
|
|
1842
1841
|
updatedLinkedFormIds = [...updatedLinkedFormIds, ...formInitResult.allLinkedFormIds];
|
|
1843
1842
|
updatedLinkedForms = [...updatedLinkedForms, ...formInitResult.initializedFormsInfo];
|
|
1844
|
-
updatedPendingUserFormsIds = [
|
|
1843
|
+
updatedPendingUserFormsIds = [
|
|
1844
|
+
...updatedPendingUserFormsIds,
|
|
1845
|
+
...formInitResult.pendingUserFormsIds
|
|
1846
|
+
];
|
|
1845
1847
|
}
|
|
1846
1848
|
const extendedProcedures = [...metadata.extendedProcedures || [], extendedProcedureInfo];
|
|
1847
1849
|
const appointmentRef = doc5(db, APPOINTMENTS_COLLECTION, appointmentId);
|
|
@@ -1861,9 +1863,7 @@ async function removeExtendedProcedureUtil(db, appointmentId, procedureId) {
|
|
|
1861
1863
|
if (!metadata.extendedProcedures || metadata.extendedProcedures.length === 0) {
|
|
1862
1864
|
throw new Error("No extended procedures found for this appointment");
|
|
1863
1865
|
}
|
|
1864
|
-
const procedureIndex = metadata.extendedProcedures.findIndex(
|
|
1865
|
-
(p) => p.procedureId === procedureId
|
|
1866
|
-
);
|
|
1866
|
+
const procedureIndex = metadata.extendedProcedures.findIndex((p) => p.procedureId === procedureId);
|
|
1867
1867
|
if (procedureIndex === -1) {
|
|
1868
1868
|
throw new Error(`Extended procedure ${procedureId} not found in this appointment`);
|
|
1869
1869
|
}
|
|
@@ -1871,6 +1871,20 @@ async function removeExtendedProcedureUtil(db, appointmentId, procedureId) {
|
|
|
1871
1871
|
const updatedProducts = (metadata.appointmentProducts || []).filter(
|
|
1872
1872
|
(p) => p.procedureId !== procedureId
|
|
1873
1873
|
);
|
|
1874
|
+
const updatedZonesData = { ...metadata.zonesData || {} };
|
|
1875
|
+
let productsRemovedFromZones = 0;
|
|
1876
|
+
Object.keys(updatedZonesData).forEach((zoneId) => {
|
|
1877
|
+
const originalLength = updatedZonesData[zoneId].length;
|
|
1878
|
+
updatedZonesData[zoneId] = updatedZonesData[zoneId].filter((item) => {
|
|
1879
|
+
if (item.type === "note") return true;
|
|
1880
|
+
if (item.type === "item" && item.belongingProcedureId !== procedureId) return true;
|
|
1881
|
+
return false;
|
|
1882
|
+
});
|
|
1883
|
+
productsRemovedFromZones += originalLength - updatedZonesData[zoneId].length;
|
|
1884
|
+
});
|
|
1885
|
+
console.log(
|
|
1886
|
+
`\u{1F5D1}\uFE0F [removeExtendedProcedure] Removed ${productsRemovedFromZones} products from zones for procedure ${procedureId}`
|
|
1887
|
+
);
|
|
1874
1888
|
const removedFormIds = await removeFormsForExtendedProcedure(db, appointmentId, procedureId);
|
|
1875
1889
|
const updatedLinkedFormIds = (appointment.linkedFormIds || []).filter(
|
|
1876
1890
|
(formId) => !removedFormIds.includes(formId)
|
|
@@ -1885,6 +1899,7 @@ async function removeExtendedProcedureUtil(db, appointmentId, procedureId) {
|
|
|
1885
1899
|
await updateDoc4(appointmentRef, {
|
|
1886
1900
|
"metadata.extendedProcedures": metadata.extendedProcedures,
|
|
1887
1901
|
"metadata.appointmentProducts": updatedProducts,
|
|
1902
|
+
"metadata.zonesData": updatedZonesData,
|
|
1888
1903
|
linkedFormIds: updatedLinkedFormIds,
|
|
1889
1904
|
linkedForms: updatedLinkedForms,
|
|
1890
1905
|
pendingUserFormsIds: updatedPendingUserFormsIds,
|
|
@@ -1923,7 +1938,7 @@ async function createExtendedProcedureInfoForRecommended(db, procedureId) {
|
|
|
1923
1938
|
procedureSubCategoryName: data.subcategory.name,
|
|
1924
1939
|
procedureTechnologyId: data.technology.id,
|
|
1925
1940
|
procedureTechnologyName: data.technology.name,
|
|
1926
|
-
procedureProducts: (data.productsMetadata || []).map((pp) => ({
|
|
1941
|
+
procedureProducts: (data.productsMetadata || []).filter((pp) => pp && pp.product).map((pp) => ({
|
|
1927
1942
|
productId: pp.product.id,
|
|
1928
1943
|
productName: pp.product.name,
|
|
1929
1944
|
brandId: pp.product.brandId,
|
|
@@ -8747,15 +8762,8 @@ var PractitionerService = class extends BaseService {
|
|
|
8747
8762
|
price: 0,
|
|
8748
8763
|
currency: "EUR" /* EUR */,
|
|
8749
8764
|
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
8750
|
-
productsMetadata:
|
|
8751
|
-
|
|
8752
|
-
productId: "free-consultation-product",
|
|
8753
|
-
price: 0,
|
|
8754
|
-
currency: "EUR" /* EUR */,
|
|
8755
|
-
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
8756
|
-
isDefault: true
|
|
8757
|
-
}
|
|
8758
|
-
],
|
|
8765
|
+
productsMetadata: void 0,
|
|
8766
|
+
// No products needed for consultations
|
|
8759
8767
|
duration: 30,
|
|
8760
8768
|
// 30 minutes consultation
|
|
8761
8769
|
practitionerId,
|
|
@@ -16562,11 +16570,11 @@ var createProcedureSchema = z26.object({
|
|
|
16562
16570
|
categoryId: z26.string().min(1),
|
|
16563
16571
|
subcategoryId: z26.string().min(1),
|
|
16564
16572
|
technologyId: z26.string().min(1),
|
|
16565
|
-
productId: z26.string().min(1),
|
|
16573
|
+
productId: z26.string().min(1).optional(),
|
|
16566
16574
|
price: z26.number().min(0),
|
|
16567
16575
|
currency: z26.nativeEnum(Currency),
|
|
16568
16576
|
pricingMeasure: z26.nativeEnum(PricingMeasure),
|
|
16569
|
-
productsMetadata: z26.array(procedureProductDataSchema).min(1),
|
|
16577
|
+
productsMetadata: z26.array(procedureProductDataSchema).min(1).optional(),
|
|
16570
16578
|
duration: z26.number().min(1).max(480),
|
|
16571
16579
|
// Max 8 hours
|
|
16572
16580
|
practitionerId: z26.string().min(1),
|
|
@@ -16603,10 +16611,10 @@ var procedureSchema = z26.object({
|
|
|
16603
16611
|
// We'll validate the full subcategory object separately
|
|
16604
16612
|
technology: z26.any(),
|
|
16605
16613
|
// We'll validate the full technology object separately
|
|
16606
|
-
product: z26.any(),
|
|
16607
|
-
// We'll validate the full product object separately
|
|
16608
|
-
productsMetadata: z26.array(storedProcedureProductSchema).
|
|
16609
|
-
// Use stored format schema
|
|
16614
|
+
product: z26.any().optional(),
|
|
16615
|
+
// We'll validate the full product object separately (optional for consultations)
|
|
16616
|
+
productsMetadata: z26.array(storedProcedureProductSchema).optional(),
|
|
16617
|
+
// Use stored format schema (optional for consultations)
|
|
16610
16618
|
price: z26.number().min(0),
|
|
16611
16619
|
currency: z26.nativeEnum(Currency),
|
|
16612
16620
|
pricingMeasure: z26.nativeEnum(PricingMeasure),
|
|
@@ -16699,11 +16707,14 @@ var ProcedureService = class extends BaseService {
|
|
|
16699
16707
|
}
|
|
16700
16708
|
/**
|
|
16701
16709
|
* Transforms validated procedure product data (with productId) to ProcedureProduct objects (with full product)
|
|
16702
|
-
* @param productsMetadata Array of validated procedure product data
|
|
16710
|
+
* @param productsMetadata Array of validated procedure product data (optional)
|
|
16703
16711
|
* @param technologyId Technology ID to fetch products from
|
|
16704
16712
|
* @returns Array of ProcedureProduct objects with full product information
|
|
16705
16713
|
*/
|
|
16706
16714
|
async transformProductsMetadata(productsMetadata, technologyId) {
|
|
16715
|
+
if (!productsMetadata || productsMetadata.length === 0) {
|
|
16716
|
+
return [];
|
|
16717
|
+
}
|
|
16707
16718
|
const transformedProducts = [];
|
|
16708
16719
|
for (const productData of productsMetadata) {
|
|
16709
16720
|
const product = await this.productService.getById(technologyId, productData.productId);
|
|
@@ -16730,12 +16741,16 @@ var ProcedureService = class extends BaseService {
|
|
|
16730
16741
|
async createProcedure(data) {
|
|
16731
16742
|
var _a, _b, _c;
|
|
16732
16743
|
const validatedData = createProcedureSchema.parse(data);
|
|
16744
|
+
if (!validatedData.productId) {
|
|
16745
|
+
throw new Error("productId is required for regular procedures. Use createConsultationProcedure for product-free procedures.");
|
|
16746
|
+
}
|
|
16733
16747
|
const procedureId = this.generateId();
|
|
16734
16748
|
const [category, subcategory, technology, product] = await Promise.all([
|
|
16735
16749
|
this.categoryService.getById(validatedData.categoryId),
|
|
16736
16750
|
this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
|
|
16737
16751
|
this.technologyService.getById(validatedData.technologyId),
|
|
16738
16752
|
this.productService.getById(validatedData.technologyId, validatedData.productId)
|
|
16753
|
+
// Safe: validated above
|
|
16739
16754
|
]);
|
|
16740
16755
|
if (!category || !subcategory || !technology || !product) {
|
|
16741
16756
|
throw new Error("One or more required base entities not found");
|
|
@@ -16844,6 +16859,9 @@ var ProcedureService = class extends BaseService {
|
|
|
16844
16859
|
if (!practitionerIds || practitionerIds.length === 0) {
|
|
16845
16860
|
throw new Error("Practitioner IDs array cannot be empty.");
|
|
16846
16861
|
}
|
|
16862
|
+
if (!baseData.productId) {
|
|
16863
|
+
throw new Error("productId is required for regular procedures. Use createConsultationProcedure for product-free procedures.");
|
|
16864
|
+
}
|
|
16847
16865
|
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
16848
16866
|
const validatedData = createProcedureSchema.parse(validationData);
|
|
16849
16867
|
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
@@ -16851,6 +16869,7 @@ var ProcedureService = class extends BaseService {
|
|
|
16851
16869
|
this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
|
|
16852
16870
|
this.technologyService.getById(validatedData.technologyId),
|
|
16853
16871
|
this.productService.getById(validatedData.technologyId, validatedData.productId),
|
|
16872
|
+
// Safe: validated above
|
|
16854
16873
|
getDoc37(doc36(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
16855
16874
|
]);
|
|
16856
16875
|
if (!category || !subcategory || !technology || !product) {
|
|
@@ -17711,20 +17730,6 @@ var ProcedureService = class extends BaseService {
|
|
|
17711
17730
|
rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
|
|
17712
17731
|
services: practitioner.procedures || []
|
|
17713
17732
|
};
|
|
17714
|
-
const consultationProduct = {
|
|
17715
|
-
id: "consultation-no-product",
|
|
17716
|
-
name: "No Product Required",
|
|
17717
|
-
description: "Consultation procedures do not require specific products",
|
|
17718
|
-
brandId: "consultation-brand",
|
|
17719
|
-
brandName: "Consultation",
|
|
17720
|
-
technologyId: data.technologyId,
|
|
17721
|
-
technologyName: technology.name,
|
|
17722
|
-
categoryId: technology.categoryId,
|
|
17723
|
-
subcategoryId: technology.subcategoryId,
|
|
17724
|
-
isActive: true,
|
|
17725
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
17726
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
17727
|
-
};
|
|
17728
17733
|
const { productsMetadata: _, ...dataWithoutProductsMetadata } = data;
|
|
17729
17734
|
const newProcedure = {
|
|
17730
17735
|
id: procedureId,
|
|
@@ -17734,10 +17739,10 @@ var ProcedureService = class extends BaseService {
|
|
|
17734
17739
|
category,
|
|
17735
17740
|
subcategory,
|
|
17736
17741
|
technology,
|
|
17737
|
-
product:
|
|
17738
|
-
//
|
|
17742
|
+
product: void 0,
|
|
17743
|
+
// No product needed for consultations
|
|
17739
17744
|
productsMetadata: transformedProductsMetadata,
|
|
17740
|
-
//
|
|
17745
|
+
// Empty array for consultations
|
|
17741
17746
|
blockingConditions: technology.blockingConditions,
|
|
17742
17747
|
contraindications: technology.contraindications || [],
|
|
17743
17748
|
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
package/package.json
CHANGED
|
@@ -1004,29 +1004,33 @@ export class BookingAdmin {
|
|
|
1004
1004
|
procedureTechnologyName: procedureTechnology?.name || "",
|
|
1005
1005
|
procedureProductBrandId: procedureProduct?.brandId || "",
|
|
1006
1006
|
procedureProductBrandName: procedureProduct?.brandName || "",
|
|
1007
|
-
procedureProducts: productsMetadata
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1007
|
+
procedureProducts: productsMetadata
|
|
1008
|
+
.filter((pp: any) => pp && pp.product) // Safety check for product-free procedures
|
|
1009
|
+
.map((pp: any) => ({
|
|
1010
|
+
productId: pp.product.id,
|
|
1011
|
+
productName: pp.product.name,
|
|
1012
|
+
brandId: pp.product.brandId,
|
|
1013
|
+
brandName: pp.product.brandName,
|
|
1014
|
+
})),
|
|
1013
1015
|
};
|
|
1014
1016
|
}
|
|
1015
1017
|
|
|
1016
1018
|
private _generateAppointmentProductsFromProcedure(procedure: Procedure): any[] {
|
|
1017
1019
|
const productsMetadata = procedure.productsMetadata || [];
|
|
1018
1020
|
|
|
1019
|
-
return productsMetadata
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1021
|
+
return productsMetadata
|
|
1022
|
+
.filter((pp: any) => pp && pp.product) // Safety check for product-free procedures
|
|
1023
|
+
.map((pp: any) => {
|
|
1024
|
+
const product = pp.product;
|
|
1025
|
+
return {
|
|
1026
|
+
productId: product.id,
|
|
1027
|
+
productName: product.name,
|
|
1028
|
+
brandId: product.brandId,
|
|
1029
|
+
brandName: product.brandName,
|
|
1030
|
+
procedureId: procedure.id,
|
|
1031
|
+
price: pp.price,
|
|
1032
|
+
currency: pp.currency,
|
|
1033
|
+
unitOfMeasurement: pp.pricingMeasure,
|
|
1030
1034
|
};
|
|
1031
1035
|
});
|
|
1032
1036
|
}
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
import { CATEGORIES_COLLECTION } from '../../backoffice/types/category.types';
|
|
8
8
|
import { SUBCATEGORIES_COLLECTION } from '../../backoffice/types/subcategory.types';
|
|
9
9
|
import { TECHNOLOGIES_COLLECTION } from '../../backoffice/types/technology.types';
|
|
10
|
-
import { PRODUCTS_COLLECTION } from '../../backoffice/types/product.types';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Ensures that the free consultation infrastructure exists in the database
|
|
14
|
-
* Creates category, subcategory,
|
|
13
|
+
* Creates category, subcategory, and technology if they don't exist
|
|
14
|
+
* Note: Consultations are product-free procedures, so no product is created
|
|
15
15
|
* @param db - Firestore database instance (optional, defaults to admin.firestore())
|
|
16
16
|
* @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
|
|
17
17
|
*/
|
|
@@ -54,7 +54,7 @@ export async function freeConsultationInfrastructure(
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Creates the complete free consultation infrastructure
|
|
57
|
-
* Creates category, subcategory, technology
|
|
57
|
+
* Creates category, subcategory, and technology (no product needed)
|
|
58
58
|
* @param db - Firestore database instance
|
|
59
59
|
*/
|
|
60
60
|
async function createFreeConsultationInfrastructure(db: admin.firestore.Firestore): Promise<void> {
|
|
@@ -125,33 +125,6 @@ async function createFreeConsultationInfrastructure(db: admin.firestore.Firestor
|
|
|
125
125
|
};
|
|
126
126
|
batch.set(technologyRef, technologyData);
|
|
127
127
|
|
|
128
|
-
// 4. Create Product: "free-consultation-product"
|
|
129
|
-
const productRef = db
|
|
130
|
-
.collection(TECHNOLOGIES_COLLECTION)
|
|
131
|
-
.doc('free-consultation-tech')
|
|
132
|
-
.collection(PRODUCTS_COLLECTION)
|
|
133
|
-
.doc('free-consultation-product');
|
|
134
|
-
|
|
135
|
-
const productData = {
|
|
136
|
-
id: 'free-consultation-product',
|
|
137
|
-
name: 'Free Consultation Service',
|
|
138
|
-
description: 'No physical product required for consultation services',
|
|
139
|
-
brandId: 'consultation-brand',
|
|
140
|
-
brandName: 'Consultation Services',
|
|
141
|
-
technologyId: 'free-consultation-tech',
|
|
142
|
-
technologyName: 'Free Consultation Technology',
|
|
143
|
-
categoryId: 'consultation',
|
|
144
|
-
subcategoryId: 'free-consultation',
|
|
145
|
-
isActive: true,
|
|
146
|
-
createdAt: now,
|
|
147
|
-
updatedAt: now,
|
|
148
|
-
technicalDetails: 'Virtual consultation service - no physical product required',
|
|
149
|
-
warnings: [],
|
|
150
|
-
indications: ['Initial patient assessment', 'Treatment planning', 'Patient education'],
|
|
151
|
-
contraindications: [],
|
|
152
|
-
};
|
|
153
|
-
batch.set(productRef, productData);
|
|
154
|
-
|
|
155
128
|
// Commit all changes atomically
|
|
156
129
|
await batch.commit();
|
|
157
130
|
|
|
@@ -159,5 +132,5 @@ async function createFreeConsultationInfrastructure(db: admin.firestore.Firestor
|
|
|
159
132
|
console.log(' ✓ Category: consultation');
|
|
160
133
|
console.log(' ✓ Subcategory: free-consultation');
|
|
161
134
|
console.log(' ✓ Technology: free-consultation-tech');
|
|
162
|
-
console.log('
|
|
135
|
+
console.log(' ℹ No product needed - consultations are product-free procedures');
|
|
163
136
|
}
|
|
@@ -135,8 +135,8 @@ export async function fetchAggregatedInfoUtil(
|
|
|
135
135
|
categoryName: procedureData.category?.name || '',
|
|
136
136
|
subcategoryName: procedureData.subcategory?.name || '',
|
|
137
137
|
technologyName: procedureData.technology?.name || '',
|
|
138
|
-
brandName: procedureData.product?.
|
|
139
|
-
productName: procedureData.product?.name || '',
|
|
138
|
+
brandName: procedureData.product?.brandName || '', // Safe: optional chaining
|
|
139
|
+
productName: procedureData.product?.name || '', // Safe: optional chaining
|
|
140
140
|
price: procedureData.price || 0,
|
|
141
141
|
pricingMeasure: procedureData.pricingMeasure,
|
|
142
142
|
currency: procedureData.currency,
|