@blackcode_sa/metaestetics-api 1.12.40 → 1.12.41

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
@@ -1244,31 +1254,24 @@ var ProductService = class extends BaseService {
1244
1254
  return snapshot.data().count;
1245
1255
  }
1246
1256
  /**
1247
- * Gets counts of active products grouped by category, subcategory, and technology.
1248
- * This uses a single collectionGroup query for efficiency.
1257
+ * Gets counts of active products grouped by technology.
1258
+ * NOTE: Only counts top-level collection to avoid duplication during migration.
1259
+ * Categories/subcategories not available in top-level structure.
1249
1260
  */
1250
1261
  async getProductCounts() {
1251
- const q = query6(collectionGroup(this.db, PRODUCTS_COLLECTION), where6("isActive", "==", true));
1252
- const snapshot = await getDocs6(q);
1253
1262
  const counts = {
1254
1263
  byCategory: {},
1255
1264
  bySubcategory: {},
1256
1265
  byTechnology: {}
1257
1266
  };
1258
- if (snapshot.empty) {
1259
- return counts;
1260
- }
1267
+ const q = query6(this.getTopLevelProductsRef(), where6("isActive", "==", true));
1268
+ const snapshot = await getDocs6(q);
1261
1269
  snapshot.docs.forEach((doc11) => {
1262
1270
  const product = doc11.data();
1263
- const { categoryId, subcategoryId, technologyId } = product;
1264
- if (categoryId) {
1265
- counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
1266
- }
1267
- if (subcategoryId) {
1268
- counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
1269
- }
1270
- if (technologyId) {
1271
- counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
1271
+ if (product.assignedTechnologyIds && Array.isArray(product.assignedTechnologyIds)) {
1272
+ product.assignedTechnologyIds.forEach((techId) => {
1273
+ counts.byTechnology[techId] = (counts.byTechnology[techId] || 0) + 1;
1274
+ });
1272
1275
  }
1273
1276
  });
1274
1277
  return counts;
@@ -1347,6 +1350,160 @@ var ProductService = class extends BaseService {
1347
1350
  ...docSnap.data()
1348
1351
  };
1349
1352
  }
1353
+ // ==========================================
1354
+ // NEW METHODS: Top-level collection (preferred)
1355
+ // ==========================================
1356
+ /**
1357
+ * Creates a new product in the top-level collection
1358
+ */
1359
+ async createTopLevel(brandId, product, technologyIds = []) {
1360
+ const now = /* @__PURE__ */ new Date();
1361
+ const newProduct = {
1362
+ ...product,
1363
+ brandId,
1364
+ assignedTechnologyIds: technologyIds,
1365
+ createdAt: now,
1366
+ updatedAt: now,
1367
+ isActive: true
1368
+ };
1369
+ const productRef = await addDoc3(this.getTopLevelProductsRef(), newProduct);
1370
+ return { id: productRef.id, ...newProduct };
1371
+ }
1372
+ /**
1373
+ * Gets all products from the top-level collection
1374
+ */
1375
+ async getAllTopLevel(options) {
1376
+ const { rowsPerPage, lastVisible, brandId } = options;
1377
+ const constraints = [where6("isActive", "==", true), orderBy6("name")];
1378
+ if (brandId) {
1379
+ constraints.push(where6("brandId", "==", brandId));
1380
+ }
1381
+ if (lastVisible) {
1382
+ constraints.push(startAfter5(lastVisible));
1383
+ }
1384
+ constraints.push(limit6(rowsPerPage));
1385
+ const q = query6(this.getTopLevelProductsRef(), ...constraints);
1386
+ const snapshot = await getDocs6(q);
1387
+ const products = snapshot.docs.map(
1388
+ (doc11) => ({
1389
+ id: doc11.id,
1390
+ ...doc11.data()
1391
+ })
1392
+ );
1393
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
1394
+ return { products, lastVisible: newLastVisible };
1395
+ }
1396
+ /**
1397
+ * Gets a product by ID from the top-level collection
1398
+ */
1399
+ async getByIdTopLevel(productId) {
1400
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1401
+ const docSnap = await getDoc6(docRef);
1402
+ if (!docSnap.exists()) return null;
1403
+ return {
1404
+ id: docSnap.id,
1405
+ ...docSnap.data()
1406
+ };
1407
+ }
1408
+ /**
1409
+ * Updates a product in the top-level collection
1410
+ */
1411
+ async updateTopLevel(productId, product) {
1412
+ const updateData = {
1413
+ ...product,
1414
+ updatedAt: /* @__PURE__ */ new Date()
1415
+ };
1416
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1417
+ await updateDoc6(docRef, updateData);
1418
+ return this.getByIdTopLevel(productId);
1419
+ }
1420
+ /**
1421
+ * Deletes a product from the top-level collection (soft delete)
1422
+ */
1423
+ async deleteTopLevel(productId) {
1424
+ await this.updateTopLevel(productId, {
1425
+ isActive: false
1426
+ });
1427
+ }
1428
+ /**
1429
+ * Assigns a product to a technology
1430
+ */
1431
+ async assignToTechnology(productId, technologyId) {
1432
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1433
+ await updateDoc6(docRef, {
1434
+ assignedTechnologyIds: arrayUnion(technologyId),
1435
+ updatedAt: /* @__PURE__ */ new Date()
1436
+ });
1437
+ }
1438
+ /**
1439
+ * Unassigns a product from a technology
1440
+ */
1441
+ async unassignFromTechnology(productId, technologyId) {
1442
+ const docRef = doc6(this.getTopLevelProductsRef(), productId);
1443
+ await updateDoc6(docRef, {
1444
+ assignedTechnologyIds: arrayRemove(technologyId),
1445
+ updatedAt: /* @__PURE__ */ new Date()
1446
+ });
1447
+ }
1448
+ /**
1449
+ * Gets products assigned to a specific technology
1450
+ */
1451
+ async getAssignedProducts(technologyId) {
1452
+ const q = query6(
1453
+ this.getTopLevelProductsRef(),
1454
+ where6("assignedTechnologyIds", "array-contains", technologyId),
1455
+ where6("isActive", "==", true),
1456
+ orderBy6("name")
1457
+ );
1458
+ const snapshot = await getDocs6(q);
1459
+ return snapshot.docs.map(
1460
+ (doc11) => ({
1461
+ id: doc11.id,
1462
+ ...doc11.data()
1463
+ })
1464
+ );
1465
+ }
1466
+ /**
1467
+ * Gets products NOT assigned to a specific technology
1468
+ */
1469
+ async getUnassignedProducts(technologyId) {
1470
+ const q = query6(
1471
+ this.getTopLevelProductsRef(),
1472
+ where6("isActive", "==", true),
1473
+ orderBy6("name")
1474
+ );
1475
+ const snapshot = await getDocs6(q);
1476
+ const allProducts = snapshot.docs.map(
1477
+ (doc11) => ({
1478
+ id: doc11.id,
1479
+ ...doc11.data()
1480
+ })
1481
+ );
1482
+ return allProducts.filter(
1483
+ (product) => {
1484
+ var _a;
1485
+ return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
1486
+ }
1487
+ );
1488
+ }
1489
+ /**
1490
+ * Gets all products for a brand (from top-level collection)
1491
+ */
1492
+ async getByBrand(brandId) {
1493
+ const q = query6(
1494
+ this.getTopLevelProductsRef(),
1495
+ where6("brandId", "==", brandId),
1496
+ where6("isActive", "==", true),
1497
+ orderBy6("name")
1498
+ );
1499
+ const snapshot = await getDocs6(q);
1500
+ return snapshot.docs.map(
1501
+ (doc11) => ({
1502
+ id: doc11.id,
1503
+ ...doc11.data()
1504
+ })
1505
+ );
1506
+ }
1350
1507
  };
