@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.
@@ -13,6 +13,8 @@ import {
13
13
  startAfter,
14
14
  getCountFromServer,
15
15
  QueryConstraint,
16
+ arrayUnion,
17
+ arrayRemove,
16
18
  } from 'firebase/firestore';
17
19
  import { Product, PRODUCTS_COLLECTION, IProductService } from '../types/product.types';
18
20
  import { BaseService } from '../../services/base.service';
@@ -20,7 +22,15 @@ import { TECHNOLOGIES_COLLECTION } from '../types/technology.types';
20
22
 
21
23
  export class ProductService extends BaseService implements IProductService {
22
24
  /**
23
- * Gets reference to products collection under a technology
25
+ * Gets reference to top-level products collection (source of truth)
26
+ * @returns Firestore collection reference
27
+ */
28
+ private getTopLevelProductsRef() {
29
+ return collection(this.db, PRODUCTS_COLLECTION);
30
+ }
31
+
32
+ /**
33
+ * Gets reference to products collection under a technology (backward compatibility)
24
34
  * @param technologyId - ID of the technology
25
35
  * @returns Firestore collection reference
26
36
  */
@@ -37,11 +47,11 @@ export class ProductService extends BaseService implements IProductService {
37
47
  product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'technologyId'>,
38
48
  ): Promise<Product> {
39
49
  const now = new Date();
40
- // categoryId and subcategoryId are now expected to be part of the product object
50
+ // Create product with legacy structure for subcollection compatibility
41
51
  const newProduct: Omit<Product, 'id'> = {
42
52
  ...product,
43
53
  brandId,
44
- technologyId,
54
+ technologyId, // Required for old subcollection structure
45
55
  createdAt: now,
46
56
  updatedAt: now,
47
57
  isActive: true,
@@ -125,38 +135,35 @@ export class ProductService extends BaseService implements IProductService {
125
135
 
126
136
  /**
127
137
  * Gets counts of active products grouped by category, subcategory, and technology.
128
- * This uses a single collectionGroup query for efficiency.
138
+ * Queries technology subcollections which have the legacy fields synced by Cloud Functions.
129
139
  */
130
140
  async getProductCounts(): Promise<{
131
141
  byCategory: Record<string, number>;
132
142
  bySubcategory: Record<string, number>;
133
143
  byTechnology: Record<string, number>;
134
144
  }> {
135
- const q = query(collectionGroup(this.db, PRODUCTS_COLLECTION), where('isActive', '==', true));
136
- const snapshot = await getDocs(q);
137
-
138
145
  const counts = {
139
146
  byCategory: {} as Record<string, number>,
140
147
  bySubcategory: {} as Record<string, number>,
141
148
  byTechnology: {} as Record<string, number>,
142
149
  };
143
150
 
144
- if (snapshot.empty) {
145
- return counts;
146
- }
151
+ // Query technology subcollections (which have synced legacy fields)
152
+ const q = query(collectionGroup(this.db, PRODUCTS_COLLECTION), where('isActive', '==', true));
153
+ const snapshot = await getDocs(q);
147
154
 
148
155
  snapshot.docs.forEach(doc => {
149
156
  const product = doc.data() as Product;
150
- const { categoryId, subcategoryId, technologyId } = product;
151
-
152
- if (categoryId) {
153
- counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
157
+
158
+ // Use legacy fields from subcollections
159
+ if (product.categoryId) {
160
+ counts.byCategory[product.categoryId] = (counts.byCategory[product.categoryId] || 0) + 1;
154
161
  }
155
- if (subcategoryId) {
156
- counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
162
+ if (product.subcategoryId) {
163
+ counts.bySubcategory[product.subcategoryId] = (counts.bySubcategory[product.subcategoryId] || 0) + 1;
157
164
  }
158
- if (technologyId) {
159
- counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
165
+ if (product.technologyId) {
166
+ counts.byTechnology[product.technologyId] = (counts.byTechnology[product.technologyId] || 0) + 1;
160
167
  }
161
168
  });
162
169
 
@@ -252,4 +259,195 @@ export class ProductService extends BaseService implements IProductService {
252
259
  ...docSnap.data(),
253
260
  } as Product;
254
261
  }
262
+
263
+ // ==========================================
264
+ // NEW METHODS: Top-level collection (preferred)
265
+ // ==========================================
266
+
267
+ /**
268
+ * Creates a new product in the top-level collection
269
+ */
270
+ async createTopLevel(
271
+ brandId: string,
272
+ product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'assignedTechnologyIds'>,
273
+ technologyIds: string[] = [],
274
+ ): Promise<Product> {
275
+ const now = new Date();
276
+ const newProduct: Omit<Product, 'id'> = {
277
+ ...product,
278
+ brandId,
279
+ assignedTechnologyIds: technologyIds,
280
+ createdAt: now,
281
+ updatedAt: now,
282
+ isActive: true,
283
+ };
284
+
285
+ const productRef = await addDoc(this.getTopLevelProductsRef(), newProduct);
286
+ return { id: productRef.id, ...newProduct };
287
+ }
288
+
289
+ /**
290
+ * Gets all products from the top-level collection
291
+ */
292
+ async getAllTopLevel(options: {
293
+ rowsPerPage: number;
294
+ lastVisible?: any;
295
+ brandId?: string;
296
+ }): Promise<{ products: Product[]; lastVisible: any }> {
297
+ const { rowsPerPage, lastVisible, brandId } = options;
298
+
299
+ const constraints: QueryConstraint[] = [where('isActive', '==', true), orderBy('name')];
300
+
301
+ if (brandId) {
302
+ constraints.push(where('brandId', '==', brandId));
303
+ }
304
+
305
+ if (lastVisible) {
306
+ constraints.push(startAfter(lastVisible));
307
+ }
308
+ constraints.push(limit(rowsPerPage));
309
+
310
+ const q = query(this.getTopLevelProductsRef(), ...constraints);
311
+ const snapshot = await getDocs(q);
312
+
313
+ const products = snapshot.docs.map(
314
+ doc =>
315
+ ({
316
+ id: doc.id,
317
+ ...doc.data(),
318
+ } as Product),
319
+ );
320
+ const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
321
+
322
+ return { products, lastVisible: newLastVisible };
323
+ }
324
+
325
+ /**
326
+ * Gets a product by ID from the top-level collection
327
+ */
328
+ async getByIdTopLevel(productId: string): Promise<Product | null> {
329
+ const docRef = doc(this.getTopLevelProductsRef(), productId);
330
+ const docSnap = await getDoc(docRef);
331
+ if (!docSnap.exists()) return null;
332
+ return {
333
+ id: docSnap.id,
334
+ ...docSnap.data(),
335
+ } as Product;
336
+ }
337
+
338
+ /**
339
+ * Updates a product in the top-level collection
340
+ */
341
+ async updateTopLevel(
342
+ productId: string,
343
+ product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId'>>,
344
+ ): Promise<Product | null> {
345
+ const updateData = {
346
+ ...product,
347
+ updatedAt: new Date(),
348
+ };
349
+
350
+ const docRef = doc(this.getTopLevelProductsRef(), productId);
351
+ await updateDoc(docRef, updateData);
352
+
353
+ return this.getByIdTopLevel(productId);
354
+ }
355
+
356
+ /**
357
+ * Deletes a product from the top-level collection (soft delete)
358
+ */
359
+ async deleteTopLevel(productId: string): Promise<void> {
360
+ await this.updateTopLevel(productId, {
361
+ isActive: false,
362
+ });
363
+ }
364
+
365
+ /**
366
+ * Assigns a product to a technology
367
+ */
368
+ async assignToTechnology(productId: string, technologyId: string): Promise<void> {
369
+ const docRef = doc(this.getTopLevelProductsRef(), productId);
370
+ await updateDoc(docRef, {
371
+ assignedTechnologyIds: arrayUnion(technologyId),
372
+ updatedAt: new Date(),
373
+ });
374
+ // Cloud Function will handle syncing to subcollection
375
+ }
376
+
377
+ /**
378
+ * Unassigns a product from a technology
379
+ */
380
+ async unassignFromTechnology(productId: string, technologyId: string): Promise<void> {
381
+ const docRef = doc(this.getTopLevelProductsRef(), productId);
382
+ await updateDoc(docRef, {
383
+ assignedTechnologyIds: arrayRemove(technologyId),
384
+ updatedAt: new Date(),
385
+ });
386
+ // Cloud Function will handle removing from subcollection
387
+ }
388
+
389
+ /**
390
+ * Gets products assigned to a specific technology
391
+ */
392
+ async getAssignedProducts(technologyId: string): Promise<Product[]> {
393
+ const q = query(
394
+ this.getTopLevelProductsRef(),
395
+ where('assignedTechnologyIds', 'array-contains', technologyId),
396
+ where('isActive', '==', true),
397
+ orderBy('name'),
398
+ );
399
+ const snapshot = await getDocs(q);
400
+ return snapshot.docs.map(
401
+ doc =>
402
+ ({
403
+ id: doc.id,
404
+ ...doc.data(),
405
+ } as Product),
406
+ );
407
+ }
408
+
409
+ /**
410
+ * Gets products NOT assigned to a specific technology
411
+ */
412
+ async getUnassignedProducts(technologyId: string): Promise<Product[]> {
413
+ const q = query(
414
+ this.getTopLevelProductsRef(),
415
+ where('isActive', '==', true),
416
+ orderBy('name'),
417
+ );
418
+ const snapshot = await getDocs(q);
419
+
420
+ const allProducts = snapshot.docs.map(
421
+ doc =>
422
+ ({
423
+ id: doc.id,
424
+ ...doc.data(),
425
+ } as Product),
426
+ );
427
+
428
+ // Filter out products already assigned to this technology
429
+ return allProducts.filter(product =>
430
+ !product.assignedTechnologyIds?.includes(technologyId)
431
+ );
432
+ }
433
+
434
+ /**
435
+ * Gets all products for a brand (from top-level collection)
436
+ */
437
+ async getByBrand(brandId: string): Promise<Product[]> {
438
+ const q = query(
439
+ this.getTopLevelProductsRef(),
440
+ where('brandId', '==', brandId),
441
+ where('isActive', '==', true),
442
+ orderBy('name'),
443
+ );
444
+ const snapshot = await getDocs(q);
445
+ return snapshot.docs.map(
446
+ doc =>
447
+ ({
448
+ id: doc.id,
449
+ ...doc.data(),
450
+ } as Product),
451
+ );
452
+ }
255
453
  }
@@ -15,6 +15,7 @@ import {
15
15
  arrayUnion,
16
16
  arrayRemove,
17
17
  Firestore,
18
+ writeBatch,
18
19
  } from 'firebase/firestore';
19
20
  import { Technology, TECHNOLOGIES_COLLECTION, ITechnologyService } from '../types/technology.types';
20
21
  import { Requirement, RequirementType } from '../types/requirement.types';
@@ -29,6 +30,7 @@ import {
29
30
  import { BaseService } from '../../services/base.service';
30
31
  import { ProcedureFamily } from '../types/static/procedure-family.types';
31
32
  import { Practitioner, PractitionerCertification } from '../../types/practitioner';
33
+ import { Product, PRODUCTS_COLLECTION } from '../types/product.types';
32
34
 
33
35
  /**
34
36
  * Default vrednosti za sertifikaciju
@@ -240,7 +242,25 @@ export class TechnologyService extends BaseService implements ITechnologyService
240
242
  updateData.updatedAt = new Date();
241
243
 
242
244
  const docRef = doc(this.technologiesRef, id);
245
+
246
+ // Get the technology before update to check what changed
247
+ const beforeTech = await this.getById(id);
248
+
243
249
  await updateDoc(docRef, updateData);
250
+
251
+ // If categoryId, subcategoryId, or name changed, update all products in subcollection
252
+ const categoryChanged = beforeTech && updateData.categoryId && beforeTech.categoryId !== updateData.categoryId;
253
+ const subcategoryChanged = beforeTech && updateData.subcategoryId && beforeTech.subcategoryId !== updateData.subcategoryId;
254
+ const nameChanged = beforeTech && updateData.name && beforeTech.name !== updateData.name;
255
+
256
+ if (categoryChanged || subcategoryChanged || nameChanged) {
257
+ await this.updateProductsInSubcollection(id, {
258
+ categoryId: updateData.categoryId,
259
+ subcategoryId: updateData.subcategoryId,
260
+ technologyName: updateData.name,
261
+ });
262
+ }
263
+
244
264
  return this.getById(id);
245
265
  }
246
266
 
@@ -778,4 +798,153 @@ export class TechnologyService extends BaseService implements ITechnologyService
778
798
  } as Technology),
779
799
  );
780
800
  }
801
+
802
+ // ==========================================
803
+ // NEW METHODS: Product assignment management
804
+ // ==========================================
805
+
806
+ /**
807
+ * Assigns multiple products to a technology
808
+ * Updates each product's assignedTechnologyIds array
809
+ */
810
+ async assignProducts(technologyId: string, productIds: string[]): Promise<void> {
811
+ const batch = writeBatch(this.db);
812
+
813
+ for (const productId of productIds) {
814
+ const productRef = doc(this.db, PRODUCTS_COLLECTION, productId);
815
+ batch.update(productRef, {
816
+ assignedTechnologyIds: arrayUnion(technologyId),
817
+ updatedAt: new Date(),
818
+ });
819
+ }
820
+
821
+ await batch.commit();
822
+ // Cloud Function will handle syncing to subcollections
823
+ }
824
+
825
+ /**
826
+ * Unassigns multiple products from a technology
827
+ * Updates each product's assignedTechnologyIds array
828
+ */
829
+ async unassignProducts(technologyId: string, productIds: string[]): Promise<void> {
830
+ const batch = writeBatch(this.db);
831
+
832
+ for (const productId of productIds) {
833
+ const productRef = doc(this.db, PRODUCTS_COLLECTION, productId);
834
+ batch.update(productRef, {
835
+ assignedTechnologyIds: arrayRemove(technologyId),
836
+ updatedAt: new Date(),
837
+ });
838
+ }
839
+
840
+ await batch.commit();
841
+ // Cloud Function will handle removing from subcollections
842
+ }
843
+
844
+ /**
845
+ * Gets products assigned to a specific technology
846
+ * Reads from top-level collection for immediate consistency (Cloud Functions may lag)
847
+ */
848
+ async getAssignedProducts(technologyId: string): Promise<Product[]> {
849
+ const q = query(
850
+ collection(this.db, PRODUCTS_COLLECTION),
851
+ where('assignedTechnologyIds', 'array-contains', technologyId),
852
+ where('isActive', '==', true),
853
+ orderBy('name'),
854
+ );
855
+ const snapshot = await getDocs(q);
856
+
857
+ return snapshot.docs.map(
858
+ doc =>
859
+ ({
860
+ id: doc.id,
861
+ ...doc.data(),
862
+ } as Product),
863
+ );
864
+ }
865
+
866
+ /**
867
+ * Gets products NOT assigned to a specific technology
868
+ */
869
+ async getUnassignedProducts(technologyId: string): Promise<Product[]> {
870
+ const q = query(
871
+ collection(this.db, PRODUCTS_COLLECTION),
872
+ where('isActive', '==', true),
873
+ orderBy('name'),
874
+ );
875
+ const snapshot = await getDocs(q);
876
+
877
+ const allProducts = snapshot.docs.map(
878
+ doc =>
879
+ ({
880
+ id: doc.id,
881
+ ...doc.data(),
882
+ } as Product),
883
+ );
884
+
885
+ // Filter out products already assigned to this technology
886
+ return allProducts.filter(product =>
887
+ !product.assignedTechnologyIds?.includes(technologyId)
888
+ );
889
+ }
890
+
891
+ /**
892
+ * Gets product assignment statistics for a technology
893
+ */
894
+ async getProductStats(technologyId: string): Promise<{
895
+ totalAssigned: number;
896
+ byBrand: Record<string, number>;
897
+ }> {
898
+ const products = await this.getAssignedProducts(technologyId);
899
+
900
+ const byBrand: Record<string, number> = {};
901
+ products.forEach(product => {
902
+ byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
903
+ });
904
+
905
+ return {
906
+ totalAssigned: products.length,
907
+ byBrand,
908
+ };
909
+ }
910
+
911
+ /**
912
+ * Updates products in technology subcollection when technology metadata changes
913
+ * @param technologyId - ID of the technology
914
+ * @param updates - Fields to update (categoryId, subcategoryId, technologyName)
915
+ */
916
+ private async updateProductsInSubcollection(
917
+ technologyId: string,
918
+ updates: { categoryId?: string; subcategoryId?: string; technologyName?: string }
919
+ ): Promise<void> {
920
+ const productsRef = collection(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
921
+ const productsSnapshot = await getDocs(productsRef);
922
+
923
+ if (productsSnapshot.empty) {
924
+ return;
925
+ }
926
+
927
+ const batch = writeBatch(this.db);
928
+
929
+ for (const productDoc of productsSnapshot.docs) {
930
+ const productRef = productDoc.ref;
931
+ const updateFields: any = {};
932
+
933
+ if (updates.categoryId !== undefined) {
934
+ updateFields.categoryId = updates.categoryId;
935
+ }
936
+ if (updates.subcategoryId !== undefined) {
937
+ updateFields.subcategoryId = updates.subcategoryId;
938
+ }
939
+ if (updates.technologyName !== undefined) {
940
+ updateFields.technologyName = updates.technologyName;
941
+ }
942
+
943
+ if (Object.keys(updateFields).length > 0) {
944
+ batch.update(productRef, updateFields);
945
+ }
946
+ }
947
+
948
+ await batch.commit();
949
+ }
781
950
  }
@@ -6,9 +6,10 @@ import type { ContraindicationDynamic } from './admin-constants.types';
6
6
  *
7
7
  * @property id - Unique identifier of the product
8
8
  * @property name - Name of the product
9
- * @property description - Detailed description of the product and its purpose
10
9
  * @property brandId - ID of the brand that manufactures this product
11
- * @property technologyId - ID of the technology this product is used with
10
+ * @property brandName - Name of the brand (denormalized for display)
11
+ * @property assignedTechnologyIds - Array of technology IDs this product is assigned to
12
+ * @property description - Detailed description of the product and its purpose
12
13
  * @property technicalDetails - Technical details and specifications
13
14
  * @property warnings - List of warnings related to product use
14
15
  * @property dosage - Dosage information (if applicable)
@@ -24,10 +25,11 @@ export interface Product {
24
25
  name: string;
25
26
  brandId: string;
26
27
  brandName: string;
27
- technologyId: string;
28
- technologyName: string;
29
- categoryId: string;
30
- subcategoryId: string;
28
+
29
+ // NEW: Technology assignment tracking
30
+ assignedTechnologyIds?: string[];
31
+
32
+ // Product details
31
33
  createdAt: Date;
32
34
  updatedAt: Date;
33
35
  isActive: boolean;
@@ -38,6 +40,18 @@ export interface Product {
38
40
  composition?: string;
39
41
  indications?: string[];
40
42
  contraindications?: ContraindicationDynamic[];
43
+
44
+ // LEGACY FIELDS: Only present in technology subcollections (/technologies/{id}/products/)
45
+ // These fields are synced by Cloud Functions for backward compatibility
46
+ // NOT stored in top-level /products collection
47
+ /** Present only in subcollections - synced from technology metadata */
48
+ technologyId?: string;
49
+ /** Present only in subcollections - synced from technology name */
50
+ technologyName?: string;
51
+ /** Present only in subcollections - synced from technology categoryId */
52
+ categoryId?: string;
53
+ /** Present only in subcollections - synced from technology subcategoryId */
54
+ subcategoryId?: string;
41
55
  }
42
56
 
43
57
  /**
@@ -47,9 +61,97 @@ export const PRODUCTS_COLLECTION = 'products';
47
61
 
48
62
  /**
49
63
  * Interface for the ProductService class
64
+ *
65
+ * NOTE: This interface maintains backward compatibility while adding new top-level collection methods.
66
+ * Old methods using technologyId are kept for existing code, new methods work with top-level collection.
50
67
  */
51
68
  export interface IProductService {
69
+ // ==========================================
70
+ // NEW METHODS: Top-level collection (preferred)
71
+ // ==========================================
72
+
73
+ /**
74
+ * Creates a new product in the top-level collection
75
+ * @param brandId - ID of the brand that manufactures this product
76
+ * @param product - Product data
77
+ * @param technologyIds - Optional array of technology IDs to assign this product to
78
+ */
79
+ createTopLevel(
80
+ brandId: string,
81
+ product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'assignedTechnologyIds'>,
82
+ technologyIds?: string[],
83
+ ): Promise<Product>;
84
+
85
+ /**
86
+ * Gets all products from the top-level collection
87
+ * @param options - Query options
88
+ */
89
+ getAllTopLevel(options: {
90
+ rowsPerPage: number;
91
+ lastVisible?: any;
92
+ brandId?: string;
93
+ }): Promise<{ products: Product[]; lastVisible: any }>;
94
+
95
+ /**
96
+ * Gets a product by ID from the top-level collection
97
+ * @param productId - ID of the product
98
+ */
99
+ getByIdTopLevel(productId: string): Promise<Product | null>;
100
+
101
+ /**
102
+ * Updates a product in the top-level collection
103
+ * @param productId - ID of the product to update
104
+ * @param product - Updated product data
105
+ */
106
+ updateTopLevel(
107
+ productId: string,
108
+ product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId'>>,
109
+ ): Promise<Product | null>;
110
+
111
+ /**
112
+ * Deletes a product from the top-level collection (soft delete)
113
+ * @param productId - ID of the product to delete
114
+ */
115
+ deleteTopLevel(productId: string): Promise<void>;
116
+
117
+ /**
118
+ * Assigns a product to a technology
119
+ * @param productId - ID of the product
120
+ * @param technologyId - ID of the technology
121
+ */
122
+ assignToTechnology(productId: string, technologyId: string): Promise<void>;
123
+
124
+ /**
125
+ * Unassigns a product from a technology
126
+ * @param productId - ID of the product
127
+ * @param technologyId - ID of the technology
128
+ */
129
+ unassignFromTechnology(productId: string, technologyId: string): Promise<void>;
130
+
131
+ /**
132
+ * Gets products assigned to a specific technology
133
+ * @param technologyId - ID of the technology
134
+ */
135
+ getAssignedProducts(technologyId: string): Promise<Product[]>;
136
+
137
+ /**
138
+ * Gets products NOT assigned to a specific technology
139
+ * @param technologyId - ID of the technology
140
+ */
141
+ getUnassignedProducts(technologyId: string): Promise<Product[]>;
142
+
143
+ /**
144
+ * Gets all products for a brand
145
+ * @param brandId - ID of the brand
146
+ */
147
+ getByBrand(brandId: string): Promise<Product[]>;
148
+
149
+ // ==========================================
150
+ // DEPRECATED METHODS: Kept for backward compatibility
151
+ // ==========================================
152
+
52
153
  /**
154
+ * @deprecated Use createTopLevel instead
53
155
  * Creates a new product
54
156
  * @param technologyId - ID of the technology this product is used with
55
157
  * @param brandId - ID of the brand that manufactures this product
@@ -62,6 +164,7 @@ export interface IProductService {
62
164
  ): Promise<Product>;
63
165
 
64
166
  /**
167
+ * @deprecated Use getAllTopLevel instead
65
168
  * Gets a paginated list of all products, with optional filters.
66
169
  */
67
170
  getAll(options: {
@@ -73,6 +176,7 @@ export interface IProductService {
73
176
  }): Promise<{ products: Product[]; lastVisible: any }>;
74
177
 
75
178
  /**
179
+ * @deprecated Use alternative counting methods
76
180
  * Gets the total count of active products, with optional filters.
77
181
  */
78
182
  getProductsCount(options: {
@@ -82,6 +186,7 @@ export interface IProductService {
82
186
  }): Promise<number>;
83
187
 
84
188
  /**
189
+ * @deprecated Use alternative counting methods
85
190
  * Gets counts of active products grouped by category, subcategory, and technology.
86
191
  */
87
192
  getProductCounts(): Promise<{
@@ -91,18 +196,21 @@ export interface IProductService {
91
196
  }>;
92
197
 
93
198
  /**
199
+ * @deprecated Use getAssignedProducts instead
94
200
  * Gets all products for a specific technology (non-paginated, for filters/dropdowns)
95
201
  * @param technologyId - ID of the technology
96
202
  */
97
203
  getAllByTechnology(technologyId: string): Promise<Product[]>;
98
204
 
99
205
  /**
206
+ * @deprecated Use getByBrand instead
100
207
  * Gets all products for a brand
101
208
  * @param brandId - ID of the brand
102
209
  */
103
210
  getAllByBrand(brandId: string): Promise<Product[]>;
104
211
 
105
212
  /**
213
+ * @deprecated Use updateTopLevel instead
106
214
  * Updates a product
107
215
  * @param technologyId - ID of the technology
108
216
  * @param productId - ID of the product to update
@@ -115,6 +223,7 @@ export interface IProductService {
115
223
  ): Promise<Product | null>;
116
224
 
117
225
  /**
226
+ * @deprecated Use deleteTopLevel instead
118
227
  * Deletes a product (soft delete)
119
228
  * @param technologyId - ID of the technology
120
229
  * @param productId - ID of the product to delete
@@ -122,6 +231,7 @@ export interface IProductService {
122
231
  delete(technologyId: string, productId: string): Promise<void>;
123
232
 
124
233
  /**
234
+ * @deprecated Use getByIdTopLevel instead
125
235
  * Gets a product by ID
126
236
  * @param technologyId - ID of the technology
127
237
  * @param productId - ID of the product