@blackcode_sa/metaestetics-api 1.12.40 → 1.12.42

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.
@@ -1158,7 +1158,9 @@ import {
1158
1158
  limit as limit6,
1159
1159
  orderBy as orderBy6,
1160
1160
  startAfter as startAfter5,
1161
- getCountFromServer as getCountFromServer4
1161
+ getCountFromServer as getCountFromServer4,
1162
+ arrayUnion,
1163
+ arrayRemove
1162
1164
  } from "firebase/firestore";
1163
1165
 
1164
1166
  // src/backoffice/types/product.types.ts
@@ -1170,7 +1172,14 @@ var TECHNOLOGIES_COLLECTION = "technologies";
1170
1172
  // src/backoffice/services/product.service.ts
1171
1173
  var ProductService = class extends BaseService {
1172
1174
  /**
1173
- * Gets reference to products collection under a technology
1175
+ * Gets reference to top-level products collection (source of truth)
1176
+ * @returns Firestore collection reference
1177
+ */
1178
+ getTopLevelProductsRef() {
1179
+ return collection6(this.db, PRODUCTS_COLLECTION);
1180
+ }
1181
+ /**
1182
+ * Gets reference to products collection under a technology (backward compatibility)
1174
1183
  * @param technologyId - ID of the technology
1175
1184
  * @returns Firestore collection reference
1176
1185
  */
@@ -1186,6 +1195,7 @@ var ProductService = class extends BaseService {
1186
1195
  ...product,
1187
1196
  brandId,
1188
1197
  technologyId,
1198
+ // Required for old subcollection structure
1189
1199
  createdAt: now,
1190
1200
  updatedAt: now,
1191
1201
  isActive: true
@@ -1245,30 +1255,26 @@ var ProductService = class extends BaseService {
1245
1255
  }
1246
1256
  /**
1247
1257
  * Gets counts of active products grouped by category, subcategory, and technology.
1248
- * This uses a single collectionGroup query for efficiency.
1258
+ * Queries technology subcollections which have the legacy fields synced by Cloud Functions.
1249
1259
  */
1250
1260
  async getProductCounts() {
1251
- const q = query6(collectionGroup(this.db, PRODUCTS_COLLECTION), where6("isActive", "==", true));
1252
- const snapshot = await getDocs6(q);
1253
1261
  const counts = {
1254
1262
  byCategory: {},
1255
1263
  bySubcategory: {},
1256
1264
  byTechnology: {}
1257
1265
  };
1258
- if (snapshot.empty) {
1259
- return counts;
1260
- }
1266
+ const q = query6(collectionGroup(this.db, PRODUCTS_COLLECTION), where6("isActive", "==", true));
1267
+ const snapshot = await getDocs6(q);
1261
1268
  snapshot.docs.forEach((doc11) => {
1262
1269
  const product = doc11.data();
1263
- const { categoryId, subcategoryId, technologyId } = product;
1264
- if (categoryId) {
1265
- counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
1270
+ if (product.categoryId) {
1271
+ counts.byCategory[product.categoryId] = (counts.byCategory[product.categoryId] || 0) + 1;
1266
1272
  }
1267
- if (subcategoryId) {
1268
- counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
1273
+ if (product.subcategoryId) {
1274
+ counts.bySubcategory[product.subcategoryId] = (counts.bySubcategory[product.subcategoryId] || 0) + 1;
1269
1275
  }
1270
- if (technologyId) {
1271
- counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
1276
+ if (product.technologyId) {
1277
+ counts.byTechnology[product.technologyId] = (counts.byTechnology[product.technologyId] || 0) + 1;
1272
1278
  }
1273
1279
  });
1274
1280
  return counts;
@@ -1347,6 +1353,160 @@ var ProductService = class extends BaseService {
1347
1353
  ...docSnap.data()
1348
1354
  };
1349
1355
  }
1356
+ // ==========================================
1357
+ // NEW METHODS: Top-level collection (preferred)
1358
+ // ==========================================
1359
+ /**
1360
+ * Creates a new product in the top-level collection
1361
+ */
1362
+ async createTopLevel(brandId, product, technologyIds = []) {
1363
+ const now = /* @__PURE__ */ new Date();
1364
+ const newProduct = {
1365
+ ...product,
1366
+ brandId,
1367
+ assignedTechnologyIds: technologyIds,
1368
+ createdAt: now,
1369
+ updatedAt: now,
1370
+ isActive: true
1371
+ };
1372
+ const productRef = await addDoc3(this.getTopLevelProductsRef(), newProduct);
1373
+ return { id: productRef.id, ...newProduct };
1374
+ }
1375
+ /**
1376
+ * Gets all products from the top-level collection
1377
+ */
1378
+ async getAllTopLevel(options) {
1379
+ const { rowsPerPage, lastVisible, brandId } = options;
1380
+ const constraints = [where6("isActive", "==", true), orderBy6("name")];
1381
+ if (brandId) {
1382
+ constraints.push(where6("brandId", "==", brandId));
1383
+ }
1384
+ if (lastVisible) {
1385
+ constraints.push(startAfter5(lastVisible));
1386
+ }
1387
+ constraints.push(limit6(rowsPerPage));
1388
+ const q = query6(this.getTopLevelProductsRef(), ...constraints);
1389
+ const snapshot = await getDocs6(q);
1390
+ const products = snapshot.docs.map(
1391
+ (doc11) => ({
1392
+ id: doc11.id,
1393
+ ...doc11.data()
1394
+ })
1395
+ );
1396
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
1397
+ return { products, lastVisible: newLastVisible };
1398
+ }
1399
+ /**
1400
+ * Gets a product by ID from the top-level collection
1401
+ */
1402
+ async getByIdTopLevel(productId) {
1403
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1404
+ const docSnap = await getDoc6(docRef);
1405
+ if (!docSnap.exists()) return null;
1406
+ return {
1407
+ id: docSnap.id,
1408
+ ...docSnap.data()
1409
+ };
1410
+ }
1411
+ /**
1412
+ * Updates a product in the top-level collection
1413
+ */
1414
+ async updateTopLevel(productId, product) {
1415
+ const updateData = {
1416
+ ...product,
1417
+ updatedAt: /* @__PURE__ */ new Date()
1418
+ };
1419
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1420
+ await updateDoc6(docRef, updateData);
1421
+ return this.getByIdTopLevel(productId);
1422
+ }
1423
+ /**
1424
+ * Deletes a product from the top-level collection (soft delete)
1425
+ */
1426
+ async deleteTopLevel(productId) {
1427
+ await this.updateTopLevel(productId, {
1428
+ isActive: false
1429
+ });
1430
+ }
1431
+ /**
1432
+ * Assigns a product to a technology
1433
+ */
1434
+ async assignToTechnology(productId, technologyId) {
1435
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1436
+ await updateDoc6(docRef, {
1437
+ assignedTechnologyIds: arrayUnion(technologyId),
1438
+ updatedAt: /* @__PURE__ */ new Date()
1439
+ });
1440
+ }
1441
+ /**
1442
+ * Unassigns a product from a technology
1443
+ */
1444
+ async unassignFromTechnology(productId, technologyId) {
1445
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1446
+ await updateDoc6(docRef, {
1447
+ assignedTechnologyIds: arrayRemove(technologyId),
1448
+ updatedAt: /* @__PURE__ */ new Date()
1449
+ });
1450
+ }
1451
+ /**
1452
+ * Gets products assigned to a specific technology
1453
+ */
1454
+ async getAssignedProducts(technologyId) {
1455
+ const q = query6(
1456
+ this.getTopLevelProductsRef(),
1457
+ where6("assignedTechnologyIds", "array-contains", technologyId),
1458
+ where6("isActive", "==", true),
1459
+ orderBy6("name")
1460
+ );
1461
+ const snapshot = await getDocs6(q);
1462
+ return snapshot.docs.map(
1463
+ (doc11) => ({
1464
+ id: doc11.id,
1465
+ ...doc11.data()
1466
+ })
1467
+ );
1468
+ }
1469
+ /**
1470
+ * Gets products NOT assigned to a specific technology
1471
+ */
1472
+ async getUnassignedProducts(technologyId) {
1473
+ const q = query6(
1474
+ this.getTopLevelProductsRef(),
1475
+ where6("isActive", "==", true),
1476
+ orderBy6("name")
1477
+ );
1478
+ const snapshot = await getDocs6(q);
1479
+ const allProducts = snapshot.docs.map(
1480
+ (doc11) => ({
1481
+ id: doc11.id,
1482
+ ...doc11.data()
1483
+ })
1484
+ );
1485
+ return allProducts.filter(
1486
+ (product) => {
1487
+ var _a;
1488
+ return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
1489
+ }
1490
+ );
1491
+ }
1492
+ /**
1493
+ * Gets all products for a brand (from top-level collection)
1494
+ */
1495
+ async getByBrand(brandId) {
1496
+ const q = query6(
1497
+ this.getTopLevelProductsRef(),
1498
+ where6("brandId", "==", brandId),
1499
+ where6("isActive", "==", true),
1500
+ orderBy6("name")
1501
+ );
1502
+ const snapshot = await getDocs6(q);
1503
+ return snapshot.docs.map(
1504
+ (doc11) => ({
1505
+ id: doc11.id,
1506
+ ...doc11.data()
1507
+ })
1508
+ );
1509
+ }
1350
1510
  };
1351
1511
 
1352
1512
  // src/backoffice/services/requirement.service.ts
@@ -1737,8 +1897,9 @@ import {
1737
1897
  startAfter as startAfter7,
1738
1898
  updateDoc as updateDoc9,
1739
1899
  where as where9,
1740
- arrayUnion,
1741
- arrayRemove
1900
+ arrayUnion as arrayUnion2,
1901
+ arrayRemove as arrayRemove2,
1902
+ writeBatch
1742
1903
  } from "firebase/firestore";
1743
1904
 
1744
1905
  // src/backoffice/types/static/certification.types.ts
@@ -1927,7 +2088,18 @@ var TechnologyService = class extends BaseService {
1927
2088
  });
1928
2089
  updateData.updatedAt = /* @__PURE__ */ new Date();
1929
2090
  const docRef = doc9(this.technologiesRef, id);
2091
+ const beforeTech = await this.getById(id);
1930
2092
  await updateDoc9(docRef, updateData);
2093
+ const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
2094
+ const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
2095
+ const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
2096
+ if (categoryChanged || subcategoryChanged || nameChanged) {
2097
+ await this.updateProductsInSubcollection(id, {
2098
+ categoryId: updateData.categoryId,
2099
+ subcategoryId: updateData.subcategoryId,
2100
+ technologyName: updateData.name
2101
+ });
2102
+ }
1931
2103
  return this.getById(id);
1932
2104
  }
1933
2105
  /**
@@ -1968,7 +2140,7 @@ var TechnologyService = class extends BaseService {
1968
2140
  const docRef = doc9(this.technologiesRef, technologyId);
1969
2141
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1970
2142
  await updateDoc9(docRef, {
1971
- [requirementType]: arrayUnion(requirement),
2143
+ [requirementType]: arrayUnion2(requirement),
1972
2144
  updatedAt: /* @__PURE__ */ new Date()
1973
2145
  });
