@blackcode_sa/metaestetics-api 1.12.39 → 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.
- package/dist/admin/index.d.mts +12 -6
- package/dist/admin/index.d.ts +12 -6
- package/dist/backoffice/index.d.mts +178 -19
- package/dist/backoffice/index.d.ts +178 -19
- package/dist/backoffice/index.js +261 -17
- package/dist/backoffice/index.mjs +277 -30
- package/dist/index.d.mts +170 -11
- package/dist/index.d.ts +170 -11
- package/dist/index.js +346 -25
- package/dist/index.mjs +357 -33
- package/package.json +1 -1
- package/src/backoffice/services/migrate-products.ts +116 -0
- package/src/backoffice/services/product.service.ts +216 -21
- package/src/backoffice/services/technology.service.ts +111 -0
- package/src/backoffice/types/product.types.ts +115 -6
- package/src/services/appointment/appointment.service.ts +120 -16
|
@@ -581,9 +581,10 @@ interface UpdateDocumentTemplateData {
|
|
|
581
581
|
*
|
|
582
582
|
* @property id - Unique identifier of the product
|
|
583
583
|
* @property name - Name of the product
|
|
584
|
-
* @property description - Detailed description of the product and its purpose
|
|
585
584
|
* @property brandId - ID of the brand that manufactures this product
|
|
586
|
-
* @property
|
|
585
|
+
* @property brandName - Name of the brand (denormalized for display)
|
|
586
|
+
* @property assignedTechnologyIds - Array of technology IDs this product is assigned to
|
|
587
|
+
* @property description - Detailed description of the product and its purpose
|
|
587
588
|
* @property technicalDetails - Technical details and specifications
|
|
588
589
|
* @property warnings - List of warnings related to product use
|
|
589
590
|
* @property dosage - Dosage information (if applicable)
|
|
@@ -599,10 +600,7 @@ interface Product {
|
|
|
599
600
|
name: string;
|
|
600
601
|
brandId: string;
|
|
601
602
|
brandName: string;
|
|
602
|
-
|
|
603
|
-
technologyName: string;
|
|
604
|
-
categoryId: string;
|
|
605
|
-
subcategoryId: string;
|
|
603
|
+
assignedTechnologyIds?: string[];
|
|
606
604
|
createdAt: Date;
|
|
607
605
|
updatedAt: Date;
|
|
608
606
|
isActive: boolean;
|
|
@@ -613,6 +611,14 @@ interface Product {
|
|
|
613
611
|
composition?: string;
|
|
614
612
|
indications?: string[];
|
|
615
613
|
contraindications?: ContraindicationDynamic[];
|
|
614
|
+
/** @deprecated Use assignedTechnologyIds instead */
|
|
615
|
+
technologyId?: string;
|
|
616
|
+
/** @deprecated Will be removed in future version */
|
|
617
|
+
technologyName?: string;
|
|
618
|
+
/** @deprecated Not needed in top-level collection */
|
|
619
|
+
categoryId?: string;
|
|
620
|
+
/** @deprecated Not needed in top-level collection */
|
|
621
|
+
subcategoryId?: string;
|
|
616
622
|
}
|
|
617
623
|
/**
|
|
618
624
|
* Collection in Firestore database where products are stored
|
|
@@ -620,9 +626,75 @@ interface Product {
|
|
|
620
626
|
declare const PRODUCTS_COLLECTION = "products";
|
|
621
627
|
/**
|
|
622
628
|
* Interface for the ProductService class
|
|
629
|
+
*
|
|
630
|
+
* NOTE: This interface maintains backward compatibility while adding new top-level collection methods.
|
|
631
|
+
* Old methods using technologyId are kept for existing code, new methods work with top-level collection.
|
|
623
632
|
*/
|
|
624
633
|
interface IProductService {
|
|
625
634
|
/**
|
|
635
|
+
* Creates a new product in the top-level collection
|
|
636
|
+
* @param brandId - ID of the brand that manufactures this product
|
|
637
|
+
* @param product - Product data
|
|
638
|
+
* @param technologyIds - Optional array of technology IDs to assign this product to
|
|
639
|
+
*/
|
|
640
|
+
createTopLevel(brandId: string, product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'assignedTechnologyIds'>, technologyIds?: string[]): Promise<Product>;
|
|
641
|
+
/**
|
|
642
|
+
* Gets all products from the top-level collection
|
|
643
|
+
* @param options - Query options
|
|
644
|
+
*/
|
|
645
|
+
getAllTopLevel(options: {
|
|
646
|
+
rowsPerPage: number;
|
|
647
|
+
lastVisible?: any;
|
|
648
|
+
brandId?: string;
|
|
649
|
+
}): Promise<{
|
|
650
|
+
products: Product[];
|
|
651
|
+
lastVisible: any;
|
|
652
|
+
}>;
|
|
653
|
+
/**
|
|
654
|
+
* Gets a product by ID from the top-level collection
|
|
655
|
+
* @param productId - ID of the product
|
|
656
|
+
*/
|
|
657
|
+
getByIdTopLevel(productId: string): Promise<Product | null>;
|
|
658
|
+
/**
|
|
659
|
+
* Updates a product in the top-level collection
|
|
660
|
+
* @param productId - ID of the product to update
|
|
661
|
+
* @param product - Updated product data
|
|
662
|
+
*/
|
|
663
|
+
updateTopLevel(productId: string, product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId'>>): Promise<Product | null>;
|
|
664
|
+
/**
|
|
665
|
+
* Deletes a product from the top-level collection (soft delete)
|
|
666
|
+
* @param productId - ID of the product to delete
|
|
667
|
+
*/
|
|
668
|
+
deleteTopLevel(productId: string): Promise<void>;
|
|
669
|
+
/**
|
|
670
|
+
* Assigns a product to a technology
|
|
671
|
+
* @param productId - ID of the product
|
|
672
|
+
* @param technologyId - ID of the technology
|
|
673
|
+
*/
|
|
674
|
+
assignToTechnology(productId: string, technologyId: string): Promise<void>;
|
|
675
|
+
/**
|
|
676
|
+
* Unassigns a product from a technology
|
|
677
|
+
* @param productId - ID of the product
|
|
678
|
+
* @param technologyId - ID of the technology
|
|
679
|
+
*/
|
|
680
|
+
unassignFromTechnology(productId: string, technologyId: string): Promise<void>;
|
|
681
|
+
/**
|
|
682
|
+
* Gets products assigned to a specific technology
|
|
683
|
+
* @param technologyId - ID of the technology
|
|
684
|
+
*/
|
|
685
|
+
getAssignedProducts(technologyId: string): Promise<Product[]>;
|
|
686
|
+
/**
|
|
687
|
+
* Gets products NOT assigned to a specific technology
|
|
688
|
+
* @param technologyId - ID of the technology
|
|
689
|
+
*/
|
|
690
|
+
getUnassignedProducts(technologyId: string): Promise<Product[]>;
|
|
691
|
+
/**
|
|
692
|
+
* Gets all products for a brand
|
|
693
|
+
* @param brandId - ID of the brand
|
|
694
|
+
*/
|
|
695
|
+
getByBrand(brandId: string): Promise<Product[]>;
|
|
696
|
+
/**
|
|
697
|
+
* @deprecated Use createTopLevel instead
|
|
626
698
|
* Creates a new product
|
|
627
699
|
* @param technologyId - ID of the technology this product is used with
|
|
628
700
|
* @param brandId - ID of the brand that manufactures this product
|
|
@@ -630,6 +702,7 @@ interface IProductService {
|
|
|
630
702
|
*/
|
|
631
703
|
create(technologyId: string, brandId: string, product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'technologyId'>): Promise<Product>;
|
|
632
704
|
/**
|
|
705
|
+
* @deprecated Use getAllTopLevel instead
|
|
633
706
|
* Gets a paginated list of all products, with optional filters.
|
|
634
707
|
*/
|
|
635
708
|
getAll(options: {
|
|
@@ -643,6 +716,7 @@ interface IProductService {
|
|
|
643
716
|
lastVisible: any;
|
|
644
717
|
}>;
|
|
645
718
|
/**
|
|
719
|
+
* @deprecated Use alternative counting methods
|
|
646
720
|
* Gets the total count of active products, with optional filters.
|
|
647
721
|
*/
|
|
648
722
|
getProductsCount(options: {
|
|
@@ -651,6 +725,7 @@ interface IProductService {
|
|
|
651
725
|
technologyId?: string;
|
|
652
726
|
}): Promise<number>;
|
|
653
727
|
/**
|
|
728
|
+
* @deprecated Use alternative counting methods
|
|
654
729
|
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
655
730
|
*/
|
|
656
731
|
getProductCounts(): Promise<{
|
|
@@ -659,16 +734,19 @@ interface IProductService {
|
|
|
659
734
|
byTechnology: Record<string, number>;
|
|
660
735
|
}>;
|
|
661
736
|
/**
|
|
737
|
+
* @deprecated Use getAssignedProducts instead
|
|
662
738
|
* Gets all products for a specific technology (non-paginated, for filters/dropdowns)
|
|
663
739
|
* @param technologyId - ID of the technology
|
|
664
740
|
*/
|
|
665
741
|
getAllByTechnology(technologyId: string): Promise<Product[]>;
|
|
666
742
|
/**
|
|
743
|
+
* @deprecated Use getByBrand instead
|
|
667
744
|
* Gets all products for a brand
|
|
668
745
|
* @param brandId - ID of the brand
|
|
669
746
|
*/
|
|
670
747
|
getAllByBrand(brandId: string): Promise<Product[]>;
|
|
671
748
|
/**
|
|
749
|
+
* @deprecated Use updateTopLevel instead
|
|
672
750
|
* Updates a product
|
|
673
751
|
* @param technologyId - ID of the technology
|
|
674
752
|
* @param productId - ID of the product to update
|
|
@@ -676,12 +754,14 @@ interface IProductService {
|
|
|
676
754
|
*/
|
|
677
755
|
update(technologyId: string, productId: string, product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId' | 'technologyId'>>): Promise<Product | null>;
|
|
678
756
|
/**
|
|
757
|
+
* @deprecated Use deleteTopLevel instead
|
|
679
758
|
* Deletes a product (soft delete)
|
|
680
759
|
* @param technologyId - ID of the technology
|
|
681
760
|
* @param productId - ID of the product to delete
|
|
682
761
|
*/
|
|
683
762
|
delete(technologyId: string, productId: string): Promise<void>;
|
|
684
763
|
/**
|
|
764
|
+
* @deprecated Use getByIdTopLevel instead
|
|
685
765
|
* Gets a product by ID
|
|
686
766
|
* @param technologyId - ID of the technology
|
|
687
767
|
* @param productId - ID of the product
|
|
@@ -1468,7 +1548,12 @@ declare class DocumentationTemplateServiceBackoffice {
|
|
|
1468
1548
|
|
|
1469
1549
|
declare class ProductService extends BaseService implements IProductService {
|
|
1470
1550
|
/**
|
|
1471
|
-
* Gets reference to products collection
|
|
1551
|
+
* Gets reference to top-level products collection (source of truth)
|
|
1552
|
+
* @returns Firestore collection reference
|
|
1553
|
+
*/
|
|
1554
|
+
private getTopLevelProductsRef;
|
|
1555
|
+
/**
|
|
1556
|
+
* Gets reference to products collection under a technology (backward compatibility)
|
|
1472
1557
|
* @param technologyId - ID of the technology
|
|
1473
1558
|
* @returns Firestore collection reference
|
|
1474
1559
|
*/
|
|
@@ -1500,8 +1585,9 @@ declare class ProductService extends BaseService implements IProductService {
|
|
|
1500
1585
|
technologyId?: string;
|
|
1501
1586
|
}): Promise<number>;
|
|
1502
1587
|
/**
|
|
1503
|
-
* Gets counts of active products grouped by
|
|
1504
|
-
*
|
|
1588
|
+
* Gets counts of active products grouped by technology.
|
|
1589
|
+
* NOTE: Only counts top-level collection to avoid duplication during migration.
|
|
1590
|
+
* Categories/subcategories not available in top-level structure.
|
|
1505
1591
|
*/
|
|
1506
1592
|
getProductCounts(): Promise<{
|
|
1507
1593
|
byCategory: Record<string, number>;
|
|
@@ -1528,6 +1614,53 @@ declare class ProductService extends BaseService implements IProductService {
|
|
|
1528
1614
|
* Gets a product by ID
|
|
1529
1615
|
*/
|
|
1530
1616
|
getById(technologyId: string, productId: string): Promise<Product | null>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Creates a new product in the top-level collection
|
|
1619
|
+
*/
|
|
1620
|
+
createTopLevel(brandId: string, product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'assignedTechnologyIds'>, technologyIds?: string[]): Promise<Product>;
|
|
1621
|
+
/**
|
|
1622
|
+
* Gets all products from the top-level collection
|
|
1623
|
+
*/
|
|
1624
|
+
getAllTopLevel(options: {
|
|
1625
|
+
rowsPerPage: number;
|
|
1626
|
+
lastVisible?: any;
|
|
1627
|
+
brandId?: string;
|
|
1628
|
+
}): Promise<{
|
|
1629
|
+
products: Product[];
|
|
1630
|
+
lastVisible: any;
|
|
1631
|
+
}>;
|
|
1632
|
+
/**
|
|
1633
|
+
* Gets a product by ID from the top-level collection
|
|
1634
|
+
*/
|
|
1635
|
+
getByIdTopLevel(productId: string): Promise<Product | null>;
|
|
1636
|
+
/**
|
|
1637
|
+
* Updates a product in the top-level collection
|
|
1638
|
+
*/
|
|
1639
|
+
updateTopLevel(productId: string, product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId'>>): Promise<Product | null>;
|
|
1640
|
+
/**
|
|
1641
|
+
* Deletes a product from the top-level collection (soft delete)
|
|
1642
|
+
*/
|
|
1643
|
+
deleteTopLevel(productId: string): Promise<void>;
|
|
1644
|
+
/**
|
|
1645
|
+
* Assigns a product to a technology
|
|
1646
|
+
*/
|
|
1647
|
+
assignToTechnology(productId: string, technologyId: string): Promise<void>;
|
|
1648
|
+
/**
|
|
1649
|
+
* Unassigns a product from a technology
|
|
1650
|
+
*/
|
|
1651
|
+
unassignFromTechnology(productId: string, technologyId: string): Promise<void>;
|
|
1652
|
+
/**
|
|
1653
|
+
* Gets products assigned to a specific technology
|
|
1654
|
+
*/
|
|
1655
|
+
getAssignedProducts(technologyId: string): Promise<Product[]>;
|
|
1656
|
+
/**
|
|
1657
|
+
* Gets products NOT assigned to a specific technology
|
|
1658
|
+
*/
|
|
1659
|
+
getUnassignedProducts(technologyId: string): Promise<Product[]>;
|
|
1660
|
+
/**
|
|
1661
|
+
* Gets all products for a brand (from top-level collection)
|
|
1662
|
+
*/
|
|
1663
|
+
getByBrand(brandId: string): Promise<Product[]>;
|
|
1531
1664
|
}
|
|
1532
1665
|
|
|
1533
1666
|
/**
|
|
@@ -1740,10 +1873,10 @@ declare class TechnologyService extends BaseService implements ITechnologyServic
|
|
|
1740
1873
|
isActive: boolean;
|
|
1741
1874
|
description: string;
|
|
1742
1875
|
family: ProcedureFamily;
|
|
1743
|
-
categoryId: string;
|
|
1744
|
-
subcategoryId: string;
|
|
1745
1876
|
technicalDetails?: string | undefined;
|
|
1746
1877
|
contraindications: ContraindicationDynamic[];
|
|
1878
|
+
categoryId: string;
|
|
1879
|
+
subcategoryId: string;
|
|
1747
1880
|
requirements: {
|
|
1748
1881
|
pre: Requirement[];
|
|
1749
1882
|
post: Requirement[];
|
|
@@ -2012,6 +2145,32 @@ declare class TechnologyService extends BaseService implements ITechnologyServic
|
|
|
2012
2145
|
* Gets all active technologies for filter dropdowns.
|
|
2013
2146
|
*/
|
|
2014
2147
|
getAllForFilter(): Promise<Technology[]>;
|
|
2148
|
+
/**
|
|
2149
|
+
* Assigns multiple products to a technology
|
|
2150
|
+
* Updates each product's assignedTechnologyIds array
|
|
2151
|
+
*/
|
|
2152
|
+
assignProducts(technologyId: string, productIds: string[]): Promise<void>;
|
|
2153
|
+
/**
|
|
2154
|
+
* Unassigns multiple products from a technology
|
|
2155
|
+
* Updates each product's assignedTechnologyIds array
|
|
2156
|
+
*/
|
|
2157
|
+
unassignProducts(technologyId: string, productIds: string[]): Promise<void>;
|
|
2158
|
+
/**
|
|
2159
|
+
* Gets products assigned to a specific technology
|
|
2160
|
+
* Reads from top-level collection for immediate consistency (Cloud Functions may lag)
|
|
2161
|
+
*/
|
|
2162
|
+
getAssignedProducts(technologyId: string): Promise<Product[]>;
|
|
2163
|
+
/**
|
|
2164
|
+
* Gets products NOT assigned to a specific technology
|
|
2165
|
+
*/
|
|
2166
|
+
getUnassignedProducts(technologyId: string): Promise<Product[]>;
|
|
2167
|
+
/**
|
|
2168
|
+
* Gets product assignment statistics for a technology
|
|
2169
|
+
*/
|
|
2170
|
+
getProductStats(technologyId: string): Promise<{
|
|
2171
|
+
totalAssigned: number;
|
|
2172
|
+
byBrand: Record<string, number>;
|
|
2173
|
+
}>;
|
|
2015
2174
|
}
|
|
2016
2175
|
|
|
2017
2176
|
/**
|
|
@@ -4825,13 +4984,13 @@ declare const technologySchema: z.ZodObject<{
|
|
|
4825
4984
|
name: string;
|
|
4826
4985
|
isActive: boolean;
|
|
4827
4986
|
family: ProcedureFamily;
|
|
4828
|
-
categoryId: string;
|
|
4829
|
-
subcategoryId: string;
|
|
4830
4987
|
contraindications: {
|
|
4831
4988
|
id: string;
|
|
4832
4989
|
name: string;
|
|
4833
4990
|
description?: string | undefined;
|
|
4834
4991
|
}[];
|
|
4992
|
+
categoryId: string;
|
|
4993
|
+
subcategoryId: string;
|
|
4835
4994
|
requirements: {
|
|
4836
4995
|
pre: {
|
|
4837
4996
|
name: string;
|
|
@@ -4970,13 +5129,13 @@ declare const technologySchema: z.ZodObject<{
|
|
|
4970
5129
|
}, {
|
|
4971
5130
|
name: string;
|
|
4972
5131
|
family: ProcedureFamily;
|
|
4973
|
-
categoryId: string;
|
|
4974
|
-
subcategoryId: string;
|
|
4975
5132
|
contraindications: {
|
|
4976
5133
|
id: string;
|
|
4977
5134
|
name: string;
|
|
4978
5135
|
description?: string | undefined;
|
|
4979
5136
|
}[];
|
|
5137
|
+
categoryId: string;
|
|
5138
|
+
subcategoryId: string;
|
|
4980
5139
|
blockingConditions: BlockingCondition[];
|
|
4981
5140
|
benefits: {
|
|
4982
5141
|
id: string;
|
|
@@ -5839,14 +5998,14 @@ declare const technologyUpdateSchema: z.ZodObject<{
|
|
|
5839
5998
|
isActive?: boolean | undefined;
|
|
5840
5999
|
description?: string | undefined;
|
|
5841
6000
|
family?: ProcedureFamily | undefined;
|
|
5842
|
-
categoryId?: string | undefined;
|
|
5843
|
-
subcategoryId?: string | undefined;
|
|
5844
6001
|
technicalDetails?: string | undefined;
|
|
5845
6002
|
contraindications?: {
|
|
5846
6003
|
id: string;
|
|
5847
6004
|
name: string;
|
|
5848
6005
|
description?: string | undefined;
|
|
5849
6006
|
}[] | undefined;
|
|
6007
|
+
categoryId?: string | undefined;
|
|
6008
|
+
subcategoryId?: string | undefined;
|
|
5850
6009
|
requirements?: {
|
|
5851
6010
|
pre: {
|
|
5852
6011
|
name: string;
|
|
@@ -5985,14 +6144,14 @@ declare const technologyUpdateSchema: z.ZodObject<{
|
|
|
5985
6144
|
isActive?: boolean | undefined;
|
|
5986
6145
|
description?: string | undefined;
|
|
5987
6146
|
family?: ProcedureFamily | undefined;
|
|
5988
|
-
categoryId?: string | undefined;
|
|
5989
|
-
subcategoryId?: string | undefined;
|
|
5990
6147
|
technicalDetails?: string | undefined;
|
|
5991
6148
|
contraindications?: {
|
|
5992
6149
|
id: string;
|
|
5993
6150
|
name: string;
|
|
5994
6151
|
description?: string | undefined;
|
|
5995
6152
|
}[] | undefined;
|
|
6153
|
+
categoryId?: string | undefined;
|
|
6154
|
+
subcategoryId?: string | undefined;
|
|
5996
6155
|
requirements?: {
|
|
5997
6156
|
pre: {
|
|
5998
6157
|
name: string;
|
package/dist/backoffice/index.js
CHANGED
|
@@ -1193,7 +1193,14 @@ var TECHNOLOGIES_COLLECTION = "technologies";
|
|
|
1193
1193
|
// src/backoffice/services/product.service.ts
|
|
1194
1194
|
var ProductService = class extends BaseService {
|
|
1195
1195
|
/**
|
|
1196
|
-
* Gets reference to products collection
|
|
1196
|
+
* Gets reference to top-level products collection (source of truth)
|
|
1197
|
+
* @returns Firestore collection reference
|
|
1198
|
+
*/
|
|
1199
|
+
getTopLevelProductsRef() {
|
|
1200
|
+
return (0, import_firestore8.collection)(this.db, PRODUCTS_COLLECTION);
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Gets reference to products collection under a technology (backward compatibility)
|
|
1197
1204
|
* @param technologyId - ID of the technology
|
|
1198
1205
|
* @returns Firestore collection reference
|
|
1199
1206
|
*/
|
|
@@ -1209,6 +1216,7 @@ var ProductService = class extends BaseService {
|
|
|
1209
1216
|
...product,
|
|
1210
1217
|
brandId,
|
|
1211
1218
|
technologyId,
|
|
1219
|
+
// Required for old subcollection structure
|
|
1212
1220
|
createdAt: now,
|
|
1213
1221
|
updatedAt: now,
|
|
1214
1222
|
isActive: true
|
|
@@ -1267,31 +1275,24 @@ var ProductService = class extends BaseService {
|
|
|
1267
1275
|
return snapshot.data().count;
|
|
1268
1276
|
}
|
|
1269
1277
|
/**
|
|
1270
|
-
* Gets counts of active products grouped by
|
|
1271
|
-
*
|
|
1278
|
+
* Gets counts of active products grouped by technology.
|
|
1279
|
+
* NOTE: Only counts top-level collection to avoid duplication during migration.
|
|
1280
|
+
* Categories/subcategories not available in top-level structure.
|
|
1272
1281
|
*/
|
|
1273
1282
|
async getProductCounts() {
|
|
1274
|
-
const q = (0, import_firestore8.query)((0, import_firestore8.collectionGroup)(this.db, PRODUCTS_COLLECTION), (0, import_firestore8.where)("isActive", "==", true));
|
|
1275
|
-
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1276
1283
|
const counts = {
|
|
1277
1284
|
byCategory: {},
|
|
1278
1285
|
bySubcategory: {},
|
|
1279
1286
|
byTechnology: {}
|
|
1280
1287
|
};
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
}
|
|
1288
|
+
const q = (0, import_firestore8.query)(this.getTopLevelProductsRef(), (0, import_firestore8.where)("isActive", "==", true));
|
|
1289
|
+
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1284
1290
|
snapshot.docs.forEach((doc11) => {
|
|
1285
1291
|
const product = doc11.data();
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
if (subcategoryId) {
|
|
1291
|
-
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
1292
|
-
}
|
|
1293
|
-
if (technologyId) {
|
|
1294
|
-
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
1292
|
+
if (product.assignedTechnologyIds && Array.isArray(product.assignedTechnologyIds)) {
|
|
1293
|
+
product.assignedTechnologyIds.forEach((techId) => {
|
|
1294
|
+
counts.byTechnology[techId] = (counts.byTechnology[techId] || 0) + 1;
|
|
1295
|
+
});
|
|
1295
1296
|
}
|
|
1296
1297
|
});
|
|
1297
1298
|
return counts;
|
|
@@ -1370,6 +1371,160 @@ var ProductService = class extends BaseService {
|
|
|
1370
1371
|
...docSnap.data()
|
|
1371
1372
|
};
|
|
1372
1373
|
}
|
|
1374
|
+
// ==========================================
|
|
1375
|
+
// NEW METHODS: Top-level collection (preferred)
|
|
1376
|
+
// ==========================================
|
|
1377
|
+
/**
|
|
1378
|
+
* Creates a new product in the top-level collection
|
|
1379
|
+
*/
|
|
1380
|
+
async createTopLevel(brandId, product, technologyIds = []) {
|
|
1381
|
+
const now = /* @__PURE__ */ new Date();
|
|
1382
|
+
const newProduct = {
|
|
1383
|
+
...product,
|
|
1384
|
+
brandId,
|
|
1385
|
+
assignedTechnologyIds: technologyIds,
|
|
1386
|
+
createdAt: now,
|
|
1387
|
+
updatedAt: now,
|
|
1388
|
+
isActive: true
|
|
1389
|
+
};
|
|
1390
|
+
const productRef = await (0, import_firestore8.addDoc)(this.getTopLevelProductsRef(), newProduct);
|
|
1391
|
+
return { id: productRef.id, ...newProduct };
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Gets all products from the top-level collection
|
|
1395
|
+
*/
|
|
1396
|
+
async getAllTopLevel(options) {
|
|
1397
|
+
const { rowsPerPage, lastVisible, brandId } = options;
|
|
1398
|
+
const constraints = [(0, import_firestore8.where)("isActive", "==", true), (0, import_firestore8.orderBy)("name")];
|
|
1399
|
+
if (brandId) {
|
|
1400
|
+
constraints.push((0, import_firestore8.where)("brandId", "==", brandId));
|
|
1401
|
+
}
|
|
1402
|
+
if (lastVisible) {
|
|
1403
|
+
constraints.push((0, import_firestore8.startAfter)(lastVisible));
|
|
1404
|
+
}
|
|
1405
|
+
constraints.push((0, import_firestore8.limit)(rowsPerPage));
|
|
1406
|
+
const q = (0, import_firestore8.query)(this.getTopLevelProductsRef(), ...constraints);
|
|
1407
|
+
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1408
|
+
const products = snapshot.docs.map(
|
|
1409
|
+
(doc11) => ({
|
|
1410
|
+
id: doc11.id,
|
|
1411
|
+
...doc11.data()
|
|
1412
|
+
})
|
|
1413
|
+
);
|
|
1414
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
1415
|
+
return { products, lastVisible: newLastVisible };
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Gets a product by ID from the top-level collection
|
|
1419
|
+
*/
|
|
1420
|
+
async getByIdTopLevel(productId) {
|
|
1421
|
+
const docRef = (0, import_firestore8.doc)(this.getTopLevelProductsRef(), productId);
|
|
1422
|
+
const docSnap = await (0, import_firestore8.getDoc)(docRef);
|
|
1423
|
+
if (!docSnap.exists()) return null;
|
|
1424
|
+
return {
|
|
1425
|
+
id: docSnap.id,
|
|
1426
|
+
...docSnap.data()
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Updates a product in the top-level collection
|
|
1431
|
+
*/
|
|
1432
|
+
async updateTopLevel(productId, product) {
|
|
1433
|
+
const updateData = {
|
|
1434
|
+
...product,
|
|
1435
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1436
|
+
};
|
|
1437
|
+
const docRef = (0, import_firestore8.doc)(this.getTopLevelProductsRef(), productId);
|
|
1438
|
+
await (0, import_firestore8.updateDoc)(docRef, updateData);
|
|
1439
|
+
return this.getByIdTopLevel(productId);
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Deletes a product from the top-level collection (soft delete)
|
|
1443
|
+
*/
|
|
1444
|
+
async deleteTopLevel(productId) {
|
|
1445
|
+
await this.updateTopLevel(productId, {
|
|
1446
|
+
isActive: false
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Assigns a product to a technology
|
|
1451
|
+
*/
|
|
1452
|
+
async assignToTechnology(productId, technologyId) {
|
|
1453
|
+
const docRef = (0, import_firestore8.doc)(this.getTopLevelProductsRef(), productId);
|
|
1454
|
+
await (0, import_firestore8.updateDoc)(docRef, {
|
|
1455
|
+
assignedTechnologyIds: (0, import_firestore8.arrayUnion)(technologyId),
|
|
1456
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Unassigns a product from a technology
|
|
1461
|
+
*/
|
|
1462
|
+
async unassignFromTechnology(productId, technologyId) {
|
|
1463
|
+
const docRef = (0, import_firestore8.doc)(this.getTopLevelProductsRef(), productId);
|
|
1464
|
+
await (0, import_firestore8.updateDoc)(docRef, {
|
|
1465
|
+
assignedTechnologyIds: (0, import_firestore8.arrayRemove)(technologyId),
|
|
1466
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Gets products assigned to a specific technology
|
|
1471
|
+
*/
|
|
1472
|
+
async getAssignedProducts(technologyId) {
|
|
1473
|
+
const q = (0, import_firestore8.query)(
|
|
1474
|
+
this.getTopLevelProductsRef(),
|
|
1475
|
+
(0, import_firestore8.where)("assignedTechnologyIds", "array-contains", technologyId),
|
|
1476
|
+
(0, import_firestore8.where)("isActive", "==", true),
|
|
1477
|
+
(0, import_firestore8.orderBy)("name")
|
|
1478
|
+
);
|
|
1479
|
+
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1480
|
+
return snapshot.docs.map(
|
|
1481
|
+
(doc11) => ({
|
|
1482
|
+
id: doc11.id,
|
|
1483
|
+
...doc11.data()
|
|
1484
|
+
})
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Gets products NOT assigned to a specific technology
|
|
1489
|
+
*/
|
|
1490
|
+
async getUnassignedProducts(technologyId) {
|
|
1491
|
+
const q = (0, import_firestore8.query)(
|
|
1492
|
+
this.getTopLevelProductsRef(),
|
|
1493
|
+
(0, import_firestore8.where)("isActive", "==", true),
|
|
1494
|
+
(0, import_firestore8.orderBy)("name")
|
|
1495
|
+
);
|
|
1496
|
+
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1497
|
+
const allProducts = snapshot.docs.map(
|
|
1498
|
+
(doc11) => ({
|
|
1499
|
+
id: doc11.id,
|
|
1500
|
+
...doc11.data()
|
|
1501
|
+
})
|
|
1502
|
+
);
|
|
1503
|
+
return allProducts.filter(
|
|
1504
|
+
(product) => {
|
|
1505
|
+
var _a;
|
|
1506
|
+
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Gets all products for a brand (from top-level collection)
|
|
1512
|
+
*/
|
|
1513
|
+
async getByBrand(brandId) {
|
|
1514
|
+
const q = (0, import_firestore8.query)(
|
|
1515
|
+
this.getTopLevelProductsRef(),
|
|
1516
|
+
(0, import_firestore8.where)("brandId", "==", brandId),
|
|
1517
|
+
(0, import_firestore8.where)("isActive", "==", true),
|
|
1518
|
+
(0, import_firestore8.orderBy)("name")
|
|
1519
|
+
);
|
|
1520
|
+
const snapshot = await (0, import_firestore8.getDocs)(q);
|
|
1521
|
+
return snapshot.docs.map(
|
|
1522
|
+
(doc11) => ({
|
|
1523
|
+
id: doc11.id,
|
|
1524
|
+
...doc11.data()
|
|
1525
|
+
})
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1373
1528
|
};
|
|
1374
1529
|
|
|
1375
1530
|
// src/backoffice/services/requirement.service.ts
|
|
@@ -2347,6 +2502,95 @@ var TechnologyService = class extends BaseService {
|
|
|
2347
2502
|
})
|
|
2348
2503
|
);
|
|
2349
2504
|
}
|
|
2505
|
+
// ==========================================
|
|
2506
|
+
// NEW METHODS: Product assignment management
|
|
2507
|
+
// ==========================================
|
|
2508
|
+
/**
|
|
2509
|
+
* Assigns multiple products to a technology
|
|
2510
|
+
* Updates each product's assignedTechnologyIds array
|
|
2511
|
+
*/
|
|
2512
|
+
async assignProducts(technologyId, productIds) {
|
|
2513
|
+
const batch = (0, import_firestore11.writeBatch)(this.db);
|
|
2514
|
+
for (const productId of productIds) {
|
|
2515
|
+
const productRef = (0, import_firestore11.doc)(this.db, PRODUCTS_COLLECTION, productId);
|
|
2516
|
+
batch.update(productRef, {
|
|
2517
|
+
assignedTechnologyIds: (0, import_firestore11.arrayUnion)(technologyId),
|
|
2518
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
await batch.commit();
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Unassigns multiple products from a technology
|
|
2525
|
+
* Updates each product's assignedTechnologyIds array
|
|
2526
|
+
*/
|
|
2527
|
+
async unassignProducts(technologyId, productIds) {
|
|
2528
|
+
const batch = (0, import_firestore11.writeBatch)(this.db);
|
|
2529
|
+
for (const productId of productIds) {
|
|
2530
|
+
const productRef = (0, import_firestore11.doc)(this.db, PRODUCTS_COLLECTION, productId);
|
|
2531
|
+
batch.update(productRef, {
|
|
2532
|
+
assignedTechnologyIds: (0, import_firestore11.arrayRemove)(technologyId),
|
|
2533
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
await batch.commit();
|
|
2537
|
+
}
|
|
2538
|
+
/**
|
|
2539
|
+
* Gets products assigned to a specific technology
|
|
2540
|
+
* Reads from top-level collection for immediate consistency (Cloud Functions may lag)
|
|
2541
|
+
*/
|
|
2542
|
+
async getAssignedProducts(technologyId) {
|
|
2543
|
+
const q = (0, import_firestore11.query)(
|
|
2544
|
+
(0, import_firestore11.collection)(this.db, PRODUCTS_COLLECTION),
|
|
2545
|
+
(0, import_firestore11.where)("assignedTechnologyIds", "array-contains", technologyId),
|
|
2546
|
+
(0, import_firestore11.where)("isActive", "==", true),
|
|
2547
|
+
(0, import_firestore11.orderBy)("name")
|
|
2548
|
+
);
|
|
2549
|
+
const snapshot = await (0, import_firestore11.getDocs)(q);
|
|
2550
|
+
return snapshot.docs.map(
|
|
2551
|
+
(doc11) => ({
|
|
2552
|
+
id: doc11.id,
|
|
2553
|
+
...doc11.data()
|
|
2554
|
+
})
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* Gets products NOT assigned to a specific technology
|
|
2559
|
+
*/
|
|
2560
|
+
async getUnassignedProducts(technologyId) {
|
|
2561
|
+
const q = (0, import_firestore11.query)(
|
|
2562
|
+
(0, import_firestore11.collection)(this.db, PRODUCTS_COLLECTION),
|
|
2563
|
+
(0, import_firestore11.where)("isActive", "==", true),
|
|
2564
|
+
(0, import_firestore11.orderBy)("name")
|
|
2565
|
+
);
|
|
2566
|
+
const snapshot = await (0, import_firestore11.getDocs)(q);
|
|
2567
|
+
const allProducts = snapshot.docs.map(
|
|
2568
|
+
(doc11) => ({
|
|
2569
|
+
id: doc11.id,
|
|
2570
|
+
...doc11.data()
|
|
2571
|
+
})
|
|
2572
|
+
);
|
|
2573
|
+
return allProducts.filter(
|
|
2574
|
+
(product) => {
|
|
2575
|
+
var _a;
|
|
2576
|
+
return !((_a = product.assignedTechnologyIds) == null ? void 0 : _a.includes(technologyId));
|
|
2577
|
+
}
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Gets product assignment statistics for a technology
|
|
2582
|
+
*/
|
|
2583
|
+
async getProductStats(technologyId) {
|
|
2584
|
+
const products = await this.getAssignedProducts(technologyId);
|
|
2585
|
+
const byBrand = {};
|
|
2586
|
+
products.forEach((product) => {
|
|
2587
|
+
byBrand[product.brandName] = (byBrand[product.brandName] || 0) + 1;
|
|
2588
|
+
});
|
|
2589
|
+
return {
|
|
2590
|
+
totalAssigned: products.length,
|
|
2591
|
+
byBrand
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2350
2594
|
};
|
|
2351
2595
|
|
|
2352
2596
|
// src/backoffice/services/constants.service.ts
|