1351
1508
 
1352
1509
  // src/backoffice/services/requirement.service.ts
@@ -1737,8 +1894,9 @@ import {
1737
1894
  startAfter as startAfter7,
1738
1895
  updateDoc as updateDoc9,
1739
1896
  where as where9,
1740
- arrayUnion,
1741
- arrayRemove
1897
+ arrayUnion as arrayUnion2,
1898
+ arrayRemove as arrayRemove2,
1899
+ writeBatch
1742
1900
  } from "firebase/firestore";
1743
1901
 
1744
1902
  // src/backoffice/types/static/certification.types.ts
@@ -1968,7 +2126,7 @@ var TechnologyService = class extends BaseService {
1968
2126
  const docRef = doc9(this.technologiesRef, technologyId);
1969
2127
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1970
2128
  await updateDoc9(docRef, {
1971
- [requirementType]: arrayUnion(requirement),
2129
+ [requirementType]: arrayUnion2(requirement),
1972
2130
  updatedAt: /* @__PURE__ */ new Date()
1973
2131
  });
1974
2132
  return this.getById(technologyId);
@@ -1983,7 +2141,7 @@ var TechnologyService = class extends BaseService {
1983
2141
  const docRef = doc9(this.technologiesRef, technologyId);
1984
2142
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
1985
2143
  await updateDoc9(docRef, {
1986
- [requirementType]: arrayRemove(requirement),
2144
+ [requirementType]: arrayRemove2(requirement),
1987
2145
  updatedAt: /* @__PURE__ */ new Date()
1988
2146
  });
1989
2147
  return this.getById(technologyId);
@@ -2022,7 +2180,7 @@ var TechnologyService = class extends BaseService {
2022
2180
  async addBlockingCondition(technologyId, condition) {
2023
2181
  const docRef = doc9(this.technologiesRef, technologyId);
2024
2182
  await updateDoc9(docRef, {
2025
- blockingConditions: arrayUnion(condition),
2183
+ blockingConditions: arrayUnion2(condition),
2026
2184
  updatedAt: /* @__PURE__ */ new Date()
2027
2185
  });
2028
2186
  return this.getById(technologyId);
@@ -2036,7 +2194,7 @@ var TechnologyService = class extends BaseService {
2036
2194
  async removeBlockingCondition(technologyId, condition) {
2037
2195
  const docRef = doc9(this.technologiesRef, technologyId);
2038
2196
  await updateDoc9(docRef, {
2039
- blockingConditions: arrayRemove(condition),
2197
+ blockingConditions: arrayRemove2(condition),
2040
2198
  updatedAt: /* @__PURE__ */ new Date()
2041
2199
  });
2042
2200
  return this.getById(technologyId);
@@ -2363,12 +2521,101 @@ var TechnologyService = class extends BaseService {
2363
2521
  })
2364
2522
  );
2365
2523
  }
2524
+ // ==========================================
2525
+ // NEW METHODS: Product assignment management
2526
+ // ==========================================
2527
+ /**
2528
+ * Assigns multiple products to a technology
2529
+ * Updates each product's assignedTechnologyIds array
2530
+ */
2531
+ async assignProducts(technologyId, productIds) {
2532
+ const batch = writeBatch(this.db);
2533
+ for (const productId of productIds) {
2534
+ const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
2535
+ batch.update(productRef, {
2536
+ assignedTechnologyIds: arrayUnion2(technologyId),
2537
+ updatedAt: /* @__PURE__ */ new Date()
2538
+ });
2539
+ }
2540
+ await batch.commit();
2541
+ }
2542
+ /**
2543
+ * Unassigns multiple products from a technology
2544
+ * Updates each product's assignedTechnologyIds array
2545
+ */
2546
+ async unassignProducts(technologyId, productIds) {
2547
+ const batch = writeBatch(this.db);
2548
+ for (const productId of productIds) {
2549
+ const productRef = doc9(this.db, PRODUCTS_COLLECTION, productId);
2550
+ batch.update(productRef, {
2551
+ assignedTechnologyIds: arrayRemove2(technologyId),
2552
+ updatedAt: /* @__PURE__ */ new Date()
2553
+ });
2554
+ }
2555
+ await batch.commit();
2556
+ }
2557
+ /**
2558
+ * Gets products assigned to a specific technology
2559
+ * Reads from top-level collection for immediate consistency (Cloud Functions may lag)
2560
+ */
2561
+ async getAssignedProducts(technologyId) {
2562
+ const q = query9(
2563
+ collection9(this.db, PRODUCTS_COLLECTION),
2564
+ where9("assignedTechnologyIds", "array-contains", technologyId),
2565
+ where9("isActive", "==", true),
2566
+ orderBy8("name")
2567
+ );
2568
+ const snapshot = await getDocs9(q);
2569
+ return snapshot.docs.map(
2570
+ (doc11) => ({
2571
+ id: doc11.id,
2572
+ ...doc11.data()
2573
+ })
2574
+ );
2575
+ }
2576
+ /**
2577
+ * Gets products NOT assigned to a specific technology
2578
+ */
2579
+ async getUnassignedProducts(technologyId) {
2580
+ const q = query9(
2581
+ collection9(this.db, PRODUCTS_COLLECTION),
2582
+ where9("isActive", "==", true),
2583
+ orderBy8("name")
2584
+ );
2585
+ const snapshot = await getDocs9(q);
2586
+ const allProducts = snapshot.docs.map(
2587
+ (doc11) => ({
2588
+ id: doc11.id,
2589
+ ...doc11.data()
2590
+ })
2591
+ );
2592
+ return allProducts.filter(
2593
+ (product) => {
2594
+ var _a;
2595
+ return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
2596
+ }
2597
+ );
2598
+ }
2599
+ /**
2600
+ * Gets product assignment statistics for a technology
2601
+ */
2602
+ async getProductStats(technologyId) {
2603
+ const products = await this.getAssignedProducts(technologyId);
2604
+ const byBrand = {};
2605
+ products.forEach((product) => {
2606
+ byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
2607
+ });
2608
+ return {
2609
+ totalAssigned: products.length,
2610
+ byBrand
2611
+ };
2612
+ }
2366
2613
  };
2367
2614
 
2368
2615
  // src/backoffice/services/constants.service.ts
2369
2616
  import {
2370
- arrayRemove as arrayRemove2,
2371
- arrayUnion as arrayUnion2,
2617
+ arrayRemove as arrayRemove3,
2618
+ arrayUnion as arrayUnion3,
2372
2619
  doc as doc10,
2373
2620
  getDoc as getDoc10,
2374
2621
  setDoc as setDoc5,
@@ -2436,7 +2683,7 @@ var ConstantsService = class extends BaseService {
2436
2683
  await setDoc5(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
2437
2684
  } else {
2438
2685
  await updateDoc10(this.treatmentBenefitsDocRef, {
2439
- benefits: arrayUnion2(newBenefit)
2686
+ benefits: arrayUnion3(newBenefit)
2440
2687
  });
2441
2688
  }
2442
2689
  return newBenefit;
@@ -2490,7 +2737,7 @@ var ConstantsService = class extends BaseService {
2490
2737
  return;
2491
2738
  }
2492
2739
  await updateDoc10(this.treatmentBenefitsDocRef, {
2493
- benefits: arrayRemove2(benefitToRemove)
2740
+ benefits: arrayRemove3(benefitToRemove)
2494
2741
  });
2495
2742
  }
2496
2743
  // =================================================================
@@ -2543,7 +2790,7 @@ var ConstantsService = class extends BaseService {
2543
2790
  });
2544
2791
  } else {
2545
2792
  await updateDoc10(this.contraindicationsDocRef, {
2546
- contraindications: arrayUnion2(newContraindication)
2793
+ contraindications: arrayUnion3(newContraindication)
2547
2794
  });
2548
2795
  }
2549
2796
  return newContraindication;
@@ -2599,7 +2846,7 @@ var ConstantsService = class extends BaseService {
2599
2846
  return;
2600
2847
  }
2601
2848
  await updateDoc10(this.contraindicationsDocRef, {
2602
- contraindications: arrayRemove2(toRemove)
2849
+ contraindications: arrayRemove3(toRemove)
2603
2850
  });
2604
2851
  }
2605
2852
  };