1974
2146
  return this.getById(technologyId);
@@ -1983,7 +2155,7 @@ var TechnologyService = class extends BaseService {
1983
2155
  const docRef = doc9(this.technologiesRef, technologyId);
1984
2156
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1985
2157
  await updateDoc9(docRef, {
1986
- [requirementType]: arrayRemove(requirement),
2158
+ [requirementType]: arrayRemove2(requirement),
1987
2159
  updatedAt: /* @__PURE__ */ new Date()
1988
2160
  });
1989
2161
  return this.getById(technologyId);
@@ -2022,7 +2194,7 @@ var TechnologyService = class extends BaseService {
2022
2194
  async addBlockingCondition(technologyId, condition) {
2023
2195
  const docRef = doc9(this.technologiesRef, technologyId);
2024
2196
  await updateDoc9(docRef, {
2025
- blockingConditions: arrayUnion(condition),
2197
+ blockingConditions: arrayUnion2(condition),
2026
2198
  updatedAt: /* @__PURE__ */ new Date()
2027
2199
  });
2028
2200
  return this.getById(technologyId);
@@ -2036,7 +2208,7 @@ var TechnologyService = class extends BaseService {
2036
2208
  async removeBlockingCondition(technologyId, condition) {
2037
2209
  const docRef = doc9(this.technologiesRef, technologyId);
2038
2210
  await updateDoc9(docRef, {
2039
- blockingConditions: arrayRemove(condition),
2211
+ blockingConditions: arrayRemove2(condition),
2040
2212
  updatedAt: /* @__PURE__ */ new Date()
2041
2213
  });
2042
2214
  return this.getById(technologyId);
@@ -2363,12 +2535,131 @@ var TechnologyService = class extends BaseService {
2363
2535
  })
2364
2536
  );
2365
2537
  }
2538
+ // ==========================================
2539
+ // NEW METHODS: Product assignment management
2540
+ // ==========================================
2541
+ /**
2542
+ * Assigns multiple products to a technology
2543
+ * Updates each product's assignedTechnologyIds array
2544
+ */
2545
+ async assignProducts(technologyId, productIds) {
2546
+ const batch = writeBatch(this.db);
2547
+ for (const productId of productIds) {
2548
+ const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
2549
+ batch.update(productRef, {
2550
+ assignedTechnologyIds: arrayUnion2(technologyId),
2551
+ updatedAt: /* @__PURE__ */ new Date()
2552
+ });
2553
+ }
2554
+ await batch.commit();
2555
+ }
2556
+ /**
2557
+ * Unassigns multiple products from a technology
2558
+ * Updates each product's assignedTechnologyIds array
2559
+ */
2560
+ async unassignProducts(technologyId, productIds) {
2561
+ const batch = writeBatch(this.db);
2562
+ for (const productId of productIds) {
2563
+ const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
2564
+ batch.update(productRef, {
2565
+ assignedTechnologyIds: arrayRemove2(technologyId),
2566
+ updatedAt: /* @__PURE__ */ new Date()
2567
+ });
2568
+ }
2569
+ await batch.commit();
2570
+ }
2571
+ /**
2572
+ * Gets products assigned to a specific technology
2573
+ * Reads from top-level collection for immediate consistency (Cloud Functions may lag)
2574
+ */
2575
+ async getAssignedProducts(technologyId) {
2576
+ const q = query9(
2577
+ collection9(this.db, PRODUCTS_COLLECTION),
2578
+ where9("assignedTechnologyIds", "array-contains", technologyId),
2579
+ where9("isActive", "==", true),
2580
+ orderBy8("name")
2581
+ );
2582
+ const snapshot = await getDocs9(q);
2583
+ return snapshot.docs.map(
2584
+ (doc11) => ({
2585
+ id: doc11.id,
2586
+ ...doc11.data()
2587
+ })
2588
+ );
2589
+ }
2590
+ /**
2591
+ * Gets products NOT assigned to a specific technology
2592
+ */
2593
+ async getUnassignedProducts(technologyId) {
2594
+ const q = query9(
2595
+ collection9(this.db, PRODUCTS_COLLECTION),
2596
+ where9("isActive", "==", true),
2597
+ orderBy8("name")
2598
+ );
2599
+ const snapshot = await getDocs9(q);
2600
+ const allProducts = snapshot.docs.map(
2601
+ (doc11) => ({
2602
+ id: doc11.id,
2603
+ ...doc11.data()
2604
+ })
2605
+ );
2606
+ return allProducts.filter(
2607
+ (product) => {
2608
+ var _a;
2609
+ return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
2610
+ }
2611
+ );
2612
+ }
2613
+ /**
2614
+ * Gets product assignment statistics for a technology
2615
+ */
2616
+ async getProductStats(technologyId) {
2617
+ const products = await this.getAssignedProducts(technologyId);
2618
+ const byBrand = {};
2619
+ products.forEach((product) => {
2620
+ byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
2621
+ });
2622
+ return {
2623
+ totalAssigned: products.length,
2624
+ byBrand
2625
+ };
2626
+ }
2627
+ /**
2628
+ * Updates products in technology subcollection when technology metadata changes
2629
+ * @param technologyId - ID of the technology
2630
+ * @param updates - Fields to update (categoryId, subcategoryId, technologyName)
2631
+ */
2632
+ async updateProductsInSubcollection(technologyId, updates) {
2633
+ const productsRef = collection9(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
2634
+ const productsSnapshot = await getDocs9(productsRef);
2635
+ if (productsSnapshot.empty) {
2636
+ return;
2637
+ }
2638
+ const batch = writeBatch(this.db);
2639
+ for (const productDoc of productsSnapshot.docs) {
2640
+ const productRef = productDoc.ref;
2641
+ const updateFields = {};
2642
+ if (updates.categoryId !== void 0) {
2643
+ updateFields.categoryId = updates.categoryId;
2644
+ }
2645
+ if (updates.subcategoryId !== void 0) {
2646
+ updateFields.subcategoryId = updates.subcategoryId;
2647
+ }
2648
+ if (updates.technologyName !== void 0) {
2649
+ updateFields.technologyName = updates.technologyName;
2650
+ }
2651
+ if (Object.keys(updateFields).length > 0) {
2652
+ batch.update(productRef, updateFields);
2653
+ }
2654
+ }
2655
+ await batch.commit();
2656
+ }
2366
2657
  };
2367
2658
 
2368
2659
  // src/backoffice/services/constants.service.ts
2369
2660
  import {
2370
- arrayRemove as arrayRemove2,
2371
- arrayUnion as arrayUnion2,
2661
+ arrayRemove as arrayRemove3,
2662
+ arrayUnion as arrayUnion3,
2372
2663
  doc as doc10,
2373
2664
  getDoc as getDoc10,
2374
2665
  setDoc as setDoc5,
@@ -2436,7 +2727,7 @@ var ConstantsService = class extends BaseService {
2436
2727
  await setDoc5(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
2437
2728
  } else {
2438
2729
  await updateDoc10(this.treatmentBenefitsDocRef, {
2439
- benefits: arrayUnion2(newBenefit)
2730
+ benefits: arrayUnion3(newBenefit)
2440
2731
  });
2441
2732
  }
2442
2733
  return newBenefit;
@@ -2490,7 +2781,7 @@ var ConstantsService = class extends BaseService {
2490
2781
  return;
2491
2782
  }
2492
2783
  await updateDoc10(this.treatmentBenefitsDocRef, {
2493
- benefits: arrayRemove2(benefitToRemove)
2784
+ benefits: arrayRemove3(benefitToRemove)
2494
2785
  });
2495
2786
  }
2496
2787
  // =================================================================
@@ -2543,7 +2834,7 @@ var ConstantsService = class extends BaseService {
2543
2834
  });
2544
2835
  } else {
2545
2836
  await updateDoc10(this.contraindicationsDocRef, {
2546
- contraindications: arrayUnion2(newContraindication)
2837
+ contraindications: arrayUnion3(newContraindication)
2547
2838
  });
2548
2839
  }
2549
2840
  return newContraindication;
@@ -2599,7 +2890,7 @@ var ConstantsService = class extends BaseService {
2599
2890
  return;
2600
2891
  }
2601
2892
  await updateDoc10(this.contraindicationsDocRef, {
2602
- contraindications: arrayRemove2(toRemove)
2893
+ contraindications: arrayRemove3(toRemove)
2603
2894
  });
2604
2895
  }
2605
2896
  };