@blackcode_sa/metaestetics-api 1.12.50 → 1.12.51
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 +12 -6
- package/dist/admin/index.d.ts +12 -6
- package/dist/backoffice/index.d.mts +275 -18
- package/dist/backoffice/index.d.ts +275 -18
- package/dist/backoffice/index.js +802 -14
- package/dist/backoffice/index.mjs +830 -38
- package/dist/index.d.mts +255 -10
- package/dist/index.d.ts +255 -10
- package/dist/index.js +748 -19
- package/dist/index.mjs +759 -27
- package/package.json +1 -1
- package/src/backoffice/services/brand.service.ts +86 -0
- package/src/backoffice/services/category.service.ts +84 -0
- package/src/backoffice/services/constants.service.ts +77 -0
- package/src/backoffice/services/migrate-products.ts +116 -0
- package/src/backoffice/services/product.service.ts +316 -18
- package/src/backoffice/services/requirement.service.ts +76 -0
- package/src/backoffice/services/subcategory.service.ts +87 -0
- package/src/backoffice/services/technology.service.ts +289 -0
- package/src/backoffice/types/product.types.ts +116 -6
- package/src/services/procedure/procedure.service.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -18277,6 +18277,73 @@ var BrandService = class extends BaseService {
|
|
|
18277
18277
|
...docSnap.data()
|
|
18278
18278
|
};
|
|
18279
18279
|
}
|
|
18280
|
+
/**
|
|
18281
|
+
* Exports brands to CSV string, suitable for Excel/Sheets.
|
|
18282
|
+
* Includes headers and optional UTF-8 BOM.
|
|
18283
|
+
* By default exports only active brands (set includeInactive to true to export all).
|
|
18284
|
+
*/
|
|
18285
|
+
async exportToCsv(options) {
|
|
18286
|
+
var _a, _b;
|
|
18287
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
18288
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
18289
|
+
const headers = [
|
|
18290
|
+
"id",
|
|
18291
|
+
"name",
|
|
18292
|
+
"manufacturer",
|
|
18293
|
+
"website",
|
|
18294
|
+
"description",
|
|
18295
|
+
"isActive"
|
|
18296
|
+
];
|
|
18297
|
+
const rows = [];
|
|
18298
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
18299
|
+
const PAGE_SIZE = 1e3;
|
|
18300
|
+
let cursor;
|
|
18301
|
+
const baseConstraints = [];
|
|
18302
|
+
if (!includeInactive) {
|
|
18303
|
+
baseConstraints.push((0, import_firestore58.where)("isActive", "==", true));
|
|
18304
|
+
}
|
|
18305
|
+
baseConstraints.push((0, import_firestore58.orderBy)("name_lowercase"));
|
|
18306
|
+
while (true) {
|
|
18307
|
+
const constraints = [...baseConstraints, (0, import_firestore58.limit)(PAGE_SIZE)];
|
|
18308
|
+
if (cursor) constraints.push((0, import_firestore58.startAfter)(cursor));
|
|
18309
|
+
const q = (0, import_firestore58.query)(this.getBrandsRef(), ...constraints);
|
|
18310
|
+
const snapshot = await (0, import_firestore58.getDocs)(q);
|
|
18311
|
+
if (snapshot.empty) break;
|
|
18312
|
+
for (const d of snapshot.docs) {
|
|
18313
|
+
const brand = { id: d.id, ...d.data() };
|
|
18314
|
+
rows.push(this.brandToCsvRow(brand));
|
|
18315
|
+
}
|
|
18316
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
18317
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
18318
|
+
}
|
|
18319
|
+
const csvBody = rows.join("\r\n");
|
|
18320
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
18321
|
+
}
|
|
18322
|
+
brandToCsvRow(brand) {
|
|
18323
|
+
var _a, _b, _c, _d, _e, _f;
|
|
18324
|
+
const values = [
|
|
18325
|
+
(_a = brand.id) != null ? _a : "",
|
|
18326
|
+
(_b = brand.name) != null ? _b : "",
|
|
18327
|
+
(_c = brand.manufacturer) != null ? _c : "",
|
|
18328
|
+
(_d = brand.website) != null ? _d : "",
|
|
18329
|
+
(_e = brand.description) != null ? _e : "",
|
|
18330
|
+
String((_f = brand.isActive) != null ? _f : "")
|
|
18331
|
+
];
|
|
18332
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
18333
|
+
}
|
|
18334
|
+
formatDateIso(value) {
|
|
18335
|
+
if (value instanceof Date) return value.toISOString();
|
|
18336
|
+
if (value && typeof value.toDate === "function") {
|
|
18337
|
+
const d = value.toDate();
|
|
18338
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
18339
|
+
}
|
|
18340
|
+
return String(value != null ? value : "");
|
|
18341
|
+
}
|
|
18342
|
+
formatCsvValue(value) {
|
|
18343
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
18344
|
+
const escaped = str.replace(/"/g, '""');
|
|
18345
|
+
return `"${escaped}"`;
|
|
18346
|
+
}
|
|
18280
18347
|
};
|
|
18281
18348
|
|
|
18282
18349
|
// src/backoffice/services/category.service.ts
|
|
@@ -18455,6 +18522,71 @@ var CategoryService = class extends BaseService {
|
|
|
18455
18522
|
...docSnap.data()
|
|
18456
18523
|
};
|
|
18457
18524
|
}
|
|
18525
|
+
/**
|
|
18526
|
+
* Exports categories to CSV string, suitable for Excel/Sheets.
|
|
18527
|
+
* Includes headers and optional UTF-8 BOM.
|
|
18528
|
+
* By default exports only active categories (set includeInactive to true to export all).
|
|
18529
|
+
*/
|
|
18530
|
+
async exportToCsv(options) {
|
|
18531
|
+
var _a, _b;
|
|
18532
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
18533
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
18534
|
+
const headers = [
|
|
18535
|
+
"id",
|
|
18536
|
+
"name",
|
|
18537
|
+
"description",
|
|
18538
|
+
"family",
|
|
18539
|
+
"isActive"
|
|
18540
|
+
];
|
|
18541
|
+
const rows = [];
|
|
18542
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
18543
|
+
const PAGE_SIZE = 1e3;
|
|
18544
|
+
let cursor;
|
|
18545
|
+
const constraints = [];
|
|
18546
|
+
if (!includeInactive) {
|
|
18547
|
+
constraints.push((0, import_firestore59.where)("isActive", "==", true));
|
|
18548
|
+
}
|
|
18549
|
+
constraints.push((0, import_firestore59.orderBy)("name"));
|
|
18550
|
+
while (true) {
|
|
18551
|
+
const queryConstraints = [...constraints, (0, import_firestore59.limit)(PAGE_SIZE)];
|
|
18552
|
+
if (cursor) queryConstraints.push((0, import_firestore59.startAfter)(cursor));
|
|
18553
|
+
const q = (0, import_firestore59.query)(this.categoriesRef, ...queryConstraints);
|
|
18554
|
+
const snapshot = await (0, import_firestore59.getDocs)(q);
|
|
18555
|
+
if (snapshot.empty) break;
|
|
18556
|
+
for (const d of snapshot.docs) {
|
|
18557
|
+
const category = { id: d.id, ...d.data() };
|
|
18558
|
+
rows.push(this.categoryToCsvRow(category));
|
|
18559
|
+
}
|
|
18560
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
18561
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
18562
|
+
}
|
|
18563
|
+
const csvBody = rows.join("\r\n");
|
|
18564
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
18565
|
+
}
|
|
18566
|
+
categoryToCsvRow(category) {
|
|
18567
|
+
var _a, _b, _c, _d, _e;
|
|
18568
|
+
const values = [
|
|
18569
|
+
(_a = category.id) != null ? _a : "",
|
|
18570
|
+
(_b = category.name) != null ? _b : "",
|
|
18571
|
+
(_c = category.description) != null ? _c : "",
|
|
18572
|
+
(_d = category.family) != null ? _d : "",
|
|
18573
|
+
String((_e = category.isActive) != null ? _e : "")
|
|
18574
|
+
];
|
|
18575
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
18576
|
+
}
|
|
18577
|
+
formatDateIso(value) {
|
|
18578
|
+
if (value instanceof Date) return value.toISOString();
|
|
18579
|
+
if (value && typeof value.toDate === "function") {
|
|
18580
|
+
const d = value.toDate();
|
|
18581
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
18582
|
+
}
|
|
18583
|
+
return String(value != null ? value : "");
|
|
18584
|
+
}
|
|
18585
|
+
formatCsvValue(value) {
|
|
18586
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
18587
|
+
const escaped = str.replace(/"/g, '""');
|
|
18588
|
+
return `"${escaped}"`;
|
|
18589
|
+
}
|
|
18458
18590
|
};
|
|
18459
18591
|
|
|
18460
18592
|
// src/backoffice/services/subcategory.service.ts
|
|
@@ -18682,10 +18814,83 @@ var SubcategoryService = class extends BaseService {
|
|
|
18682
18814
|
...docSnap.data()
|
|
18683
18815
|
};
|
|
18684
18816
|
}
|
|
18817
|
+
/**
|
|
18818
|
+
* Exports subcategories to CSV string, suitable for Excel/Sheets.
|
|
18819
|
+
* Includes headers and optional UTF-8 BOM.
|
|
18820
|
+
* By default exports only active subcategories (set includeInactive to true to export all).
|
|
18821
|
+
*/
|
|
18822
|
+
async exportToCsv(options) {
|
|
18823
|
+
var _a, _b;
|
|
18824
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
18825
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
18826
|
+
const headers = [
|
|
18827
|
+
"id",
|
|
18828
|
+
"name",
|
|
18829
|
+
"categoryId",
|
|
18830
|
+
"description",
|
|
18831
|
+
"isActive"
|
|
18832
|
+
];
|
|
18833
|
+
const rows = [];
|
|
18834
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
18835
|
+
const PAGE_SIZE = 1e3;
|
|
18836
|
+
let cursor;
|
|
18837
|
+
const constraints = [];
|
|
18838
|
+
if (!includeInactive) {
|
|
18839
|
+
constraints.push((0, import_firestore60.where)("isActive", "==", true));
|
|
18840
|
+
}
|
|
18841
|
+
constraints.push((0, import_firestore60.orderBy)("name"));
|
|
18842
|
+
while (true) {
|
|
18843
|
+
const queryConstraints = [...constraints, (0, import_firestore60.limit)(PAGE_SIZE)];
|
|
18844
|
+
if (cursor) queryConstraints.push((0, import_firestore60.startAfter)(cursor));
|
|
18845
|
+
const q = (0, import_firestore60.query)(
|
|
18846
|
+
(0, import_firestore60.collectionGroup)(this.db, SUBCATEGORIES_COLLECTION),
|
|
18847
|
+
...queryConstraints
|
|
18848
|
+
);
|
|
18849
|
+
const snapshot = await (0, import_firestore60.getDocs)(q);
|
|
18850
|
+
if (snapshot.empty) break;
|
|
18851
|
+
for (const d of snapshot.docs) {
|
|
18852
|
+
const subcategory = { id: d.id, ...d.data() };
|
|
18853
|
+
rows.push(this.subcategoryToCsvRow(subcategory));
|
|
18854
|
+
}
|
|
18855
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
18856
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
18857
|
+
}
|
|
18858
|
+
const csvBody = rows.join("\r\n");
|
|
18859
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
18860
|
+
}
|
|
18861
|
+
subcategoryToCsvRow(subcategory) {
|
|
18862
|
+
var _a, _b, _c, _d, _e;
|
|
18863
|
+
const values = [
|
|
18864
|
+
(_a = subcategory.id) != null ? _a : "",
|
|
18865
|
+
(_b = subcategory.name) != null ? _b : "",
|
|
18866
|
+
(_c = subcategory.categoryId) != null ? _c : "",
|
|
18867
|
+
(_d = subcategory.description) != null ? _d : "",
|
|
18868
|
+
String((_e = subcategory.isActive) != null ? _e : "")
|
|
18869
|
+
];
|
|
18870
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
18871
|
+
}
|
|
18872
|
+
formatDateIso(value) {
|
|
18873
|
+
if (value instanceof Date) return value.toISOString();
|
|
18874
|
+
if (value && typeof value.toDate === "function") {
|
|
18875
|
+
const d = value.toDate();
|
|
18876
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
18877
|
+
}
|
|
18878
|
+
return String(value != null ? value : "");
|
|
18879
|
+
}
|
|
18880
|
+
formatCsvValue(value) {
|
|
18881
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
18882
|
+
const escaped = str.replace(/"/g, '""');
|
|
18883
|
+
return `"${escaped}"`;
|
|
18884
|
+
}
|
|
18685
18885
|
};
|
|
18686
18886
|
|
|
18687
18887
|
// src/backoffice/services/technology.service.ts
|
|
18688
18888
|
var import_firestore61 = require("firebase/firestore");
|
|
18889
|
+
|
|
18890
|
+
// src/backoffice/types/product.types.ts
|
|
18891
|
+
var PRODUCTS_COLLECTION = "products";
|
|
18892
|
+
|
|
18893
|
+
// src/backoffice/services/technology.service.ts
|
|
18689
18894
|
var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
18690
18895
|
minimumLevel: "aesthetician" /* AESTHETICIAN */,
|
|
18691
18896
|
requiredSpecialties: []
|
|
@@ -18847,7 +19052,18 @@ var TechnologyService = class extends BaseService {
|
|
|
18847
19052
|
});
|
|
18848
19053
|
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
18849
19054
|
const docRef = (0, import_firestore61.doc)(this.technologiesRef, id);
|
|
19055
|
+
const beforeTech = await this.getById(id);
|
|
18850
19056
|
await (0, import_firestore61.updateDoc)(docRef, updateData);
|
|
19057
|
+
const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
|
|
19058
|
+
const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
|
|
19059
|
+
const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
|
|
19060
|
+
if (categoryChanged || subcategoryChanged || nameChanged) {
|
|
19061
|
+
await this.updateProductsInSubcollection(id, {
|
|
19062
|
+
categoryId: updateData.categoryId,
|
|
19063
|
+
subcategoryId: updateData.subcategoryId,
|
|
19064
|
+
technologyName: updateData.name
|
|
19065
|
+
});
|
|
19066
|
+
}
|
|
18851
19067
|
return this.getById(id);
|
|
18852
19068
|
}
|
|
18853
19069
|
/**
|
|
@@ -19283,18 +19499,239 @@ var TechnologyService = class extends BaseService {
|
|
|
19283
19499
|
})
|
|
19284
19500
|
);
|
|
19285
19501
|
}
|
|
19502
|
+
// ==========================================
|
|
19503
|
+
// NEW METHODS: Product assignment management
|
|
19504
|
+
// ==========================================
|
|
19505
|
+
/**
|
|
19506
|
+
* Assigns multiple products to a technology
|
|
19507
|
+
* Updates each product's assignedTechnologyIds array
|
|
19508
|
+
*/
|
|
19509
|
+
async assignProducts(technologyId, productIds) {
|
|
19510
|
+
const batch = (0, import_firestore61.writeBatch)(this.db);
|
|
19511
|
+
for (const productId of productIds) {
|
|
19512
|
+
const productRef = (0, import_firestore61.doc)(this.db, PRODUCTS_COLLECTION, productId);
|
|
19513
|
+
batch.update(productRef, {
|
|
19514
|
+
assignedTechnologyIds: (0, import_firestore61.arrayUnion)(technologyId),
|
|
19515
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19516
|
+
});
|
|
19517
|
+
}
|
|
19518
|
+
await batch.commit();
|
|
19519
|
+
}
|
|
19520
|
+
/**
|
|
19521
|
+
* Unassigns multiple products from a technology
|
|
19522
|
+
* Updates each product's assignedTechnologyIds array
|
|
19523
|
+
*/
|
|
19524
|
+
async unassignProducts(technologyId, productIds) {
|
|
19525
|
+
const batch = (0, import_firestore61.writeBatch)(this.db);
|
|
19526
|
+
for (const productId of productIds) {
|
|
19527
|
+
const productRef = (0, import_firestore61.doc)(this.db, PRODUCTS_COLLECTION, productId);
|
|
19528
|
+
batch.update(productRef, {
|
|
19529
|
+
assignedTechnologyIds: (0, import_firestore61.arrayRemove)(technologyId),
|
|
19530
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19531
|
+
});
|
|
19532
|
+
}
|
|
19533
|
+
await batch.commit();
|
|
19534
|
+
}
|
|
19535
|
+
/**
|
|
19536
|
+
* Gets products assigned to a specific technology
|
|
19537
|
+
* Reads from top-level collection for immediate consistency (Cloud Functions may lag)
|
|
19538
|
+
*/
|
|
19539
|
+
async getAssignedProducts(technologyId) {
|
|
19540
|
+
const q = (0, import_firestore61.query)(
|
|
19541
|
+
(0, import_firestore61.collection)(this.db, PRODUCTS_COLLECTION),
|
|
19542
|
+
(0, import_firestore61.where)("assignedTechnologyIds", "array-contains", technologyId),
|
|
19543
|
+
(0, import_firestore61.where)("isActive", "==", true),
|
|
19544
|
+
(0, import_firestore61.orderBy)("name")
|
|
19545
|
+
);
|
|
19546
|
+
const snapshot = await (0, import_firestore61.getDocs)(q);
|
|
19547
|
+
return snapshot.docs.map(
|
|
19548
|
+
(doc44) => ({
|
|
19549
|
+
id: doc44.id,
|
|
19550
|
+
...doc44.data()
|
|
19551
|
+
})
|
|
19552
|
+
);
|
|
19553
|
+
}
|
|
19554
|
+
/**
|
|
19555
|
+
* Gets products NOT assigned to a specific technology
|
|
19556
|
+
*/
|
|
19557
|
+
async getUnassignedProducts(technologyId) {
|
|
19558
|
+
const q = (0, import_firestore61.query)(
|
|
19559
|
+
(0, import_firestore61.collection)(this.db, PRODUCTS_COLLECTION),
|
|
19560
|
+
(0, import_firestore61.where)("isActive", "==", true),
|
|
19561
|
+
(0, import_firestore61.orderBy)("name")
|
|
19562
|
+
);
|
|
19563
|
+
const snapshot = await (0, import_firestore61.getDocs)(q);
|
|
19564
|
+
const allProducts = snapshot.docs.map(
|
|
19565
|
+
(doc44) => ({
|
|
19566
|
+
id: doc44.id,
|
|
19567
|
+
...doc44.data()
|
|
19568
|
+
})
|
|
19569
|
+
);
|
|
19570
|
+
return allProducts.filter(
|
|
19571
|
+
(product) => {
|
|
19572
|
+
var _a;
|
|
19573
|
+
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
19574
|
+
}
|
|
19575
|
+
);
|
|
19576
|
+
}
|
|
19577
|
+
/**
|
|
19578
|
+
* Gets product assignment statistics for a technology
|
|
19579
|
+
*/
|
|
19580
|
+
async getProductStats(technologyId) {
|
|
19581
|
+
const products = await this.getAssignedProducts(technologyId);
|
|
19582
|
+
const byBrand = {};
|
|
19583
|
+
products.forEach((product) => {
|
|
19584
|
+
byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
|
|
19585
|
+
});
|
|
19586
|
+
return {
|
|
19587
|
+
totalAssigned: products.length,
|
|
19588
|
+
byBrand
|
|
19589
|
+
};
|
|
19590
|
+
}
|
|
19591
|
+
/**
|
|
19592
|
+
* Updates products in technology subcollection when technology metadata changes
|
|
19593
|
+
* @param technologyId - ID of the technology
|
|
19594
|
+
* @param updates - Fields to update (categoryId, subcategoryId, technologyName)
|
|
19595
|
+
*/
|
|
19596
|
+
async updateProductsInSubcollection(technologyId, updates) {
|
|
19597
|
+
const productsRef = (0, import_firestore61.collection)(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
19598
|
+
const productsSnapshot = await (0, import_firestore61.getDocs)(productsRef);
|
|
19599
|
+
if (productsSnapshot.empty) {
|
|
19600
|
+
return;
|
|
19601
|
+
}
|
|
19602
|
+
const batch = (0, import_firestore61.writeBatch)(this.db);
|
|
19603
|
+
for (const productDoc of productsSnapshot.docs) {
|
|
19604
|
+
const productRef = productDoc.ref;
|
|
19605
|
+
const updateFields = {};
|
|
19606
|
+
if (updates.categoryId !== void 0) {
|
|
19607
|
+
updateFields.categoryId = updates.categoryId;
|
|
19608
|
+
}
|
|
19609
|
+
if (updates.subcategoryId !== void 0) {
|
|
19610
|
+
updateFields.subcategoryId = updates.subcategoryId;
|
|
19611
|
+
}
|
|
19612
|
+
if (updates.technologyName !== void 0) {
|
|
19613
|
+
updateFields.technologyName = updates.technologyName;
|
|
19614
|
+
}
|
|
19615
|
+
if (Object.keys(updateFields).length > 0) {
|
|
19616
|
+
batch.update(productRef, updateFields);
|
|
19617
|
+
}
|
|
19618
|
+
}
|
|
19619
|
+
await batch.commit();
|
|
19620
|
+
}
|
|
19621
|
+
/**
|
|
19622
|
+
* Exports technologies to CSV string, suitable for Excel/Sheets.
|
|
19623
|
+
* Includes headers and optional UTF-8 BOM.
|
|
19624
|
+
* By default exports only active technologies (set includeInactive to true to export all).
|
|
19625
|
+
* Includes product names from subcollections.
|
|
19626
|
+
*/
|
|
19627
|
+
async exportToCsv(options) {
|
|
19628
|
+
var _a, _b;
|
|
19629
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
19630
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
19631
|
+
const headers = [
|
|
19632
|
+
"id",
|
|
19633
|
+
"name",
|
|
19634
|
+
"description",
|
|
19635
|
+
"family",
|
|
19636
|
+
"categoryId",
|
|
19637
|
+
"subcategoryId",
|
|
19638
|
+
"technicalDetails",
|
|
19639
|
+
"requirements_pre",
|
|
19640
|
+
"requirements_post",
|
|
19641
|
+
"blockingConditions",
|
|
19642
|
+
"contraindications",
|
|
19643
|
+
"benefits",
|
|
19644
|
+
"certificationMinimumLevel",
|
|
19645
|
+
"certificationRequiredSpecialties",
|
|
19646
|
+
"documentationTemplateIds",
|
|
19647
|
+
"productNames",
|
|
19648
|
+
"isActive"
|
|
19649
|
+
];
|
|
19650
|
+
const rows = [];
|
|
19651
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
19652
|
+
const PAGE_SIZE = 1e3;
|
|
19653
|
+
let cursor;
|
|
19654
|
+
const constraints = [];
|
|
19655
|
+
if (!includeInactive) {
|
|
19656
|
+
constraints.push((0, import_firestore61.where)("isActive", "==", true));
|
|
19657
|
+
}
|
|
19658
|
+
constraints.push((0, import_firestore61.orderBy)("name"));
|
|
19659
|
+
while (true) {
|
|
19660
|
+
const queryConstraints = [...constraints, (0, import_firestore61.limit)(PAGE_SIZE)];
|
|
19661
|
+
if (cursor) queryConstraints.push((0, import_firestore61.startAfter)(cursor));
|
|
19662
|
+
const q = (0, import_firestore61.query)(this.technologiesRef, ...queryConstraints);
|
|
19663
|
+
const snapshot = await (0, import_firestore61.getDocs)(q);
|
|
19664
|
+
if (snapshot.empty) break;
|
|
19665
|
+
for (const d of snapshot.docs) {
|
|
19666
|
+
const technology = { id: d.id, ...d.data() };
|
|
19667
|
+
const productNames = await this.getProductNamesForTechnology(technology.id);
|
|
19668
|
+
rows.push(this.technologyToCsvRow(technology, productNames));
|
|
19669
|
+
}
|
|
19670
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
19671
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
19672
|
+
}
|
|
19673
|
+
const csvBody = rows.join("\r\n");
|
|
19674
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
19675
|
+
}
|
|
19676
|
+
/**
|
|
19677
|
+
* Gets product names from the technology's product subcollection
|
|
19678
|
+
*/
|
|
19679
|
+
async getProductNamesForTechnology(technologyId) {
|
|
19680
|
+
try {
|
|
19681
|
+
const productsRef = (0, import_firestore61.collection)(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
19682
|
+
const q = (0, import_firestore61.query)(productsRef, (0, import_firestore61.where)("isActive", "==", true));
|
|
19683
|
+
const snapshot = await (0, import_firestore61.getDocs)(q);
|
|
19684
|
+
return snapshot.docs.map((doc44) => {
|
|
19685
|
+
const product = doc44.data();
|
|
19686
|
+
return product.name || "";
|
|
19687
|
+
}).filter((name) => name);
|
|
19688
|
+
} catch (error) {
|
|
19689
|
+
console.error(`Error fetching products for technology ${technologyId}:`, error);
|
|
19690
|
+
return [];
|
|
19691
|
+
}
|
|
19692
|
+
}
|
|
19693
|
+
technologyToCsvRow(technology, productNames = []) {
|
|
19694
|
+
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;
|
|
19695
|
+
const values = [
|
|
19696
|
+
(_a = technology.id) != null ? _a : "",
|
|
19697
|
+
(_b = technology.name) != null ? _b : "",
|
|
19698
|
+
(_c = technology.description) != null ? _c : "",
|
|
19699
|
+
(_d = technology.family) != null ? _d : "",
|
|
19700
|
+
(_e = technology.categoryId) != null ? _e : "",
|
|
19701
|
+
(_f = technology.subcategoryId) != null ? _f : "",
|
|
19702
|
+
(_g = technology.technicalDetails) != null ? _g : "",
|
|
19703
|
+
(_j = (_i = (_h = technology.requirements) == null ? void 0 : _h.pre) == null ? void 0 : _i.map((r) => r.name).join(";")) != null ? _j : "",
|
|
19704
|
+
(_m = (_l = (_k = technology.requirements) == null ? void 0 : _k.post) == null ? void 0 : _l.map((r) => r.name).join(";")) != null ? _m : "",
|
|
19705
|
+
(_o = (_n = technology.blockingConditions) == null ? void 0 : _n.join(";")) != null ? _o : "",
|
|
19706
|
+
(_q = (_p = technology.contraindications) == null ? void 0 : _p.map((c) => c.name).join(";")) != null ? _q : "",
|
|
19707
|
+
(_s = (_r = technology.benefits) == null ? void 0 : _r.map((b) => b.name).join(";")) != null ? _s : "",
|
|
19708
|
+
(_u = (_t = technology.certificationRequirement) == null ? void 0 : _t.minimumLevel) != null ? _u : "",
|
|
19709
|
+
(_x = (_w = (_v = technology.certificationRequirement) == null ? void 0 : _v.requiredSpecialties) == null ? void 0 : _w.join(";")) != null ? _x : "",
|
|
19710
|
+
(_z = (_y = technology.documentationTemplates) == null ? void 0 : _y.map((t) => t.templateId).join(";")) != null ? _z : "",
|
|
19711
|
+
productNames.join(";"),
|
|
19712
|
+
String((_A = technology.isActive) != null ? _A : "")
|
|
19713
|
+
];
|
|
19714
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
19715
|
+
}
|
|
19716
|
+
formatCsvValue(value) {
|
|
19717
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
19718
|
+
const escaped = str.replace(/"/g, '""');
|
|
19719
|
+
return `"${escaped}"`;
|
|
19720
|
+
}
|
|
19286
19721
|
};
|
|
19287
19722
|
|
|
19288
19723
|
// src/backoffice/services/product.service.ts
|
|
19289
19724
|
var import_firestore62 = require("firebase/firestore");
|
|
19290
|
-
|
|
19291
|
-
// src/backoffice/types/product.types.ts
|
|
19292
|
-
var PRODUCTS_COLLECTION = "products";
|
|
19293
|
-
|
|
19294
|
-
// src/backoffice/services/product.service.ts
|
|
19295
19725
|
var ProductService = class extends BaseService {
|
|
19296
19726
|
/**
|
|
19297
|
-
* Gets reference to products collection
|
|
19727
|
+
* Gets reference to top-level products collection (source of truth)
|
|
19728
|
+
* @returns Firestore collection reference
|
|
19729
|
+
*/
|
|
19730
|
+
getTopLevelProductsRef() {
|
|
19731
|
+
return (0, import_firestore62.collection)(this.db, PRODUCTS_COLLECTION);
|
|
19732
|
+
}
|
|
19733
|
+
/**
|
|
19734
|
+
* Gets reference to products collection under a technology (backward compatibility)
|
|
19298
19735
|
* @param technologyId - ID of the technology
|
|
19299
19736
|
* @returns Firestore collection reference
|
|
19300
19737
|
*/
|
|
@@ -19310,6 +19747,7 @@ var ProductService = class extends BaseService {
|
|
|
19310
19747
|
...product,
|
|
19311
19748
|
brandId,
|
|
19312
19749
|
technologyId,
|
|
19750
|
+
// Required for old subcollection structure
|
|
19313
19751
|
createdAt: now,
|
|
19314
19752
|
updatedAt: now,
|
|
19315
19753
|
isActive: true
|
|
@@ -19369,30 +19807,26 @@ var ProductService = class extends BaseService {
|
|
|
19369
19807
|
}
|
|
19370
19808
|
/**
|
|
19371
19809
|
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
19372
|
-
*
|
|
19810
|
+
* Queries technology subcollections which have the legacy fields synced by Cloud Functions.
|
|
19373
19811
|
*/
|
|
19374
19812
|
async getProductCounts() {
|
|
19375
|
-
const q = (0, import_firestore62.query)((0, import_firestore62.collectionGroup)(this.db, PRODUCTS_COLLECTION), (0, import_firestore62.where)("isActive", "==", true));
|
|
19376
|
-
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
19377
19813
|
const counts = {
|
|
19378
19814
|
byCategory: {},
|
|
19379
19815
|
bySubcategory: {},
|
|
19380
19816
|
byTechnology: {}
|
|
19381
19817
|
};
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
}
|
|
19818
|
+
const q = (0, import_firestore62.query)((0, import_firestore62.collectionGroup)(this.db, PRODUCTS_COLLECTION), (0, import_firestore62.where)("isActive", "==", true));
|
|
19819
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
19385
19820
|
snapshot.docs.forEach((doc44) => {
|
|
19386
19821
|
const product = doc44.data();
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
19822
|
+
if (product.categoryId) {
|
|
19823
|
+
counts.byCategory[product.categoryId] = (counts.byCategory[product.categoryId] || 0) + 1;
|
|
19390
19824
|
}
|
|
19391
|
-
if (subcategoryId) {
|
|
19392
|
-
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
19825
|
+
if (product.subcategoryId) {
|
|
19826
|
+
counts.bySubcategory[product.subcategoryId] = (counts.bySubcategory[product.subcategoryId] || 0) + 1;
|
|
19393
19827
|
}
|
|
19394
|
-
if (technologyId) {
|
|
19395
|
-
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
19828
|
+
if (product.technologyId) {
|
|
19829
|
+
counts.byTechnology[product.technologyId] = (counts.byTechnology[product.technologyId] || 0) + 1;
|
|
19396
19830
|
}
|
|
19397
19831
|
});
|
|
19398
19832
|
return counts;
|
|
@@ -19471,6 +19905,241 @@ var ProductService = class extends BaseService {
|
|
|
19471
19905
|
...docSnap.data()
|
|
19472
19906
|
};
|
|
19473
19907
|
}
|
|
19908
|
+
// ==========================================
|
|
19909
|
+
// NEW METHODS: Top-level collection (preferred)
|
|
19910
|
+
// ==========================================
|
|
19911
|
+
/**
|
|
19912
|
+
* Creates a new product in the top-level collection
|
|
19913
|
+
*/
|
|
19914
|
+
async createTopLevel(brandId, product, technologyIds = []) {
|
|
19915
|
+
const now = /* @__PURE__ */ new Date();
|
|
19916
|
+
const newProduct = {
|
|
19917
|
+
...product,
|
|
19918
|
+
brandId,
|
|
19919
|
+
assignedTechnologyIds: technologyIds,
|
|
19920
|
+
createdAt: now,
|
|
19921
|
+
updatedAt: now,
|
|
19922
|
+
isActive: true
|
|
19923
|
+
};
|
|
19924
|
+
const productRef = await (0, import_firestore62.addDoc)(this.getTopLevelProductsRef(), newProduct);
|
|
19925
|
+
return { id: productRef.id, ...newProduct };
|
|
19926
|
+
}
|
|
19927
|
+
/**
|
|
19928
|
+
* Gets all products from the top-level collection
|
|
19929
|
+
*/
|
|
19930
|
+
async getAllTopLevel(options) {
|
|
19931
|
+
const { rowsPerPage, lastVisible, brandId } = options;
|
|
19932
|
+
const constraints = [(0, import_firestore62.where)("isActive", "==", true), (0, import_firestore62.orderBy)("name")];
|
|
19933
|
+
if (brandId) {
|
|
19934
|
+
constraints.push((0, import_firestore62.where)("brandId", "==", brandId));
|
|
19935
|
+
}
|
|
19936
|
+
if (lastVisible) {
|
|
19937
|
+
constraints.push((0, import_firestore62.startAfter)(lastVisible));
|
|
19938
|
+
}
|
|
19939
|
+
constraints.push((0, import_firestore62.limit)(rowsPerPage));
|
|
19940
|
+
const q = (0, import_firestore62.query)(this.getTopLevelProductsRef(), ...constraints);
|
|
19941
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
19942
|
+
const products = snapshot.docs.map(
|
|
19943
|
+
(doc44) => ({
|
|
19944
|
+
id: doc44.id,
|
|
19945
|
+
...doc44.data()
|
|
19946
|
+
})
|
|
19947
|
+
);
|
|
19948
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
19949
|
+
return { products, lastVisible: newLastVisible };
|
|
19950
|
+
}
|
|
19951
|
+
/**
|
|
19952
|
+
* Gets a product by ID from the top-level collection
|
|
19953
|
+
*/
|
|
19954
|
+
async getByIdTopLevel(productId) {
|
|
19955
|
+
const docRef = (0, import_firestore62.doc)(this.getTopLevelProductsRef(), productId);
|
|
19956
|
+
const docSnap = await (0, import_firestore62.getDoc)(docRef);
|
|
19957
|
+
if (!docSnap.exists()) return null;
|
|
19958
|
+
return {
|
|
19959
|
+
id: docSnap.id,
|
|
19960
|
+
...docSnap.data()
|
|
19961
|
+
};
|
|
19962
|
+
}
|
|
19963
|
+
/**
|
|
19964
|
+
* Updates a product in the top-level collection
|
|
19965
|
+
*/
|
|
19966
|
+
async updateTopLevel(productId, product) {
|
|
19967
|
+
const updateData = {
|
|
19968
|
+
...product,
|
|
19969
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19970
|
+
};
|
|
19971
|
+
const docRef = (0, import_firestore62.doc)(this.getTopLevelProductsRef(), productId);
|
|
19972
|
+
await (0, import_firestore62.updateDoc)(docRef, updateData);
|
|
19973
|
+
return this.getByIdTopLevel(productId);
|
|
19974
|
+
}
|
|
19975
|
+
/**
|
|
19976
|
+
* Deletes a product from the top-level collection (soft delete)
|
|
19977
|
+
*/
|
|
19978
|
+
async deleteTopLevel(productId) {
|
|
19979
|
+
await this.updateTopLevel(productId, {
|
|
19980
|
+
isActive: false
|
|
19981
|
+
});
|
|
19982
|
+
}
|
|
19983
|
+
/**
|
|
19984
|
+
* Assigns a product to a technology
|
|
19985
|
+
*/
|
|
19986
|
+
async assignToTechnology(productId, technologyId) {
|
|
19987
|
+
const docRef = (0, import_firestore62.doc)(this.getTopLevelProductsRef(), productId);
|
|
19988
|
+
await (0, import_firestore62.updateDoc)(docRef, {
|
|
19989
|
+
assignedTechnologyIds: (0, import_firestore62.arrayUnion)(technologyId),
|
|
19990
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19991
|
+
});
|
|
19992
|
+
}
|
|
19993
|
+
/**
|
|
19994
|
+
* Unassigns a product from a technology
|
|
19995
|
+
*/
|
|
19996
|
+
async unassignFromTechnology(productId, technologyId) {
|
|
19997
|
+
const docRef = (0, import_firestore62.doc)(this.getTopLevelProductsRef(), productId);
|
|
19998
|
+
await (0, import_firestore62.updateDoc)(docRef, {
|
|
19999
|
+
assignedTechnologyIds: (0, import_firestore62.arrayRemove)(technologyId),
|
|
20000
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
20001
|
+
});
|
|
20002
|
+
}
|
|
20003
|
+
/**
|
|
20004
|
+
* Gets products assigned to a specific technology
|
|
20005
|
+
*/
|
|
20006
|
+
async getAssignedProducts(technologyId) {
|
|
20007
|
+
const q = (0, import_firestore62.query)(
|
|
20008
|
+
this.getTopLevelProductsRef(),
|
|
20009
|
+
(0, import_firestore62.where)("assignedTechnologyIds", "array-contains", technologyId),
|
|
20010
|
+
(0, import_firestore62.where)("isActive", "==", true),
|
|
20011
|
+
(0, import_firestore62.orderBy)("name")
|
|
20012
|
+
);
|
|
20013
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
20014
|
+
return snapshot.docs.map(
|
|
20015
|
+
(doc44) => ({
|
|
20016
|
+
id: doc44.id,
|
|
20017
|
+
...doc44.data()
|
|
20018
|
+
})
|
|
20019
|
+
);
|
|
20020
|
+
}
|
|
20021
|
+
/**
|
|
20022
|
+
* Gets products NOT assigned to a specific technology
|
|
20023
|
+
*/
|
|
20024
|
+
async getUnassignedProducts(technologyId) {
|
|
20025
|
+
const q = (0, import_firestore62.query)(
|
|
20026
|
+
this.getTopLevelProductsRef(),
|
|
20027
|
+
(0, import_firestore62.where)("isActive", "==", true),
|
|
20028
|
+
(0, import_firestore62.orderBy)("name")
|
|
20029
|
+
);
|
|
20030
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
20031
|
+
const allProducts = snapshot.docs.map(
|
|
20032
|
+
(doc44) => ({
|
|
20033
|
+
id: doc44.id,
|
|
20034
|
+
...doc44.data()
|
|
20035
|
+
})
|
|
20036
|
+
);
|
|
20037
|
+
return allProducts.filter(
|
|
20038
|
+
(product) => {
|
|
20039
|
+
var _a;
|
|
20040
|
+
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
20041
|
+
}
|
|
20042
|
+
);
|
|
20043
|
+
}
|
|
20044
|
+
/**
|
|
20045
|
+
* Gets all products for a brand (from top-level collection)
|
|
20046
|
+
*/
|
|
20047
|
+
async getByBrand(brandId) {
|
|
20048
|
+
const q = (0, import_firestore62.query)(
|
|
20049
|
+
this.getTopLevelProductsRef(),
|
|
20050
|
+
(0, import_firestore62.where)("brandId", "==", brandId),
|
|
20051
|
+
(0, import_firestore62.where)("isActive", "==", true),
|
|
20052
|
+
(0, import_firestore62.orderBy)("name")
|
|
20053
|
+
);
|
|
20054
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
20055
|
+
return snapshot.docs.map(
|
|
20056
|
+
(doc44) => ({
|
|
20057
|
+
id: doc44.id,
|
|
20058
|
+
...doc44.data()
|
|
20059
|
+
})
|
|
20060
|
+
);
|
|
20061
|
+
}
|
|
20062
|
+
/**
|
|
20063
|
+
* Exports products to CSV string, suitable for Excel/Sheets.
|
|
20064
|
+
* Includes headers and optional UTF-8 BOM.
|
|
20065
|
+
* By default exports only active products (set includeInactive to true to export all).
|
|
20066
|
+
*/
|
|
20067
|
+
async exportToCsv(options) {
|
|
20068
|
+
var _a, _b;
|
|
20069
|
+
const includeInactive = (_a = options == null ? void 0 : options.includeInactive) != null ? _a : false;
|
|
20070
|
+
const includeBom = (_b = options == null ? void 0 : options.includeBom) != null ? _b : true;
|
|
20071
|
+
const headers = [
|
|
20072
|
+
"id",
|
|
20073
|
+
"name",
|
|
20074
|
+
"brandId",
|
|
20075
|
+
"brandName",
|
|
20076
|
+
"assignedTechnologyIds",
|
|
20077
|
+
"description",
|
|
20078
|
+
"technicalDetails",
|
|
20079
|
+
"dosage",
|
|
20080
|
+
"composition",
|
|
20081
|
+
"indications",
|
|
20082
|
+
"contraindications",
|
|
20083
|
+
"warnings",
|
|
20084
|
+
"isActive"
|
|
20085
|
+
];
|
|
20086
|
+
const rows = [];
|
|
20087
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20088
|
+
const PAGE_SIZE = 1e3;
|
|
20089
|
+
let cursor;
|
|
20090
|
+
const constraints = [];
|
|
20091
|
+
if (!includeInactive) {
|
|
20092
|
+
constraints.push((0, import_firestore62.where)("isActive", "==", true));
|
|
20093
|
+
}
|
|
20094
|
+
constraints.push((0, import_firestore62.orderBy)("name"));
|
|
20095
|
+
while (true) {
|
|
20096
|
+
const queryConstraints = [...constraints, (0, import_firestore62.limit)(PAGE_SIZE)];
|
|
20097
|
+
if (cursor) queryConstraints.push((0, import_firestore62.startAfter)(cursor));
|
|
20098
|
+
const q = (0, import_firestore62.query)(this.getTopLevelProductsRef(), ...queryConstraints);
|
|
20099
|
+
const snapshot = await (0, import_firestore62.getDocs)(q);
|
|
20100
|
+
if (snapshot.empty) break;
|
|
20101
|
+
for (const d of snapshot.docs) {
|
|
20102
|
+
const product = { id: d.id, ...d.data() };
|
|
20103
|
+
rows.push(this.productToCsvRow(product));
|
|
20104
|
+
}
|
|
20105
|
+
cursor = snapshot.docs[snapshot.docs.length - 1];
|
|
20106
|
+
if (snapshot.size < PAGE_SIZE) break;
|
|
20107
|
+
}
|
|
20108
|
+
const csvBody = rows.join("\r\n");
|
|
20109
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20110
|
+
}
|
|
20111
|
+
productToCsvRow(product) {
|
|
20112
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
|
|
20113
|
+
const values = [
|
|
20114
|
+
(_a = product.id) != null ? _a : "",
|
|
20115
|
+
(_b = product.name) != null ? _b : "",
|
|
20116
|
+
(_c = product.brandId) != null ? _c : "",
|
|
20117
|
+
(_d = product.brandName) != null ? _d : "",
|
|
20118
|
+
(_f = (_e = product.assignedTechnologyIds) == null ? void 0 : _e.join(";")) != null ? _f : "",
|
|
20119
|
+
(_g = product.description) != null ? _g : "",
|
|
20120
|
+
(_h = product.technicalDetails) != null ? _h : "",
|
|
20121
|
+
(_i = product.dosage) != null ? _i : "",
|
|
20122
|
+
(_j = product.composition) != null ? _j : "",
|
|
20123
|
+
(_l = (_k = product.indications) == null ? void 0 : _k.join(";")) != null ? _l : "",
|
|
20124
|
+
(_n = (_m = product.contraindications) == null ? void 0 : _m.map((c) => c.name).join(";")) != null ? _n : "",
|
|
20125
|
+
(_p = (_o = product.warnings) == null ? void 0 : _o.join(";")) != null ? _p : "",
|
|
20126
|
+
String((_q = product.isActive) != null ? _q : "")
|
|
20127
|
+
];
|
|
20128
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20129
|
+
}
|
|
20130
|
+
formatDateIso(value) {
|
|
20131
|
+
if (value instanceof Date) return value.toISOString();
|
|
20132
|
+
if (value && typeof value.toDate === "function") {
|
|
20133
|
+
const d = value.toDate();
|
|
20134
|
+
return d instanceof Date ? d.toISOString() : String(value);
|
|
20135
|
+
}
|
|
20136
|
+
return String(value != null ? value : "");
|
|
20137
|
+
}
|
|
20138
|
+
formatCsvValue(value) {
|
|
20139
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
20140
|
+
const escaped = str.replace(/"/g, '""');
|
|
20141
|
+
return `"${escaped}"`;
|
|
20142
|
+
}
|
|
19474
20143
|
};
|
|
19475
20144
|
|
|
19476
20145
|
// src/backoffice/services/constants.service.ts
|
|
@@ -19703,6 +20372,66 @@ var ConstantsService = class extends BaseService {
|
|
|
19703
20372
|
contraindications: (0, import_firestore63.arrayRemove)(toRemove)
|
|
19704
20373
|
});
|
|
19705
20374
|
}
|
|
20375
|
+
// =================================================================
|
|
20376
|
+
// CSV Export Methods
|
|
20377
|
+
// =================================================================
|
|
20378
|
+
/**
|
|
20379
|
+
* Exports treatment benefits to CSV string, suitable for Excel/Sheets.
|
|
20380
|
+
* Includes headers and optional UTF-8 BOM.
|
|
20381
|
+
*/
|
|
20382
|
+
async exportBenefitsToCsv(options) {
|
|
20383
|
+
var _a;
|
|
20384
|
+
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
20385
|
+
const headers = ["id", "name", "description"];
|
|
20386
|
+
const rows = [];
|
|
20387
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20388
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
20389
|
+
for (const benefit of benefits) {
|
|
20390
|
+
rows.push(this.benefitToCsvRow(benefit));
|
|
20391
|
+
}
|
|
20392
|
+
const csvBody = rows.join("\r\n");
|
|
20393
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20394
|
+
}
|
|
20395
|
+
/**
|
|
20396
|
+
* Exports contraindications to CSV string, suitable for Excel/Sheets.
|
|
20397
|
+
* Includes headers and optional UTF-8 BOM.
|
|
20398
|
+
*/
|
|
20399
|
+
async exportContraindicationsToCsv(options) {
|
|
20400
|
+
var _a;
|
|
20401
|
+
const includeBom = (_a = options == null ? void 0 : options.includeBom) != null ? _a : true;
|
|
20402
|
+
const headers = ["id", "name", "description"];
|
|
20403
|
+
const rows = [];
|
|
20404
|
+
rows.push(headers.map((h) => this.formatCsvValue(h)).join(","));
|
|
20405
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
20406
|
+
for (const contraindication of contraindications) {
|
|
20407
|
+
rows.push(this.contraindicationToCsvRow(contraindication));
|
|
20408
|
+
}
|
|
20409
|
+
const csvBody = rows.join("\r\n");
|
|
20410
|
+
return includeBom ? "\uFEFF" + csvBody : csvBody;
|
|
20411
|
+
}
|
|
20412
|
+
benefitToCsvRow(benefit) {
|
|
20413
|
+
var _a, _b, _c;
|
|
20414
|
+
const values = [
|
|
20415
|
+
(_a = benefit.id) != null ? _a : "",
|
|
20416
|
+
(_b = benefit.name) != null ? _b : "",
|
|
20417
|
+
(_c = benefit.description) != null ? _c : ""
|
|
20418
|
+
];
|
|
20419
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20420
|
+
}
|
|
20421
|
+
contraindicationToCsvRow(contraindication) {
|
|
20422
|
+
var _a, _b, _c;
|
|
20423
|
+
const values = [
|
|
20424
|
+
(_a = contraindication.id) != null ? _a : "",
|
|
20425
|
+
(_b = contraindication.name) != null ? _b : "",
|
|
20426
|
+
(_c = contraindication.description) != null ? _c : ""
|
|
20427
|
+
];
|
|
20428
|
+
return values.map((v) => this.formatCsvValue(v)).join(",");
|
|
20429
|
+
}
|
|
20430
|
+
formatCsvValue(value) {
|
|
20431
|
+
const str = value === null || value === void 0 ? "" : String(value);
|
|
20432
|
+
const escaped = str.replace(/"/g, '""');
|
|
20433
|
+
return `"${escaped}"`;
|
|
20434
|
+
}
|
|
19706
20435
|
};
|
|
19707
20436
|
|
|
19708
20437
|
// src/backoffice/types/static/contraindication.types.ts
|