@blackcode_sa/metaestetics-api 1.12.43 → 1.12.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/admin/index.d.mts +6 -12
- package/dist/admin/index.d.ts +6 -12
- package/dist/backoffice/index.d.mts +18 -275
- package/dist/backoffice/index.d.ts +18 -275
- package/dist/backoffice/index.js +14 -802
- package/dist/backoffice/index.mjs +38 -830
- package/dist/index.d.mts +10 -255
- package/dist/index.d.ts +10 -255
- package/dist/index.js +32 -750
- package/dist/index.mjs +40 -761
- package/package.json +1 -1
- package/src/backoffice/services/brand.service.ts +0 -86
- package/src/backoffice/services/category.service.ts +0 -84
- package/src/backoffice/services/constants.service.ts +0 -77
- package/src/backoffice/services/product.service.ts +18 -316
- package/src/backoffice/services/requirement.service.ts +0 -76
- package/src/backoffice/services/subcategory.service.ts +0 -87
- package/src/backoffice/services/technology.service.ts +0 -289
- package/src/backoffice/types/product.types.ts +6 -116
- package/src/services/appointment/utils/zone-management.utils.ts +17 -3
- package/src/backoffice/services/migrate-products.ts +0 -116
package/dist/index.mjs
CHANGED
|
@@ -1445,11 +1445,11 @@ function calculateItemSubtotal(item) {
|
|
|
1445
1445
|
if (item.type === "note") {
|
|
1446
1446
|
return 0;
|
|
1447
1447
|
}
|
|
1448
|
+
const quantity = item.quantity || 0;
|
|
1448
1449
|
if (item.priceOverrideAmount !== void 0 && item.priceOverrideAmount !== null) {
|
|
1449
|
-
return item.priceOverrideAmount;
|
|
1450
|
+
return item.priceOverrideAmount * quantity;
|
|
1450
1451
|
}
|
|
1451
1452
|
const price = item.price || 0;
|
|
1452
|
-
const quantity = item.quantity || 0;
|
|
1453
1453
|
return price * quantity;
|
|
1454
1454
|
}
|
|
1455
1455
|
function calculateFinalBilling(zonesData, taxRate = 0.2) {
|
|
@@ -1565,7 +1565,18 @@ async function updateZoneItemUtil(db, appointmentId, zoneId, itemIndex, updates)
|
|
|
1565
1565
|
...updates,
|
|
1566
1566
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1567
1567
|
};
|
|
1568
|
+
console.log(`[updateZoneItemUtil] BEFORE recalculation:`, {
|
|
1569
|
+
itemIndex,
|
|
1570
|
+
quantity: items[itemIndex].quantity,
|
|
1571
|
+
priceOverrideAmount: items[itemIndex].priceOverrideAmount,
|
|
1572
|
+
price: items[itemIndex].price,
|
|
1573
|
+
oldSubtotal: items[itemIndex].subtotal
|
|
1574
|
+
});
|
|
1568
1575
|
items[itemIndex].subtotal = calculateItemSubtotal(items[itemIndex]);
|
|
1576
|
+
console.log(`[updateZoneItemUtil] AFTER recalculation:`, {
|
|
1577
|
+
itemIndex,
|
|
1578
|
+
newSubtotal: items[itemIndex].subtotal
|
|
1579
|
+
});
|
|
1569
1580
|
const finalbilling = calculateFinalBilling(metadata.zonesData);
|
|
1570
1581
|
const appointmentRef = doc3(db, APPOINTMENTS_COLLECTION, appointmentId);
|
|
1571
1582
|
await updateDoc3(appointmentRef, {
|
|
@@ -18509,73 +18520,6 @@ var BrandService = class extends BaseService {
|
|
|
18509
18520
|
...docSnap.data()
|
|
18510
18521
|
};
|
|
18511
18522
|
}
|
|
18512
|
-
/**
|
|
18513
|
-
* Exports brands to CSV string, suitable for Excel/Sheets.
|
|
18514
|
-
* Includes headers and optional UTF-8 BOM.
|
|
18515
|
-
* By default exports only active brands (set includeInactive to true to export all).
|
|
18516
|
-
*/
|
|
18517
|
-
async exportToCsv(options) {
|
|
18518
|
-
var _a, _b;
|
|
18519
|
-
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
18520
|
-
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
18521
|
-
const headers = [
|
|
18522
|
-
"id",
|
|
18523
|
-
"name",
|
|
18524
|
-
"manufacturer",
|
|
18525
|
-
"website",
|
|
18526
|
-
"description",
|
|
18527
|
-
"isActive"
|
|
18528
|
-
];
|
|
18529
|
-
const rows = [];
|
|
18530
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
18531
|
-
const PAGE_SIZE = 1e3;
|
|
18532
|
-
let cursor;
|
|
18533
|
-
const baseConstraints = [];
|
|
18534
|
-
if (!includeInactive) {
|
|
18535
|
-
baseConstraints.push(where33("isActive", "==", true));
|
|
18536
|
-
}
|
|
18537
|
-
baseConstraints.push(orderBy19("name_lowercase"));
|
|
18538
|
-
while (true) {
|
|
18539
|
-
const constraints = [...baseConstraints, limit17(PAGE_SIZE)];
|
|
18540
|
-
if (cursor) constraints.push(startAfter15(cursor));
|
|
18541
|
-
const q = query33(this.getBrandsRef(), ...constraints);
|
|
18542
|
-
const snapshot = await getDocs33(q);
|
|
18543
|
-
if (snapshot.empty) break;
|
|
18544
|
-
for (const d of snapshot.docs) {
|
|
18545
|
-
const brand = { id: d.id, ...d.data() };
|
|
18546
|
-
rows.push(this.brandToCsvRow(brand));
|
|
18547
|
-
}
|
|
18548
|
-
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
18549
|
-
if (snapshot.size < PAGE_SIZE) break;
|
|
18550
|
-
}
|
|
18551
|
-
const csvBody = rows.join("\r\n");
|
|
18552
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
18553
|
-
}
|
|
18554
|
-
brandToCsvRow(brand) {
|
|
18555
|
-
var _a, _b, _c, _d, _e, _f;
|
|
18556
|
-
const values = [
|
|
18557
|
-
(_a = brand.id) != null ? _a : "",
|
|
18558
|
-
(_b = brand.name) != null ? _b : "",
|
|
18559
|
-
(_c = brand.manufacturer) != null ? _c : "",
|
|
18560
|
-
(_d = brand.website) != null ? _d : "",
|
|
18561
|
-
(_e = brand.description) != null ? _e : "",
|
|
18562
|
-
String((_f = brand.isActive) != null ? _f : "")
|
|
18563
|
-
];
|
|
18564
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
18565
|
-
}
|
|
18566
|
-
formatDateIso(value) {
|
|
18567
|
-
if (value instanceof Date) return value.toISOString();
|
|
18568
|
-
if (value && typeof value.toDate === "function") {
|
|
18569
|
-
const d = value.toDate();
|
|
18570
|
-
return d instanceof Date ? d.toISOString() : String(value);
|
|
18571
|
-
}
|
|
18572
|
-
return String(value != null ? value : "");
|
|
18573
|
-
}
|
|
18574
|
-
formatCsvValue(value) {
|
|
18575
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
18576
|
-
const escaped = str.replace(/"/g, '""');
|
|
18577
|
-
return `"${escaped}"`;
|
|
18578
|
-
}
|
|
18579
18523
|
};
|
|
18580
18524
|
|
|
18581
18525
|
// src/backoffice/services/category.service.ts
|
|
@@ -18767,71 +18711,6 @@ var CategoryService = class extends BaseService {
|
|
|
18767
18711
|
...docSnap.data()
|
|
18768
18712
|
};
|
|
18769
18713
|
}
|
|
18770
|
-
/**
|
|
18771
|
-
* Exports categories to CSV string, suitable for Excel/Sheets.
|
|
18772
|
-
* Includes headers and optional UTF-8 BOM.
|
|
18773
|
-
* By default exports only active categories (set includeInactive to true to export all).
|
|
18774
|
-
*/
|
|
18775
|
-
async exportToCsv(options) {
|
|
18776
|
-
var _a, _b;
|
|
18777
|
-
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
18778
|
-
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
18779
|
-
const headers = [
|
|
18780
|
-
"id",
|
|
18781
|
-
"name",
|
|
18782
|
-
"description",
|
|
18783
|
-
"family",
|
|
18784
|
-
"isActive"
|
|
18785
|
-
];
|
|
18786
|
-
const rows = [];
|
|
18787
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
18788
|
-
const PAGE_SIZE = 1e3;
|
|
18789
|
-
let cursor;
|
|
18790
|
-
const constraints = [];
|
|
18791
|
-
if (!includeInactive) {
|
|
18792
|
-
constraints.push(where34("isActive", "==", true));
|
|
18793
|
-
}
|
|
18794
|
-
constraints.push(orderBy20("name"));
|
|
18795
|
-
while (true) {
|
|
18796
|
-
const queryConstraints = [...constraints, limit18(PAGE_SIZE)];
|
|
18797
|
-
if (cursor) queryConstraints.push(startAfter16(cursor));
|
|
18798
|
-
const q = query34(this.categoriesRef, ...queryConstraints);
|
|
18799
|
-
const snapshot = await getDocs34(q);
|
|
18800
|
-
if (snapshot.empty) break;
|
|
18801
|
-
for (const d of snapshot.docs) {
|
|
18802
|
-
const category = { id: d.id, ...d.data() };
|
|
18803
|
-
rows.push(this.categoryToCsvRow(category));
|
|
18804
|
-
}
|
|
18805
|
-
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
18806
|
-
if (snapshot.size < PAGE_SIZE) break;
|
|
18807
|
-
}
|
|
18808
|
-
const csvBody = rows.join("\r\n");
|
|
18809
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
18810
|
-
}
|
|
18811
|
-
categoryToCsvRow(category) {
|
|
18812
|
-
var _a, _b, _c, _d, _e;
|
|
18813
|
-
const values = [
|
|
18814
|
-
(_a = category.id) != null ? _a : "",
|
|
18815
|
-
(_b = category.name) != null ? _b : "",
|
|
18816
|
-
(_c = category.description) != null ? _c : "",
|
|
18817
|
-
(_d = category.family) != null ? _d : "",
|
|
18818
|
-
String((_e = category.isActive) != null ? _e : "")
|
|
18819
|
-
];
|
|
18820
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
18821
|
-
}
|
|
18822
|
-
formatDateIso(value) {
|
|
18823
|
-
if (value instanceof Date) return value.toISOString();
|
|
18824
|
-
if (value && typeof value.toDate === "function") {
|
|
18825
|
-
const d = value.toDate();
|
|
18826
|
-
return d instanceof Date ? d.toISOString() : String(value);
|
|
18827
|
-
}
|
|
18828
|
-
return String(value != null ? value : "");
|
|
18829
|
-
}
|
|
18830
|
-
formatCsvValue(value) {
|
|
18831
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
18832
|
-
const escaped = str.replace(/"/g, '""');
|
|
18833
|
-
return `"${escaped}"`;
|
|
18834
|
-
}
|
|
18835
18714
|
};
|
|
18836
18715
|
|
|
18837
18716
|
// src/backoffice/services/subcategory.service.ts
|
|
@@ -19075,74 +18954,6 @@ var SubcategoryService = class extends BaseService {
|
|
|
19075
18954
|
...docSnap.data()
|
|
19076
18955
|
};
|
|
19077
18956
|
}
|
|
19078
|
-
/**
|
|
19079
|
-
* Exports subcategories to CSV string, suitable for Excel/Sheets.
|
|
19080
|
-
* Includes headers and optional UTF-8 BOM.
|
|
19081
|
-
* By default exports only active subcategories (set includeInactive to true to export all).
|
|
19082
|
-
*/
|
|
19083
|
-
async exportToCsv(options) {
|
|
19084
|
-
var _a, _b;
|
|
19085
|
-
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
19086
|
-
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
19087
|
-
const headers = [
|
|
19088
|
-
"id",
|
|
19089
|
-
"name",
|
|
19090
|
-
"categoryId",
|
|
19091
|
-
"description",
|
|
19092
|
-
"isActive"
|
|
19093
|
-
];
|
|
19094
|
-
const rows = [];
|
|
19095
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
19096
|
-
const PAGE_SIZE = 1e3;
|
|
19097
|
-
let cursor;
|
|
19098
|
-
const constraints = [];
|
|
19099
|
-
if (!includeInactive) {
|
|
19100
|
-
constraints.push(where35("isActive", "==", true));
|
|
19101
|
-
}
|
|
19102
|
-
constraints.push(orderBy21("name"));
|
|
19103
|
-
while (true) {
|
|
19104
|
-
const queryConstraints = [...constraints, limit19(PAGE_SIZE)];
|
|
19105
|
-
if (cursor) queryConstraints.push(startAfter17(cursor));
|
|
19106
|
-
const q = query35(
|
|
19107
|
-
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
19108
|
-
...queryConstraints
|
|
19109
|
-
);
|
|
19110
|
-
const snapshot = await getDocs35(q);
|
|
19111
|
-
if (snapshot.empty) break;
|
|
19112
|
-
for (const d of snapshot.docs) {
|
|
19113
|
-
const subcategory = { id: d.id, ...d.data() };
|
|
19114
|
-
rows.push(this.subcategoryToCsvRow(subcategory));
|
|
19115
|
-
}
|
|
19116
|
-
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
19117
|
-
if (snapshot.size < PAGE_SIZE) break;
|
|
19118
|
-
}
|
|
19119
|
-
const csvBody = rows.join("\r\n");
|
|
19120
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
19121
|
-
}
|
|
19122
|
-
subcategoryToCsvRow(subcategory) {
|
|
19123
|
-
var _a, _b, _c, _d, _e;
|
|
19124
|
-
const values = [
|
|
19125
|
-
(_a = subcategory.id) != null ? _a : "",
|
|
19126
|
-
(_b = subcategory.name) != null ? _b : "",
|
|
19127
|
-
(_c = subcategory.categoryId) != null ? _c : "",
|
|
19128
|
-
(_d = subcategory.description) != null ? _d : "",
|
|
19129
|
-
String((_e = subcategory.isActive) != null ? _e : "")
|
|
19130
|
-
];
|
|
19131
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
19132
|
-
}
|
|
19133
|
-
formatDateIso(value) {
|
|
19134
|
-
if (value instanceof Date) return value.toISOString();
|
|
19135
|
-
if (value && typeof value.toDate === "function") {
|
|
19136
|
-
const d = value.toDate();
|
|
19137
|
-
return d instanceof Date ? d.toISOString() : String(value);
|
|
19138
|
-
}
|
|
19139
|
-
return String(value != null ? value : "");
|
|
19140
|
-
}
|
|
19141
|
-
formatCsvValue(value) {
|
|
19142
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
19143
|
-
const escaped = str.replace(/"/g, '""');
|
|
19144
|
-
return `"${escaped}"`;
|
|
19145
|
-
}
|
|
19146
18957
|
};
|
|
19147
18958
|
|
|
19148
18959
|
// src/backoffice/services/technology.service.ts
|
|
@@ -19159,14 +18970,8 @@ import {
|
|
|
19159
18970
|
updateDoc as updateDoc38,
|
|
19160
18971
|
where as where36,
|
|
19161
18972
|
arrayUnion as arrayUnion9,
|
|
19162
|
-
arrayRemove as arrayRemove8
|
|
19163
|
-
writeBatch as writeBatch7
|
|
18973
|
+
arrayRemove as arrayRemove8
|
|
19164
18974
|
} from "firebase/firestore";
|
|
19165
|
-
|
|
19166
|
-
// src/backoffice/types/product.types.ts
|
|
19167
|
-
var PRODUCTS_COLLECTION = "products";
|
|
19168
|
-
|
|
19169
|
-
// src/backoffice/services/technology.service.ts
|
|
19170
18975
|
var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
19171
18976
|
minimumLevel: "aesthetician" /* AESTHETICIAN */,
|
|
19172
18977
|
requiredSpecialties: []
|
|
@@ -19328,18 +19133,7 @@ var TechnologyService = class extends BaseService {
|
|
|
19328
19133
|
});
|
|
19329
19134
|
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
19330
19135
|
const docRef = doc41(this.technologiesRef, id);
|
|
19331
|
-
const beforeTech = await this.getById(id);
|
|
19332
19136
|
await updateDoc38(docRef, updateData);
|
|
19333
|
-
const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
|
|
19334
|
-
const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
|
|
19335
|
-
const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
|
|
19336
|
-
if (categoryChanged || subcategoryChanged || nameChanged) {
|
|
19337
|
-
await this.updateProductsInSubcollection(id, {
|
|
19338
|
-
categoryId: updateData.categoryId,
|
|
19339
|
-
subcategoryId: updateData.subcategoryId,
|
|
19340
|
-
technologyName: updateData.name
|
|
19341
|
-
});
|
|
19342
|
-
}
|
|
19343
19137
|
return this.getById(id);
|
|
19344
19138
|
}
|
|
19345
19139
|
/**
|
|
@@ -19775,225 +19569,6 @@ var TechnologyService = class extends BaseService {
|
|
|
19775
19569
|
})
|
|
19776
19570
|
);
|
|
19777
19571
|
}
|
|
19778
|
-
// ==========================================
|
|
19779
|
-
// NEW METHODS: Product assignment management
|
|
19780
|
-
// ==========================================
|
|
19781
|
-
/**
|
|
19782
|
-
* Assigns multiple products to a technology
|
|
19783
|
-
* Updates each product's assignedTechnologyIds array
|
|
19784
|
-
*/
|
|
19785
|
-
async assignProducts(technologyId, productIds) {
|
|
19786
|
-
const batch = writeBatch7(this.db);
|
|
19787
|
-
for (const productId of productIds) {
|
|
19788
|
-
const productRef = doc41(this.db, PRODUCTS_COLLECTION, productId);
|
|
19789
|
-
batch.update(productRef, {
|
|
19790
|
-
assignedTechnologyIds: arrayUnion9(technologyId),
|
|
19791
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
19792
|
-
});
|
|
19793
|
-
}
|
|
19794
|
-
await batch.commit();
|
|
19795
|
-
}
|
|
19796
|
-
/**
|
|
19797
|
-
* Unassigns multiple products from a technology
|
|
19798
|
-
* Updates each product's assignedTechnologyIds array
|
|
19799
|
-
*/
|
|
19800
|
-
async unassignProducts(technologyId, productIds) {
|
|
19801
|
-
const batch = writeBatch7(this.db);
|
|
19802
|
-
for (const productId of productIds) {
|
|
19803
|
-
const productRef = doc41(this.db, PRODUCTS_COLLECTION, productId);
|
|
19804
|
-
batch.update(productRef, {
|
|
19805
|
-
assignedTechnologyIds: arrayRemove8(technologyId),
|
|
19806
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
19807
|
-
});
|
|
19808
|
-
}
|
|
19809
|
-
await batch.commit();
|
|
19810
|
-
}
|
|
19811
|
-
/**
|
|
19812
|
-
* Gets products assigned to a specific technology
|
|
19813
|
-
* Reads from top-level collection for immediate consistency (Cloud Functions may lag)
|
|
19814
|
-
*/
|
|
19815
|
-
async getAssignedProducts(technologyId) {
|
|
19816
|
-
const q = query36(
|
|
19817
|
-
collection36(this.db, PRODUCTS_COLLECTION),
|
|
19818
|
-
where36("assignedTechnologyIds", "array-contains", technologyId),
|
|
19819
|
-
where36("isActive", "==", true),
|
|
19820
|
-
orderBy22("name")
|
|
19821
|
-
);
|
|
19822
|
-
const snapshot = await getDocs36(q);
|
|
19823
|
-
return snapshot.docs.map(
|
|
19824
|
-
(doc44) => ({
|
|
19825
|
-
id: doc44.id,
|
|
19826
|
-
...doc44.data()
|
|
19827
|
-
})
|
|
19828
|
-
);
|
|
19829
|
-
}
|
|
19830
|
-
/**
|
|
19831
|
-
* Gets products NOT assigned to a specific technology
|
|
19832
|
-
*/
|
|
19833
|
-
async getUnassignedProducts(technologyId) {
|
|
19834
|
-
const q = query36(
|
|
19835
|
-
collection36(this.db, PRODUCTS_COLLECTION),
|
|
19836
|
-
where36("isActive", "==", true),
|
|
19837
|
-
orderBy22("name")
|
|
19838
|
-
);
|
|
19839
|
-
const snapshot = await getDocs36(q);
|
|
19840
|
-
const allProducts = snapshot.docs.map(
|
|
19841
|
-
(doc44) => ({
|
|
19842
|
-
id: doc44.id,
|
|
19843
|
-
...doc44.data()
|
|
19844
|
-
})
|
|
19845
|
-
);
|
|
19846
|
-
return allProducts.filter(
|
|
19847
|
-
(product) => {
|
|
19848
|
-
var _a;
|
|
19849
|
-
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
19850
|
-
}
|
|
19851
|
-
);
|
|
19852
|
-
}
|
|
19853
|
-
/**
|
|
19854
|
-
* Gets product assignment statistics for a technology
|
|
19855
|
-
*/
|
|
19856
|
-
async getProductStats(technologyId) {
|
|
19857
|
-
const products = await this.getAssignedProducts(technologyId);
|
|
19858
|
-
const byBrand = {};
|
|
19859
|
-
products.forEach((product) => {
|
|
19860
|
-
byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
|
|
19861
|
-
});
|
|
19862
|
-
return {
|
|
19863
|
-
totalAssigned: products.length,
|
|
19864
|
-
byBrand
|
|
19865
|
-
};
|
|
19866
|
-
}
|
|
19867
|
-
/**
|
|
19868
|
-
* Updates products in technology subcollection when technology metadata changes
|
|
19869
|
-
* @param technologyId - ID of the technology
|
|
19870
|
-
* @param updates - Fields to update (categoryId, subcategoryId, technologyName)
|
|
19871
|
-
*/
|
|
19872
|
-
async updateProductsInSubcollection(technologyId, updates) {
|
|
19873
|
-
const productsRef = collection36(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
19874
|
-
const productsSnapshot = await getDocs36(productsRef);
|
|
19875
|
-
if (productsSnapshot.empty) {
|
|
19876
|
-
return;
|
|
19877
|
-
}
|
|
19878
|
-
const batch = writeBatch7(this.db);
|
|
19879
|
-
for (const productDoc of productsSnapshot.docs) {
|
|
19880
|
-
const productRef = productDoc.ref;
|
|
19881
|
-
const updateFields = {};
|
|
19882
|
-
if (updates.categoryId !== void 0) {
|
|
19883
|
-
updateFields.categoryId = updates.categoryId;
|
|
19884
|
-
}
|
|
19885
|
-
if (updates.subcategoryId !== void 0) {
|
|
19886
|
-
updateFields.subcategoryId = updates.subcategoryId;
|
|
19887
|
-
}
|
|
19888
|
-
if (updates.technologyName !== void 0) {
|
|
19889
|
-
updateFields.technologyName = updates.technologyName;
|
|
19890
|
-
}
|
|
19891
|
-
if (Object.keys(updateFields).length > 0) {
|
|
19892
|
-
batch.update(productRef, updateFields);
|
|
19893
|
-
}
|
|
19894
|
-
}
|
|
19895
|
-
await batch.commit();
|
|
19896
|
-
}
|
|
19897
|
-
/**
|
|
19898
|
-
* Exports technologies to CSV string, suitable for Excel/Sheets.
|
|
19899
|
-
* Includes headers and optional UTF-8 BOM.
|
|
19900
|
-
* By default exports only active technologies (set includeInactive to true to export all).
|
|
19901
|
-
* Includes product names from subcollections.
|
|
19902
|
-
*/
|
|
19903
|
-
async exportToCsv(options) {
|
|
19904
|
-
var _a, _b;
|
|
19905
|
-
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
19906
|
-
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
19907
|
-
const headers = [
|
|
19908
|
-
"id",
|
|
19909
|
-
"name",
|
|
19910
|
-
"description",
|
|
19911
|
-
"family",
|
|
19912
|
-
"categoryId",
|
|
19913
|
-
"subcategoryId",
|
|
19914
|
-
"technicalDetails",
|
|
19915
|
-
"requirements_pre",
|
|
19916
|
-
"requirements_post",
|
|
19917
|
-
"blockingConditions",
|
|
19918
|
-
"contraindications",
|
|
19919
|
-
"benefits",
|
|
19920
|
-
"certificationMinimumLevel",
|
|
19921
|
-
"certificationRequiredSpecialties",
|
|
19922
|
-
"documentationTemplateIds",
|
|
19923
|
-
"productNames",
|
|
19924
|
-
"isActive"
|
|
19925
|
-
];
|
|
19926
|
-
const rows = [];
|
|
19927
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
19928
|
-
const PAGE_SIZE = 1e3;
|
|
19929
|
-
let cursor;
|
|
19930
|
-
const constraints = [];
|
|
19931
|
-
if (!includeInactive) {
|
|
19932
|
-
constraints.push(where36("isActive", "==", true));
|
|
19933
|
-
}
|
|
19934
|
-
constraints.push(orderBy22("name"));
|
|
19935
|
-
while (true) {
|
|
19936
|
-
const queryConstraints = [...constraints, limit20(PAGE_SIZE)];
|
|
19937
|
-
if (cursor) queryConstraints.push(startAfter18(cursor));
|
|
19938
|
-
const q = query36(this.technologiesRef, ...queryConstraints);
|
|
19939
|
-
const snapshot = await getDocs36(q);
|
|
19940
|
-
if (snapshot.empty) break;
|
|
19941
|
-
for (const d of snapshot.docs) {
|
|
19942
|
-
const technology = { id: d.id, ...d.data() };
|
|
19943
|
-
const productNames = await this.getProductNamesForTechnology(technology.id);
|
|
19944
|
-
rows.push(this.technologyToCsvRow(technology, productNames));
|
|
19945
|
-
}
|
|
19946
|
-
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
19947
|
-
if (snapshot.size < PAGE_SIZE) break;
|
|
19948
|
-
}
|
|
19949
|
-
const csvBody = rows.join("\r\n");
|
|
19950
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
19951
|
-
}
|
|
19952
|
-
/**
|
|
19953
|
-
* Gets product names from the technology's product subcollection
|
|
19954
|
-
*/
|
|
19955
|
-
async getProductNamesForTechnology(technologyId) {
|
|
19956
|
-
try {
|
|
19957
|
-
const productsRef = collection36(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
19958
|
-
const q = query36(productsRef, where36("isActive", "==", true));
|
|
19959
|
-
const snapshot = await getDocs36(q);
|
|
19960
|
-
return snapshot.docs.map((doc44) => {
|
|
19961
|
-
const product = doc44.data();
|
|
19962
|
-
return product.name || "";
|
|
19963
|
-
}).filter((name) => name);
|
|
19964
|
-
} catch (error) {
|
|
19965
|
-
console.error(`Error fetching products for technology ${technologyId}:`, error);
|
|
19966
|
-
return [];
|
|
19967
|
-
}
|
|
19968
|
-
}
|
|
19969
|
-
technologyToCsvRow(technology, productNames = []) {
|
|
19970
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
|
|
19971
|
-
const values = [
|
|
19972
|
-
(_a = technology.id) != null ? _a : "",
|
|
19973
|
-
(_b = technology.name) != null ? _b : "",
|
|
19974
|
-
(_c = technology.description) != null ? _c : "",
|
|
19975
|
-
(_d = technology.family) != null ? _d : "",
|
|
19976
|
-
(_e = technology.categoryId) != null ? _e : "",
|
|
19977
|
-
(_f = technology.subcategoryId) != null ? _f : "",
|
|
19978
|
-
(_g = technology.technicalDetails) != null ? _g : "",
|
|
19979
|
-
(_j = (_i = (_h = technology.requirements) == null ? void 0 : _h.pre) == null ? void 0 : _i.map((r) => r.name).join(";")) != null ? _j : "",
|
|
19980
|
-
(_m = (_l = (_k = technology.requirements) == null ? void 0 : _k.post) == null ? void 0 : _l.map((r) => r.name).join(";")) != null ? _m : "",
|
|
19981
|
-
(_o = (_n = technology.blockingConditions) == null ? void 0 : _n.join(";")) != null ? _o : "",
|
|
19982
|
-
(_q = (_p = technology.contraindications) == null ? void 0 : _p.map((c) => c.name).join(";")) != null ? _q : "",
|
|
19983
|
-
(_s = (_r = technology.benefits) == null ? void 0 : _r.map((b) => b.name).join(";")) != null ? _s : "",
|
|
19984
|
-
(_u = (_t = technology.certificationRequirement) == null ? void 0 : _t.minimumLevel) != null ? _u : "",
|
|
19985
|
-
(_x = (_w = (_v = technology.certificationRequirement) == null ? void 0 : _v.requiredSpecialties) == null ? void 0 : _w.join(";")) != null ? _x : "",
|
|
19986
|
-
(_z = (_y = technology.documentationTemplates) == null ? void 0 : _y.map((t) => t.templateId).join(";")) != null ? _z : "",
|
|
19987
|
-
productNames.join(";"),
|
|
19988
|
-
String((_A = technology.isActive) != null ? _A : "")
|
|
19989
|
-
];
|
|
19990
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
19991
|
-
}
|
|
19992
|
-
formatCsvValue(value) {
|
|
19993
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
19994
|
-
const escaped = str.replace(/"/g, '""');
|
|
19995
|
-
return `"${escaped}"`;
|
|
19996
|
-
}
|
|
19997
19572
|
};
|
|
19998
19573
|
|
|
19999
19574
|
// src/backoffice/services/product.service.ts
|
|
@@ -20010,20 +19585,16 @@ import {
|
|
|
20010
19585
|
limit as limit21,
|
|
20011
19586
|
orderBy as orderBy23,
|
|
20012
19587
|
startAfter as startAfter19,
|
|
20013
|
-
getCountFromServer as getCountFromServer7
|
|
20014
|
-
arrayUnion as arrayUnion10,
|
|
20015
|
-
arrayRemove as arrayRemove9
|
|
19588
|
+
getCountFromServer as getCountFromServer7
|
|
20016
19589
|
} from "firebase/firestore";
|
|
19590
|
+
|
|
19591
|
+
// src/backoffice/types/product.types.ts
|
|
19592
|
+
var PRODUCTS_COLLECTION = "products";
|
|
19593
|
+
|
|
19594
|
+
// src/backoffice/services/product.service.ts
|
|
20017
19595
|
var ProductService = class extends BaseService {
|
|
20018
19596
|
/**
|
|
20019
|
-
* Gets reference to
|
|
20020
|
-
* @returns Firestore collection reference
|
|
20021
|
-
*/
|
|
20022
|
-
getTopLevelProductsRef() {
|
|
20023
|
-
return collection37(this.db, PRODUCTS_COLLECTION);
|
|
20024
|
-
}
|
|
20025
|
-
/**
|
|
20026
|
-
* Gets reference to products collection under a technology (backward compatibility)
|
|
19597
|
+
* Gets reference to products collection under a technology
|
|
20027
19598
|
* @param technologyId - ID of the technology
|
|
20028
19599
|
* @returns Firestore collection reference
|
|
20029
19600
|
*/
|
|
@@ -20039,7 +19610,6 @@ var ProductService = class extends BaseService {
|
|
|
20039
19610
|
...product,
|
|
20040
19611
|
brandId,
|
|
20041
19612
|
technologyId,
|
|
20042
|
-
// Required for old subcollection structure
|
|
20043
19613
|
createdAt: now,
|
|
20044
19614
|
updatedAt: now,
|
|
20045
19615
|
isActive: true
|
|
@@ -20099,26 +19669,30 @@ var ProductService = class extends BaseService {
|
|
|
20099
19669
|
}
|
|
20100
19670
|
/**
|
|
20101
19671
|
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
20102
|
-
*
|
|
19672
|
+
* This uses a single collectionGroup query for efficiency.
|
|
20103
19673
|
*/
|
|
20104
19674
|
async getProductCounts() {
|
|
19675
|
+
const q = query37(collectionGroup3(this.db, PRODUCTS_COLLECTION), where37("isActive", "==", true));
|
|
19676
|
+
const snapshot = await getDocs37(q);
|
|
20105
19677
|
const counts = {
|
|
20106
19678
|
byCategory: {},
|
|
20107
19679
|
bySubcategory: {},
|
|
20108
19680
|
byTechnology: {}
|
|
20109
19681
|
};
|
|
20110
|
-
|
|
20111
|
-
|
|
19682
|
+
if (snapshot.empty) {
|
|
19683
|
+
return counts;
|
|
19684
|
+
}
|
|
20112
19685
|
snapshot.docs.forEach((doc44) => {
|
|
20113
19686
|
const product = doc44.data();
|
|
20114
|
-
|
|
20115
|
-
|
|
19687
|
+
const { categoryId, subcategoryId, technologyId } = product;
|
|
19688
|
+
if (categoryId) {
|
|
19689
|
+
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
20116
19690
|
}
|
|
20117
|
-
if (
|
|
20118
|
-
counts.bySubcategory[
|
|
19691
|
+
if (subcategoryId) {
|
|
19692
|
+
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
20119
19693
|
}
|
|
20120
|
-
if (
|
|
20121
|
-
counts.byTechnology[
|
|
19694
|
+
if (technologyId) {
|
|
19695
|
+
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
20122
19696
|
}
|
|
20123
19697
|
});
|
|
20124
19698
|
return counts;
|
|
@@ -20197,247 +19771,12 @@ var ProductService = class extends BaseService {
|
|
|
20197
19771
|
...docSnap.data()
|
|
20198
19772
|
};
|
|
20199
19773
|
}
|
|
20200
|
-
// ==========================================
|
|
20201
|
-
// NEW METHODS: Top-level collection (preferred)
|
|
20202
|
-
// ==========================================
|
|
20203
|
-
/**
|
|
20204
|
-
* Creates a new product in the top-level collection
|
|
20205
|
-
*/
|
|
20206
|
-
async createTopLevel(brandId, product, technologyIds = []) {
|
|
20207
|
-
const now = /* @__PURE__ */ new Date();
|
|
20208
|
-
const newProduct = {
|
|
20209
|
-
...product,
|
|
20210
|
-
brandId,
|
|
20211
|
-
assignedTechnologyIds: technologyIds,
|
|
20212
|
-
createdAt: now,
|
|
20213
|
-
updatedAt: now,
|
|
20214
|
-
isActive: true
|
|
20215
|
-
};
|
|
20216
|
-
const productRef = await addDoc8(this.getTopLevelProductsRef(), newProduct);
|
|
20217
|
-
return { id: productRef.id, ...newProduct };
|
|
20218
|
-
}
|
|
20219
|
-
/**
|
|
20220
|
-
* Gets all products from the top-level collection
|
|
20221
|
-
*/
|
|
20222
|
-
async getAllTopLevel(options) {
|
|
20223
|
-
const { rowsPerPage, lastVisible, brandId } = options;
|
|
20224
|
-
const constraints = [where37("isActive", "==", true), orderBy23("name")];
|
|
20225
|
-
if (brandId) {
|
|
20226
|
-
constraints.push(where37("brandId", "==", brandId));
|
|
20227
|
-
}
|
|
20228
|
-
if (lastVisible) {
|
|
20229
|
-
constraints.push(startAfter19(lastVisible));
|
|
20230
|
-
}
|
|
20231
|
-
constraints.push(limit21(rowsPerPage));
|
|
20232
|
-
const q = query37(this.getTopLevelProductsRef(), ...constraints);
|
|
20233
|
-
const snapshot = await getDocs37(q);
|
|
20234
|
-
const products = snapshot.docs.map(
|
|
20235
|
-
(doc44) => ({
|
|
20236
|
-
id: doc44.id,
|
|
20237
|
-
...doc44.data()
|
|
20238
|
-
})
|
|
20239
|
-
);
|
|
20240
|
-
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
20241
|
-
return { products, lastVisible: newLastVisible };
|
|
20242
|
-
}
|
|
20243
|
-
/**
|
|
20244
|
-
* Gets a product by ID from the top-level collection
|
|
20245
|
-
*/
|
|
20246
|
-
async getByIdTopLevel(productId) {
|
|
20247
|
-
const docRef = doc42(this.getTopLevelProductsRef(), productId);
|
|
20248
|
-
const docSnap = await getDoc43(docRef);
|
|
20249
|
-
if (!docSnap.exists()) return null;
|
|
20250
|
-
return {
|
|
20251
|
-
id: docSnap.id,
|
|
20252
|
-
...docSnap.data()
|
|
20253
|
-
};
|
|
20254
|
-
}
|
|
20255
|
-
/**
|
|
20256
|
-
* Updates a product in the top-level collection
|
|
20257
|
-
*/
|
|
20258
|
-
async updateTopLevel(productId, product) {
|
|
20259
|
-
const updateData = {
|
|
20260
|
-
...product,
|
|
20261
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
20262
|
-
};
|
|
20263
|
-
const docRef = doc42(this.getTopLevelProductsRef(), productId);
|
|
20264
|
-
await updateDoc39(docRef, updateData);
|
|
20265
|
-
return this.getByIdTopLevel(productId);
|
|
20266
|
-
}
|
|
20267
|
-
/**
|
|
20268
|
-
* Deletes a product from the top-level collection (soft delete)
|
|
20269
|
-
*/
|
|
20270
|
-
async deleteTopLevel(productId) {
|
|
20271
|
-
await this.updateTopLevel(productId, {
|
|
20272
|
-
isActive: false
|
|
20273
|
-
});
|
|
20274
|
-
}
|
|
20275
|
-
/**
|
|
20276
|
-
* Assigns a product to a technology
|
|
20277
|
-
*/
|
|
20278
|
-
async assignToTechnology(productId, technologyId) {
|
|
20279
|
-
const docRef = doc42(this.getTopLevelProductsRef(), productId);
|
|
20280
|
-
await updateDoc39(docRef, {
|
|
20281
|
-
assignedTechnologyIds: arrayUnion10(technologyId),
|
|
20282
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
20283
|
-
});
|
|
20284
|
-
}
|
|
20285
|
-
/**
|
|
20286
|
-
* Unassigns a product from a technology
|
|
20287
|
-
*/
|
|
20288
|
-
async unassignFromTechnology(productId, technologyId) {
|
|
20289
|
-
const docRef = doc42(this.getTopLevelProductsRef(), productId);
|
|
20290
|
-
await updateDoc39(docRef, {
|
|
20291
|
-
assignedTechnologyIds: arrayRemove9(technologyId),
|
|
20292
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
20293
|
-
});
|
|
20294
|
-
}
|
|
20295
|
-
/**
|
|
20296
|
-
* Gets products assigned to a specific technology
|
|
20297
|
-
*/
|
|
20298
|
-
async getAssignedProducts(technologyId) {
|
|
20299
|
-
const q = query37(
|
|
20300
|
-
this.getTopLevelProductsRef(),
|
|
20301
|
-
where37("assignedTechnologyIds", "array-contains", technologyId),
|
|
20302
|
-
where37("isActive", "==", true),
|
|
20303
|
-
orderBy23("name")
|
|
20304
|
-
);
|
|
20305
|
-
const snapshot = await getDocs37(q);
|
|
20306
|
-
return snapshot.docs.map(
|
|
20307
|
-
(doc44) => ({
|
|
20308
|
-
id: doc44.id,
|
|
20309
|
-
...doc44.data()
|
|
20310
|
-
})
|
|
20311
|
-
);
|
|
20312
|
-
}
|
|
20313
|
-
/**
|
|
20314
|
-
* Gets products NOT assigned to a specific technology
|
|
20315
|
-
*/
|
|
20316
|
-
async getUnassignedProducts(technologyId) {
|
|
20317
|
-
const q = query37(
|
|
20318
|
-
this.getTopLevelProductsRef(),
|
|
20319
|
-
where37("isActive", "==", true),
|
|
20320
|
-
orderBy23("name")
|
|
20321
|
-
);
|
|
20322
|
-
const snapshot = await getDocs37(q);
|
|
20323
|
-
const allProducts = snapshot.docs.map(
|
|
20324
|
-
(doc44) => ({
|
|
20325
|
-
id: doc44.id,
|
|
20326
|
-
...doc44.data()
|
|
20327
|
-
})
|
|
20328
|
-
);
|
|
20329
|
-
return allProducts.filter(
|
|
20330
|
-
(product) => {
|
|
20331
|
-
var _a;
|
|
20332
|
-
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
20333
|
-
}
|
|
20334
|
-
);
|
|
20335
|
-
}
|
|
20336
|
-
/**
|
|
20337
|
-
* Gets all products for a brand (from top-level collection)
|
|
20338
|
-
*/
|
|
20339
|
-
async getByBrand(brandId) {
|
|
20340
|
-
const q = query37(
|
|
20341
|
-
this.getTopLevelProductsRef(),
|
|
20342
|
-
where37("brandId", "==", brandId),
|
|
20343
|
-
where37("isActive", "==", true),
|
|
20344
|
-
orderBy23("name")
|
|
20345
|
-
);
|
|
20346
|
-
const snapshot = await getDocs37(q);
|
|
20347
|
-
return snapshot.docs.map(
|
|
20348
|
-
(doc44) => ({
|
|
20349
|
-
id: doc44.id,
|
|
20350
|
-
...doc44.data()
|
|
20351
|
-
})
|
|
20352
|
-
);
|
|
20353
|
-
}
|
|
20354
|
-
/**
|
|
20355
|
-
* Exports products to CSV string, suitable for Excel/Sheets.
|
|
20356
|
-
* Includes headers and optional UTF-8 BOM.
|
|
20357
|
-
* By default exports only active products (set includeInactive to true to export all).
|
|
20358
|
-
*/
|
|
20359
|
-
async exportToCsv(options) {
|
|
20360
|
-
var _a, _b;
|
|
20361
|
-
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
20362
|
-
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
20363
|
-
const headers = [
|
|
20364
|
-
"id",
|
|
20365
|
-
"name",
|
|
20366
|
-
"brandId",
|
|
20367
|
-
"brandName",
|
|
20368
|
-
"assignedTechnologyIds",
|
|
20369
|
-
"description",
|
|
20370
|
-
"technicalDetails",
|
|
20371
|
-
"dosage",
|
|
20372
|
-
"composition",
|
|
20373
|
-
"indications",
|
|
20374
|
-
"contraindications",
|
|
20375
|
-
"warnings",
|
|
20376
|
-
"isActive"
|
|
20377
|
-
];
|
|
20378
|
-
const rows = [];
|
|
20379
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20380
|
-
const PAGE_SIZE = 1e3;
|
|
20381
|
-
let cursor;
|
|
20382
|
-
const constraints = [];
|
|
20383
|
-
if (!includeInactive) {
|
|
20384
|
-
constraints.push(where37("isActive", "==", true));
|
|
20385
|
-
}
|
|
20386
|
-
constraints.push(orderBy23("name"));
|
|
20387
|
-
while (true) {
|
|
20388
|
-
const queryConstraints = [...constraints, limit21(PAGE_SIZE)];
|
|
20389
|
-
if (cursor) queryConstraints.push(startAfter19(cursor));
|
|
20390
|
-
const q = query37(this.getTopLevelProductsRef(), ...queryConstraints);
|
|
20391
|
-
const snapshot = await getDocs37(q);
|
|
20392
|
-
if (snapshot.empty) break;
|
|
20393
|
-
for (const d of snapshot.docs) {
|
|
20394
|
-
const product = { id: d.id, ...d.data() };
|
|
20395
|
-
rows.push(this.productToCsvRow(product));
|
|
20396
|
-
}
|
|
20397
|
-
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
20398
|
-
if (snapshot.size < PAGE_SIZE) break;
|
|
20399
|
-
}
|
|
20400
|
-
const csvBody = rows.join("\r\n");
|
|
20401
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20402
|
-
}
|
|
20403
|
-
productToCsvRow(product) {
|
|
20404
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
|
|
20405
|
-
const values = [
|
|
20406
|
-
(_a = product.id) != null ? _a : "",
|
|
20407
|
-
(_b = product.name) != null ? _b : "",
|
|
20408
|
-
(_c = product.brandId) != null ? _c : "",
|
|
20409
|
-
(_d = product.brandName) != null ? _d : "",
|
|
20410
|
-
(_f = (_e = product.assignedTechnologyIds) == null ? void 0 : _e.join(";")) != null ? _f : "",
|
|
20411
|
-
(_g = product.description) != null ? _g : "",
|
|
20412
|
-
(_h = product.technicalDetails) != null ? _h : "",
|
|
20413
|
-
(_i = product.dosage) != null ? _i : "",
|
|
20414
|
-
(_j = product.composition) != null ? _j : "",
|
|
20415
|
-
(_l = (_k = product.indications) == null ? void 0 : _k.join(";")) != null ? _l : "",
|
|
20416
|
-
(_n = (_m = product.contraindications) == null ? void 0 : _m.map((c) => c.name).join(";")) != null ? _n : "",
|
|
20417
|
-
(_p = (_o = product.warnings) == null ? void 0 : _o.join(";")) != null ? _p : "",
|
|
20418
|
-
String((_q = product.isActive) != null ? _q : "")
|
|
20419
|
-
];
|
|
20420
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20421
|
-
}
|
|
20422
|
-
formatDateIso(value) {
|
|
20423
|
-
if (value instanceof Date) return value.toISOString();
|
|
20424
|
-
if (value && typeof value.toDate === "function") {
|
|
20425
|
-
const d = value.toDate();
|
|
20426
|
-
return d instanceof Date ? d.toISOString() : String(value);
|
|
20427
|
-
}
|
|
20428
|
-
return String(value != null ? value : "");
|
|
20429
|
-
}
|
|
20430
|
-
formatCsvValue(value) {
|
|
20431
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
20432
|
-
const escaped = str.replace(/"/g, '""');
|
|
20433
|
-
return `"${escaped}"`;
|
|
20434
|
-
}
|
|
20435
19774
|
};
|
|
20436
19775
|
|
|
20437
19776
|
// src/backoffice/services/constants.service.ts
|
|
20438
19777
|
import {
|
|
20439
|
-
arrayRemove as
|
|
20440
|
-
arrayUnion as
|
|
19778
|
+
arrayRemove as arrayRemove9,
|
|
19779
|
+
arrayUnion as arrayUnion10,
|
|
20441
19780
|
doc as doc43,
|
|
20442
19781
|
getDoc as getDoc44,
|
|
20443
19782
|
setDoc as setDoc30,
|
|
@@ -20505,7 +19844,7 @@ var ConstantsService = class extends BaseService {
|
|
|
20505
19844
|
await setDoc30(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
20506
19845
|
} else {
|
|
20507
19846
|
await updateDoc40(this.treatmentBenefitsDocRef, {
|
|
20508
|
-
benefits:
|
|
19847
|
+
benefits: arrayUnion10(newBenefit)
|
|
20509
19848
|
});
|
|
20510
19849
|
}
|
|
20511
19850
|
return newBenefit;
|
|
@@ -20559,7 +19898,7 @@ var ConstantsService = class extends BaseService {
|
|
|
20559
19898
|
return;
|
|
20560
19899
|
}
|
|
20561
19900
|
await updateDoc40(this.treatmentBenefitsDocRef, {
|
|
20562
|
-
benefits:
|
|
19901
|
+
benefits: arrayRemove9(benefitToRemove)
|
|
20563
19902
|
});
|
|
20564
19903
|
}
|
|
20565
19904
|
// =================================================================
|
|
@@ -20612,7 +19951,7 @@ var ConstantsService = class extends BaseService {
|
|
|
20612
19951
|
});
|
|
20613
19952
|
} else {
|
|
20614
19953
|
await updateDoc40(this.contraindicationsDocRef, {
|
|
20615
|
-
contraindications:
|
|
19954
|
+
contraindications: arrayUnion10(newContraindication)
|
|
20616
19955
|
});
|
|
20617
19956
|
}
|
|
20618
19957
|
return newContraindication;
|
|
@@ -20668,69 +20007,9 @@ var ConstantsService = class extends BaseService {
|
|
|
20668
20007
|
return;
|
|
20669
20008
|
}
|
|
20670
20009
|
await updateDoc40(this.contraindicationsDocRef, {
|
|
20671
|
-
contraindications:
|
|
20010
|
+
contraindications: arrayRemove9(toRemove)
|
|
20672
20011
|
});
|
|
20673
20012
|
}
|
|
20674
|
-
// =================================================================
|
|
20675
|
-
// CSV Export Methods
|
|
20676
|
-
// =================================================================
|
|
20677
|
-
/**
|
|
20678
|
-
* Exports treatment benefits to CSV string, suitable for Excel/Sheets.
|
|
20679
|
-
* Includes headers and optional UTF-8 BOM.
|
|
20680
|
-
*/
|
|
20681
|
-
async exportBenefitsToCsv(options) {
|
|
20682
|
-
var _a;
|
|
20683
|
-
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
20684
|
-
const headers = ["id", "name", "description"];
|
|
20685
|
-
const rows = [];
|
|
20686
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20687
|
-
const benefits = await this.getAllBenefitsForFilter();
|
|
20688
|
-
for (const benefit of benefits) {
|
|
20689
|
-
rows.push(this.benefitToCsvRow(benefit));
|
|
20690
|
-
}
|
|
20691
|
-
const csvBody = rows.join("\r\n");
|
|
20692
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20693
|
-
}
|
|
20694
|
-
/**
|
|
20695
|
-
* Exports contraindications to CSV string, suitable for Excel/Sheets.
|
|
20696
|
-
* Includes headers and optional UTF-8 BOM.
|
|
20697
|
-
*/
|
|
20698
|
-
async exportContraindicationsToCsv(options) {
|
|
20699
|
-
var _a;
|
|
20700
|
-
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
20701
|
-
const headers = ["id", "name", "description"];
|
|
20702
|
-
const rows = [];
|
|
20703
|
-
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20704
|
-
const contraindications = await this.getAllContraindicationsForFilter();
|
|
20705
|
-
for (const contraindication of contraindications) {
|
|
20706
|
-
rows.push(this.contraindicationToCsvRow(contraindication));
|
|
20707
|
-
}
|
|
20708
|
-
const csvBody = rows.join("\r\n");
|
|
20709
|
-
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20710
|
-
}
|
|
20711
|
-
benefitToCsvRow(benefit) {
|
|
20712
|
-
var _a, _b, _c;
|
|
20713
|
-
const values = [
|
|
20714
|
-
(_a = benefit.id) != null ? _a : "",
|
|
20715
|
-
(_b = benefit.name) != null ? _b : "",
|
|
20716
|
-
(_c = benefit.description) != null ? _c : ""
|
|
20717
|
-
];
|
|
20718
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20719
|
-
}
|
|
20720
|
-
contraindicationToCsvRow(contraindication) {
|
|
20721
|
-
var _a, _b, _c;
|
|
20722
|
-
const values = [
|
|
20723
|
-
(_a = contraindication.id) != null ? _a : "",
|
|
20724
|
-
(_b = contraindication.name) != null ? _b : "",
|
|
20725
|
-
(_c = contraindication.description) != null ? _c : ""
|
|
20726
|
-
];
|
|
20727
|
-
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20728
|
-
}
|
|
20729
|
-
formatCsvValue(value) {
|
|
20730
|
-
const str = value === null || value === void 0 ? "" : String(value);
|
|
20731
|
-
const escaped = str.replace(/"/g, '""');
|
|
20732
|
-
return `"${escaped}"`;
|
|
20733
|
-
}
|
|
20734
20013
|
};
|
|
20735
20014
|
|
|
20736
20015
|
// src/backoffice/types/static/contraindication.types.ts
|