diff --git a/src/app/modules/dcb-dashboard/services/dashboard-access.service.ts b/src/app/modules/dcb-dashboard/services/dashboard-access.service.ts index b81ebf2..beeb7af 100644 --- a/src/app/modules/dcb-dashboard/services/dashboard-access.service.ts +++ b/src/app/modules/dcb-dashboard/services/dashboard-access.service.ts @@ -150,7 +150,7 @@ export class DashboardAccessService { if (access.isHubUser) { return this.merchantService.getAllMerchants().pipe( map(merchants => { - const available: AllowedMerchant[] = merchants.map(m => ({ + const available: AllowedMerchant[] = merchants.items.map(m => ({ id: m.id, name: m.name })); diff --git a/src/app/modules/hub-users-management/hub-users.ts b/src/app/modules/hub-users-management/hub-users.ts index 3d70d88..bdf10a2 100644 --- a/src/app/modules/hub-users-management/hub-users.ts +++ b/src/app/modules/hub-users-management/hub-users.ts @@ -8,7 +8,6 @@ import { Subject, takeUntil } from 'rxjs'; import { HubUsersService } from './hub-users.service'; import { RoleManagementService } from '@core/services/hub-users-roles-management.service'; import { AuthService } from '@core/services/auth.service'; -import { MerchantSyncService } from './merchant-sync-orchestrator.service'; import { PageTitle } from '@app/components/page-title/page-title'; import { HubUsersList } from './hub-users-list/hub-users-list'; import { HubUserProfile } from './hub-users-profile/hub-users-profile'; @@ -39,7 +38,6 @@ export class HubUsersManagement implements OnInit, OnDestroy { private modalService = inject(NgbModal); private authService = inject(AuthService); private hubUsersService = inject(HubUsersService); - private merchantSyncService = inject(MerchantSyncService); protected roleService = inject(RoleManagementService); private cdRef = inject(ChangeDetectorRef); private destroy$ = new Subject(); @@ -476,45 +474,6 @@ export class HubUsersManagement implements OnInit, OnDestroy { }); } - /** - * Crée un marchand dans MerchantConfig (séparément de l'utilisateur) - */ - createMerchant() { - // Validation du formulaire marchand - const merchantValidation = this.validateMerchantForm(); - if (!merchantValidation.isValid) { - this.createUserError = merchantValidation.error!; - console.error('❌ Merchant form validation failed:', merchantValidation.error); - return; - } - - this.creatingMerchant = true; - - console.log('📤 Creating merchant in MerchantConfig...'); - - this.merchantSyncService.createMerchantInConfigOnly(this.newMerchant) - .pipe(takeUntil(this.destroy$)) - .subscribe({ - next: (merchantConfig) => { - console.log('✅ Merchant created in MerchantConfig:', merchantConfig); - this.creatingMerchant = false; - - // Optionnel: proposer d'associer un utilisateur au marchand créé - console.log(`✅ Merchant ID: ${merchantConfig.id} - Name: ${merchantConfig.name}`); - - this.modalService.dismissAll(); - this.resetMerchantForm(); - this.cdRef.detectChanges(); - }, - error: (error) => { - console.error('❌ Error creating merchant in MerchantConfig:', error); - this.creatingMerchant = false; - this.createUserError = this.getMerchantErrorMessage(error); - this.cdRef.detectChanges(); - } - }); - } - /** * Vérifie si le rôle est un rôle partenaire */ diff --git a/src/app/modules/hub-users-management/merchant-sync-orchestrator.service.ts b/src/app/modules/hub-users-management/merchant-sync-orchestrator.service.ts deleted file mode 100644 index 717a741..0000000 --- a/src/app/modules/hub-users-management/merchant-sync-orchestrator.service.ts +++ /dev/null @@ -1,507 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Observable, forkJoin, map, switchMap, catchError, of, throwError } from 'rxjs'; -import { - CreateUserDto, - User, - UserType, - UserRole, -} from '@core/models/dcb-bo-hub-user.model'; - -import { MerchantUsersService} from '@modules/hub-users-management/merchant-users.service'; - -import { MerchantConfigService } from '@modules/merchant-config/merchant-config.service'; - -import { - CreateMerchantDto, - UpdateMerchantDto, - Merchant, - MerchantUser, - AddUserToMerchantDto, - UpdateUserRoleDto -} from '@core/models/merchant-config.model'; - -export interface MerchantSyncResult { - merchantConfig: Merchant; - keycloakUser?: User; // Utilisateur associé (optionnel) -} - -export interface MerchantUserSyncResult { - keycloakUser: User; - merchantConfigUser?: MerchantUser; -} - -export interface MerchantSyncStatus { - existsInKeycloak: boolean; - existsInMerchantConfig: boolean; - usersSynced: boolean; - syncedUserCount: number; - totalUserCount: number; -} - -export interface UserMerchantAssociation { - userId: string; - merchantConfigId: string; - role: UserRole; // Rôle dans MerchantConfig -} - -@Injectable({ providedIn: 'root' }) -export class MerchantSyncService { - private merchantUsersService = inject(MerchantUsersService); - private merchantConfigService = inject(MerchantConfigService); - - // ==================== CONSTANTES ==================== - - private readonly KEYCLOAK_MERCHANT_ROLES = [ - UserRole.DCB_PARTNER_ADMIN, - UserRole.DCB_PARTNER_MANAGER, - UserRole.DCB_PARTNER_SUPPORT - ]; - - private readonly MERCHANT_CONFIG_ROLES = [ - UserRole.MERCHANT_CONFIG_ADMIN, - UserRole.MERCHANT_CONFIG_MANAGER, - UserRole.MERCHANT_CONFIG_TECHNICAL, - UserRole.MERCHANT_CONFIG_VIEWER - ]; - - private readonly ROLE_MAPPING: Map = new Map([ - // Keycloak -> Merchant Config - [UserRole.DCB_PARTNER_ADMIN, UserRole.MERCHANT_CONFIG_ADMIN], - [UserRole.DCB_PARTNER_MANAGER, UserRole.MERCHANT_CONFIG_MANAGER], - [UserRole.DCB_PARTNER_SUPPORT, UserRole.MERCHANT_CONFIG_VIEWER], - - // Merchant Config -> Keycloak - [UserRole.MERCHANT_CONFIG_ADMIN, UserRole.DCB_PARTNER_ADMIN], - [UserRole.MERCHANT_CONFIG_MANAGER, UserRole.DCB_PARTNER_MANAGER], - [UserRole.MERCHANT_CONFIG_TECHNICAL, UserRole.DCB_PARTNER_SUPPORT], - [UserRole.MERCHANT_CONFIG_VIEWER, UserRole.DCB_PARTNER_SUPPORT] - ]); - - // ==================== MÉTHODES DE BASE ==================== - - /** - * CREATE - Créer un merchant uniquement dans MerchantConfig - */ - createMerchantInConfigOnly(merchantData: CreateMerchantDto): Observable { - console.log('📝 CREATE Merchant dans MerchantConfig seulement...'); - - return this.merchantConfigService.createMerchant(merchantData).pipe( - map(merchant => { - console.log('✅ Merchant créé dans MerchantConfig:', merchant); - return merchant; - }), - catchError(error => { - console.error('❌ Échec création merchant dans MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - /** - * CREATE - Créer un utilisateur dans Keycloak (sans association) - */ - createKeycloakUser( - userData: { - username: string; - email: string; - password: string; - firstName: string; - lastName: string; - role: UserRole; - enabled?: boolean; - emailVerified?: boolean; - } - ): Observable { - console.log('📝 CREATE User dans Keycloak...'); - - // Déterminer le type d'utilisateur selon le rôle - const userType = this.isMerchantRole(userData.role) - ? UserType.MERCHANT_PARTNER - : UserType.HUB; - - const keycloakUserDto: CreateUserDto = { - username: userData.username, - email: userData.email, - firstName: userData.firstName, - lastName: userData.lastName, - password: userData.password, - userType: userType, - role: userData.role, - enabled: userData.enabled ?? true, - emailVerified: userData.emailVerified ?? false - }; - - // Sélectionner le service approprié selon le type d'utilisateur - const createService = this.merchantUsersService.createMerchantUser(keycloakUserDto); - - return createService.pipe( - map(user => { - console.log('✅ Utilisateur créé dans Keycloak:', user); - return user; - }), - catchError(error => { - console.error('❌ Échec création utilisateur dans Keycloak:', error); - return throwError(() => error); - }) - ); - } - - /** - * READ - Récupérer un merchant depuis MerchantConfig seulement - */ - getMerchantFromConfigOnly(merchantConfigId: string): Observable { - console.log('🔍 READ Merchant depuis MerchantConfig seulement...'); - - return this.merchantConfigService.getMerchantById(Number(merchantConfigId)).pipe( - map(merchant => { - console.log('✅ Merchant récupéré depuis MerchantConfig:', merchant); - return merchant; - }), - catchError(error => { - console.error('❌ Erreur récupération merchant depuis MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - /** - * READ - Récupérer tous les merchants depuis MerchantConfig - */ - getAllMerchantsFromConfig(): Observable { - console.log('🔍 READ All Merchants depuis MerchantConfig...'); - - return this.merchantConfigService.getAllMerchants().pipe( - map(merchants => { - console.log(`✅ ${merchants.length} merchants récupérés depuis MerchantConfig`); - return merchants; - }), - catchError(error => { - console.error('❌ Erreur récupération merchants depuis MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - // ==================== ASSOCIATION UTILISATEUR-MERCHANT ==================== - - /** - * Associer un utilisateur existant à un marchand - */ - associateUserToMerchant( - association: UserMerchantAssociation - ): Observable { - console.log('🔗 ASSOCIATE User to Merchant...'); - - return forkJoin({ - user: this.getUserById(association.userId), - merchant: this.merchantConfigService.getMerchantById(Number(association.merchantConfigId)) - }).pipe( - switchMap(({ user, merchant }) => { - console.log(`🔗 Associating user ${user.username} to merchant ${merchant.name}`); - - // 2. Ajouter l'utilisateur à MerchantConfig - const merchantConfigUserDto: AddUserToMerchantDto = { - userId: user.id, - role: association.role, - merchantPartnerId: Number(association.merchantConfigId), - }; - - return this.merchantConfigService.addUserToMerchant(merchantConfigUserDto).pipe( - map(merchantConfigUser => { - console.log('✅ User added to Merchant Config:', merchantConfigUser); - return { - keycloakUser: user, - merchantConfigUser - }; - }), - catchError(error => { - console.error('❌ Error adding user to MerchantConfig:', error); - return throwError(() => error); - }) - ); - }), - catchError(error => { - console.error('❌ Error in association process:', error); - return throwError(() => error); - }) - ); - } - - /** - * Dissocier un utilisateur d'un marchand - */ - dissociateUserFromMerchant( - userId: string, - merchantConfigId: string - ): Observable<{ success: boolean; message: string }> { - console.log('🔗 DISSOCIATE User from Merchant...'); - - return forkJoin({ - // Retirer l'utilisateur de MerchantConfig - merchantConfigRemoval: this.merchantConfigService.removeUserFromMerchant( - Number(merchantConfigId), - userId - ).pipe(catchError(() => of({ ignored: true }))) - }).pipe( - map(() => ({ - success: true, - message: 'Utilisateur dissocié du marchand avec succès' - })), - catchError(async (error) => ({ - success: false, - message: `Erreur lors de la dissociation: ${error.message}` - })) - ); - } - - /** - * Récupérer les utilisateurs associés à un marchand - */ - getUsersByMerchant(merchantConfigId: string): Observable { - console.log('🔍 READ Users by Merchant...'); - - // Récupérer les utilisateurs de MerchantConfig - return this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe( - switchMap(merchantConfigUsers => { - if (merchantConfigUsers.total === 0) { - return of([]); - } - - // Récupérer les détails de chaque utilisateur depuis Keycloak - const userObservables = merchantConfigUsers.items.map((mcUser: { userId: string; }) => - this.getUserById(mcUser.userId).pipe( - catchError(() => of(null)) // Ignorer les utilisateurs non trouvés - ) - ); - - return forkJoin(userObservables).pipe( - map(users => users.filter((user): user is User => user !== null)) - ); - }), - map(users => { - console.log(`✅ ${users.length} utilisateurs trouvés pour merchant ${merchantConfigId}`); - return users; - }), - catchError(error => { - console.error('❌ Erreur récupération utilisateurs:', error); - return throwError(() => error); - }) - ); - } - - // ==================== MÉTHODES DE GESTION ==================== - - /** - * UPDATE - Mettre à jour un merchant dans MerchantConfig seulement - */ - updateMerchantInConfigOnly( - merchantConfigId: string, - updates: UpdateMerchantDto - ): Observable { - console.log('✏️ UPDATE Merchant dans MerchantConfig seulement...'); - - return this.merchantConfigService.updateMerchant( - Number(merchantConfigId), - updates - ).pipe( - map(merchant => { - console.log('✅ Merchant mis à jour dans MerchantConfig:', merchant); - return merchant; - }), - catchError(error => { - console.error('❌ Erreur mise à jour merchant dans MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - /** - * UPDATE - Mettre à jour un utilisateur dans Keycloak - */ - updateKeycloakUserRole( - keycloakUserId: string, - newRole: UserRole - ): Observable { - console.log('✏️ UPDATE User dans Keycloak...'); - - return this.getUserById(keycloakUserId).pipe( - switchMap(user => { - const updateService = this.merchantUsersService.updateMerchantUserRole(keycloakUserId, newRole); - - return updateService.pipe( - map(updatedUser => { - console.log('✅ Utilisateur mis à jour dans Keycloak:', updatedUser); - return updatedUser; - }) - ); - }), - catchError(error => { - console.error('❌ Erreur mise à jour utilisateur dans Keycloak:', error); - return throwError(() => error); - }) - ); - } - - /** - * UPDATE - Changer le rôle d'un utilisateur dans MerchantConfig - */ - updateUserRoleInMerchantConfig( - merchantConfigId: string, - userId: string, - newRole: UserRole - ): Observable { - console.log('✏️ UPDATE User Role dans MerchantConfig...'); - - const updateRoleDto: UpdateUserRoleDto = { - role: newRole - }; - - this.updateKeycloakUserRole(userId, newRole) - - return this.merchantConfigService.updateUserRole( - Number(merchantConfigId), - userId, - updateRoleDto - ).pipe( - map(merchantConfigUser => { - console.log('✅ Rôle utilisateur mis à jour dans MerchantConfig:', merchantConfigUser); - return merchantConfigUser; - }), - catchError(error => { - console.error('❌ Erreur changement rôle utilisateur dans MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - /** - * DELETE - Supprimer un merchant de MerchantConfig seulement - */ - deleteMerchantFromConfigOnly( - merchantConfigId: string - ): Observable<{ success: boolean; message: string }> { - console.log('🗑️ DELETE Merchant de MerchantConfig seulement...'); - - return this.merchantConfigService.deleteMerchant(Number(merchantConfigId)).pipe( - map(() => ({ - success: true, - message: 'Merchant supprimé de MerchantConfig avec succès' - })), - catchError(async (error) => ({ - success: false, - message: `Erreur suppression merchant: ${error.message}` - })) - ); - } - - /** - * DELETE - Supprimer un utilisateur de Keycloak - */ - deleteKeycloakUser( - userId: string - ): Observable<{ success: boolean; message: string }> { - console.log('🗑️ DELETE User de Keycloak...'); - - return this.getUserById(userId).pipe( - switchMap(user => { - const deleteService = this.merchantUsersService.deleteMerchantUser(userId); - - return deleteService.pipe( - map(() => ({ - success: true, - message: 'Utilisateur supprimé de Keycloak avec succès' - })) - ); - }), - catchError(async (error) => ({ - success: false, - message: `Erreur suppression utilisateur: ${error.message}` - })) - ); - } - - // ==================== MÉTHODES DE RECHERCHE ==================== - - /** - * Rechercher des merchants dans MerchantConfig - */ - searchMerchantsInConfig(query: string): Observable { - console.log('🔍 SEARCH Merchants dans MerchantConfig...'); - - return this.merchantConfigService.getAllMerchants({ query }).pipe( - map(merchants => { - console.log(`✅ ${merchants.length} merchants trouvés avec "${query}"`); - return merchants; - }), - catchError(error => { - console.error('❌ Erreur recherche merchants dans MerchantConfig:', error); - return throwError(() => error); - }) - ); - } - - /** - * Rechercher des utilisateurs dans Keycloak - */ - searchKeycloakUsers(query: string): Observable { - console.log('🔍 SEARCH Users dans Keycloak...'); - - // Rechercher dans les deux types d'utilisateurs - return forkJoin({ - merchantUsers: this.merchantUsersService.searchMerchantUsers({ query }).pipe( - catchError(() => of([])) - ) - }).pipe( - map(({ merchantUsers }) => { - const allUsers = [ - ...merchantUsers - ]; - console.log(`✅ ${allUsers.length} utilisateurs trouvés avec "${query}"`); - return allUsers; - }), - catchError(error => { - console.error('❌ Erreur recherche utilisateurs:', error); - return throwError(() => error); - }) - ); - } - - // ==================== MÉTHODES PRIVÉES ==================== - - /** - * Récupérer un utilisateur par ID (gère les deux types) - */ - private getUserById(userId: string): Observable { - return forkJoin({ - merchantUser: this.merchantUsersService.getMerchantUserById(userId).pipe( - catchError(() => of(null)) - ) - }).pipe( - map(({ merchantUser }) => { - if (merchantUser) return merchantUser; - throw new Error(`Utilisateur ${userId} non trouvé`); - }), - catchError(error => { - console.error(`❌ Erreur récupération utilisateur ${userId}:`, error); - return throwError(() => error); - }) - ); - } - - /** - * Vérifie si le rôle est un rôle partenaire - */ - private isMerchantRole(role: UserRole): boolean { - return [ - UserRole.DCB_PARTNER_ADMIN, - UserRole.DCB_PARTNER_MANAGER, - UserRole.DCB_PARTNER_SUPPORT - ].includes(role); - } - - private mapToMerchantConfigRole(keycloakRole: UserRole): UserRole { - const mappedRole = this.ROLE_MAPPING.get(keycloakRole); - return mappedRole || UserRole.MERCHANT_CONFIG_VIEWER; - } - -} \ No newline at end of file diff --git a/src/app/modules/hub-users-management/merchant-users.ts b/src/app/modules/hub-users-management/merchant-users.ts index 55167a2..441021e 100644 --- a/src/app/modules/hub-users-management/merchant-users.ts +++ b/src/app/modules/hub-users-management/merchant-users.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { NgIcon } from '@ng-icons/core'; import { NgbNavModule, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; -import { catchError, map, of, Subject, switchMap, takeUntil } from 'rxjs'; +import { catchError, map, of, Subject, switchMap, takeUntil, throwError } from 'rxjs'; import { MerchantUsersService } from './merchant-users.service'; import { RoleManagementService } from '@core/services/hub-users-roles-management.service'; @@ -346,23 +346,49 @@ export class MerchantUsersManagement implements OnInit, OnDestroy { } private loadAllMerchants(): void { + if (this.isMerchantUser) { + console.log('⚠️ User is not a Hub user, merchant list not displayed'); + return; + } + this.loadingMerchantPartners = true; this.merchantPartnersError = ''; - - this.merchantConfigService.fetchAllMerchants() + this.merchantPartners = []; + + const pageSize = 10; // batch size identique au backend + let currentPage = 1; + + const loadNextPage = () => { + this.merchantConfigService.getAllMerchants(currentPage, pageSize) .pipe(takeUntil(this.destroy$)) .subscribe({ - next: (merchants) => { - this.merchantPartners = merchants; - this.loadingMerchantPartners = false; - console.log('✅ All merchants loaded for Hub Admin:', merchants.length); + next: response => { + const items = Array.isArray(response.items) ? response.items : []; + this.merchantPartners.push(...items); + + const loadedCount = this.merchantPartners.length; + const totalItems = response.total; + + console.log(`📥 Page ${currentPage} chargée (${loadedCount}/${totalItems} merchants)`); + + if (loadedCount < totalItems) { + currentPage++; + loadNextPage(); + } else { + this.loadingMerchantPartners = false; + console.log(`✅ Tous les merchants chargés: ${loadedCount}`); + } }, - error: (error) => { - console.error('❌ Error loading all merchants:', error); + error: err => { + console.error('❌ Error loading merchants:', err); this.merchantPartnersError = 'Erreur lors du chargement des merchants'; this.loadingMerchantPartners = false; } }); + + }; + + loadNextPage(); } getCurrentUserMerchantName(): string { @@ -593,7 +619,6 @@ export class MerchantUsersManagement implements OnInit, OnDestroy { merchantPartnerId: merchantPartnerId }; - console.log('📤 Adding user to merchant config:', addUserDto); return this.merchantConfigService.addUserToMerchant(addUserDto).pipe( map((merchantConfigUser) => { return { @@ -608,15 +633,16 @@ export class MerchantUsersManagement implements OnInit, OnDestroy { // ROLLBACK: Supprimer l'utilisateur Keycloak créé if (createdUserId) { console.log(`🔄 Rollback: Deleting Keycloak user ${createdUserId} because merchant association failed`); + return this.merchantUsersService.deleteMerchantUser(createdUserId).pipe( - map(() => { + switchMap(() => { console.log(`✅ Keycloak user ${createdUserId} deleted as part of rollback`); - throw new Error(`Failed to associate user with merchant: ${merchantError.message}. User creation rolled back.`); + // On propage l'erreur originale en créant un Observable qui échoue + return throwError(() => new Error(`Failed to associate user with merchant: ${merchantError.message}. User creation rolled back.`)); }), catchError((deleteError) => { console.error(`❌ Failed to delete Keycloak user during rollback:`, deleteError); - // Même si le rollback échoue, on propage l'erreur originale avec info supplémentaire - throw new Error(`Failed to associate user with merchant: ${merchantError.message}. AND failed to rollback user creation: ${deleteError.message}`); + return throwError(() => new Error(`Failed to associate user with merchant: ${merchantError.message}. AND failed to rollback user creation: ${deleteError.message}`)); }) ); } diff --git a/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.html b/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.html index d79ff76..736f7d7 100644 --- a/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.html +++ b/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.html @@ -334,7 +334,7 @@ - @if (totalPages > 1) { + @if (totalPages >= 1) {
Affichage de {{ getStartIndex() }} diff --git a/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.ts b/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.ts index b92cc2f..b23b291 100644 --- a/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.ts +++ b/src/app/modules/merchant-config/merchant-config-list/merchant-config-list.ts @@ -154,7 +154,7 @@ export class MerchantConfigsList implements OnInit, OnDestroy { ]; } - loadMerchants() { + loadMerchants(): void { if (!this.isHubUser) { console.log('⚠️ User is not a Hub user, merchant list not displayed'); return; @@ -163,15 +163,14 @@ export class MerchantConfigsList implements OnInit, OnDestroy { this.loading = true; this.error = ''; - this.merchantConfigService.getMerchants( - this.currentPage, - this.itemsPerPage, - this.buildSearchParams() - ) + const params = this.buildSearchParams(); + const skip = (this.currentPage - 1) * this.itemsPerPage; + + this.merchantConfigService.getAllMerchants(this.currentPage, this.itemsPerPage, params) .pipe( takeUntil(this.destroy$), catchError(error => { - console.error('Error loading merchants:', error); + console.error('❌ Error loading merchants:', error); this.error = 'Erreur lors du chargement des marchands'; return of({ items: [], @@ -182,37 +181,26 @@ export class MerchantConfigsList implements OnInit, OnDestroy { } as PaginatedResponse); }) ) - .subscribe({ - next: (response) => { - console.log('📊 Pagination response:', { - page: response.page, - total: response.total, - totalPages: response.totalPages, - itemsCount: response.items?.length, - limit: response.limit - }); - - this.allMerchants = response.items || []; - this.displayedMerchants = response.items || []; - this.totalItems = response.total || 0; - this.totalPages = response.totalPages || 0; - - this.loading = false; - this.cdRef.detectChanges(); - }, - error: () => { - this.error = 'Erreur lors du chargement des marchands'; - this.loading = false; - this.allMerchants = []; - this.displayedMerchants = []; - this.totalItems = 0; - this.totalPages = 0; - this.cdRef.detectChanges(); - } + .subscribe(response => { + this.allMerchants = response.items || []; + this.displayedMerchants = response.items || []; + this.totalItems = response.total || 0; + this.totalPages = response.totalPages || Math.ceil((response.total || 0) / this.itemsPerPage); + + this.loading = false; + this.cdRef.detectChanges(); + + console.log('📊 Pagination response:', { + page: response.page, + total: response.total, + totalPages: this.totalPages, + itemsCount: response.items?.length, + limit: response.limit + }); }); } - // ==================== AFFICHAGE DU LOGO ==================== +// ==================== AFFICHAGE DU LOGO ==================== /** diff --git a/src/app/modules/merchant-config/merchant-config.service.ts b/src/app/modules/merchant-config/merchant-config.service.ts index 9c156bc..520c07d 100644 --- a/src/app/modules/merchant-config/merchant-config.service.ts +++ b/src/app/modules/merchant-config/merchant-config.service.ts @@ -61,200 +61,44 @@ export class MerchantConfigService { ); } - getMerchants(page: number = 1, limit: number = 10, params?: SearchMerchantsParams): Observable> { - // Vérifier si le cache est valide - const paramsChanged = !this.areParamsEqual(params, this.cacheParams); - - if (paramsChanged || this.merchantsCache.length === 0) { - // Nouvelle requête nécessaire - return this.fetchAllMerchants(params).pipe( - map(allMerchants => { - // Mettre en cache - this.merchantsCache = allMerchants; - this.cacheParams = params; - - // Appliquer pagination - return this.applyPagination(allMerchants, page, limit); - }) - ); - } else { - // Vérifier si le cache contient assez d'éléments pour la page demandée - const neededItems = page * limit; - - if (neededItems <= this.merchantsCache.length) { - // Cache suffisant - return of(this.applyPagination(this.merchantsCache, page, limit)); - } else { - // Besoin de plus d'éléments - const additionalNeeded = neededItems - this.merchantsCache.length; - const newTake = this.calculateOptimalTake(additionalNeeded, page, limit); - - console.log(`🔄 Cache insufficient (${this.merchantsCache.length}), fetching ${newTake} more items`); - - return this.fetchAdditionalMerchants(newTake, params).pipe( - map(additionalMerchants => { - // Fusionner avec le cache (éviter les doublons) - const merged = this.mergeMerchants(this.merchantsCache, additionalMerchants); - this.merchantsCache = merged; - - return this.applyPagination(merged, page, limit); - }) - ); - } - } - } + /** + * Récupère tous les merchants (optionnel: avec recherche) + */ + getAllMerchants( + page: number = 1, + limit: number = 10, + params?: SearchMerchantsParams + ): Observable> { + const skip = (page - 1) * limit; - // Calculer le take optimal en fonction du total - private calculateOptimalTake(totalCount: number, page: number, limit: number): number { - // Calculer combien d'éléments nous avons besoin pour la pagination - const neededItems = page * limit; - - // Ajouter un buffer pour éviter les appels fréquents - const buffer = Math.max(limit * 2, 100); - - // Calculer le take nécessaire - let optimalTake = neededItems + buffer; - - // Si le total est connu, adapter le take - if (totalCount > 0) { - // Prendre soit ce dont on a besoin, soit le total (le plus petit) - optimalTake = Math.min(optimalTake, totalCount); - } - - // Arrondir aux paliers optimaux - return this.roundToOptimalValue(optimalTake); - } - - // Arrondir à des valeurs optimales (100, 500, 1000, etc.) - private roundToOptimalValue(value: number): number { - if (value <= 100) return 100; - if (value <= 500) return 500; - if (value <= 1000) return 1000; - if (value <= 2000) return 2000; - if (value <= 5000) return 5000; - if (value <= 10000) return 10000; - - // Pour les très grands nombres, arrondir au multiple de 10000 supérieur - return Math.ceil(value / 10000) * 10000; - } - - fetchAllMerchants(params?: SearchMerchantsParams): Observable { - // Commencer avec un take raisonnable - const initialTake = 500; - - return this.fetchMerchantsWithParams(initialTake, params).pipe( - switchMap(initialBatch => { - // Si nous avons récupéré moins que demandé, c'est probablement tout - if (initialBatch.length < initialTake) { - console.log(`✅ Retrieved all ${initialBatch.length} merchants`); - return of(initialBatch); - } - - // Sinon, peut-être qu'il y a plus, essayer un take plus grand - console.log(`⚠️ Initial batch size (${initialBatch.length}) equals take, might be more`); - - const largerTake = 2000; - return this.fetchMerchantsWithParams(largerTake, params).pipe( - map(largerBatch => { - console.log(`✅ Retrieved ${largerBatch.length} merchants with larger take`); - return largerBatch; - }) - ); - }) - ); - } - - private fetchMerchantsWithParams(take: number, params?: SearchMerchantsParams): Observable { - let httpParams = new HttpParams().set('take', take.toString()); - - if (params?.query) { - httpParams = httpParams.set('query', params.query.trim()); - } - - console.log(`📥 Fetching ${take} merchants`); - - return this.http.get(this.baseApiUrl, { - params: httpParams - }).pipe( - timeout(this.REQUEST_TIMEOUT), - map(apiMerchants => - apiMerchants.map(merchant => - this.dataAdapter.convertApiMerchantToFrontend(merchant) - ) - ) - ); - } - - private fetchAdditionalMerchants(take: number, params?: SearchMerchantsParams): Observable { - // Prendre à partir de la fin du cache - const skip = this.merchantsCache.length; let httpParams = new HttpParams() - .set('take', take.toString()) - .set('skip', skip.toString()); - + .set('skip', skip.toString()) + .set('take', limit.toString()); + if (params?.query) { httpParams = httpParams.set('query', params.query.trim()); } - - console.log(`📥 Fetching additional ${take} merchants (skip: ${skip})`); - - return this.http.get(this.baseApiUrl, { - params: httpParams - }).pipe( - timeout(this.REQUEST_TIMEOUT), - map(apiMerchants => - apiMerchants.map(merchant => - this.dataAdapter.convertApiMerchantToFrontend(merchant) - ) - ) - ); - } - private mergeMerchants(existing: Merchant[], newOnes: Merchant[]): Merchant[] { - const existingIds = new Set(existing.map(m => m.id)); - const uniqueNewOnes = newOnes.filter(m => !existingIds.has(m.id)); - return [...existing, ...uniqueNewOnes]; - } + return this.http.get<{ items: ApiMerchant[], total: number }>(this.baseApiUrl, { params: httpParams }) + .pipe( + timeout(this.REQUEST_TIMEOUT), + map(response => { + // Sécuriser le mapping, même si items est undefined + const itemsArray: ApiMerchant[] = Array.isArray(response?.items) ? response.items : []; + const merchants: Merchant[] = itemsArray.map(m => this.dataAdapter.convertApiMerchantToFrontend(m)); - private applyPagination(merchants: Merchant[], page: number, limit: number): PaginatedResponse { - const total = merchants.length; - const totalPages = Math.ceil(total / limit); - - const startIndex = (page - 1) * limit; - const endIndex = Math.min(startIndex + limit, total); - const paginatedItems = merchants.slice(startIndex, endIndex); - - return { - items: paginatedItems, - total: total, - page: page, - limit: limit, - totalPages: totalPages - }; - } + const total: number = typeof response?.total === 'number' ? response.total : merchants.length; - private areParamsEqual(a?: SearchMerchantsParams, b?: SearchMerchantsParams): boolean { - if (!a && !b) return true; - if (!a || !b) return false; - return a.query === b.query; - } - - getAllMerchants(params?: SearchMerchantsParams): Observable { - let httpParams = new HttpParams(); - - if (params?.query) { - httpParams = httpParams.set('query', params.query.trim()); - } - - return this.http.get(this.baseApiUrl, { params: httpParams }).pipe( - timeout(this.REQUEST_TIMEOUT), - map(apiMerchants => - apiMerchants.map(merchant => - this.dataAdapter.convertApiMerchantToFrontend(merchant) - ) - ), - catchError(error => this.handleError('getAllMerchants', error)) - ); + return { + items: merchants, + total, + page, + limit, + totalPages: Math.ceil(total / limit) + }; + }), + catchError(error => this.handleError('getAllMerchants', error)) + ); } getMerchantById(userId: number): Observable { diff --git a/src/app/modules/merchant-config/merchant-config.ts b/src/app/modules/merchant-config/merchant-config.ts index 48bdae9..15ce4c1 100644 --- a/src/app/modules/merchant-config/merchant-config.ts +++ b/src/app/modules/merchant-config/merchant-config.ts @@ -9,7 +9,6 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { MerchantConfigService } from './merchant-config.service'; import { RoleManagementService } from '@core/services/hub-users-roles-management.service'; import { AuthService } from '@core/services/auth.service'; -import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service'; import { PageTitle } from '@app/components/page-title/page-title'; import { MerchantConfigsList } from './merchant-config-list/merchant-config-list'; import { MerchantConfigView } from './merchant-config-view/merchant-config-view'; @@ -26,7 +25,7 @@ import { MerchantConfig, TechnicalContact } from '@core/models/merchant-config.model'; -import { UserRole, UserType, PaginatedUserResponse, User } from '@core/models/dcb-bo-hub-user.model'; +import { UserRole, UserType, User } from '@core/models/dcb-bo-hub-user.model'; import { MerchantDataAdapter } from './merchant-data-adapter.service'; diff --git a/src/app/modules/modules.routes.ts b/src/app/modules/modules.routes.ts index 32d1d1c..31ed9c9 100644 --- a/src/app/modules/modules.routes.ts +++ b/src/app/modules/modules.routes.ts @@ -16,7 +16,6 @@ import { WebhooksStatus } from '@modules/webhooks/status/status'; import { WebhooksRetry } from '@modules/webhooks/retry/retry'; import { Settings } from '@modules/settings/settings'; import { Integrations } from '@modules/integrations/integrations'; -import { Support } from '@modules/support/support'; import { MyProfile } from '@modules/profile/profile'; import { Documentation } from '@modules/documentation/documentation'; import { Help } from '@modules/help/help'; @@ -233,17 +232,8 @@ const routes: Routes = [ }, // --------------------------- - // Support & Profile (Tous les utilisateurs authentifiés) + // Profile (Tous les utilisateurs authentifiés) // --------------------------- - { - path: 'support', - component: Support, - canActivate: [authGuard, roleGuard], - data: { - title: 'Support', - module: 'support' - } - }, { path: 'profile', component: MyProfile, diff --git a/src/app/modules/support/support.ts b/src/app/modules/support/support.ts deleted file mode 100644 index 1f61904..0000000 --- a/src/app/modules/support/support.ts +++ /dev/null @@ -1,628 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service'; -import { UserRole } from '@core/models/dcb-bo-hub-user.model'; -import { firstValueFrom } from 'rxjs'; -import { MerchantUsersService } from '@modules/hub-users-management/merchant-users.service'; - -@Component({ - selector: 'app-support', - templateUrl: './support.html' -}) -export class Support implements OnInit { - - private testData = { - merchantConfigId: '', - keycloakUserId: '', - testUserId: '', - testMerchantConfigUserId: '', - associatedUserId: '' // ID d'un utilisateur associé au marchand - }; - - constructor( - private merchantCrudService: MerchantSyncService, - private merchantUsersService: MerchantUsersService - ) {} - - ngOnInit() { - console.log('🚀 Support Component - Tests CRUD Merchants'); - console.log('='.repeat(60)); - console.log('📌 NOUVELLE LOGIQUE: Merchant et User indépendants, association séparée'); - console.log('='.repeat(60)); - - // Démarrer les tests automatiquement - this.runAllTests(); - } - - /** - * Exécuter tous les tests - */ - async runAllTests(): Promise { - try { - console.log('\n🧪 DÉMARRAGE DES TESTS COMPLETS'); - console.log('📌 NOUVELLE LOGIQUE: Association séparée'); - console.log('='.repeat(50)); - - // Test 1: Création indépendante - await this.testCreateOperations(); - - // Test 2: Association - await this.testAssociationOperations(); - - // Test 3: Lecture - await this.testReadOperations(); - - // Test 4: Mise à jour - await this.testUpdateOperations(); - - // Test 5: Gestion utilisateurs - await this.testUserOperations(); - - // Test 6: Suppression - await this.testDeleteOperations(); - - console.log('\n✅ TOUS LES TESTS TERMINÉS AVEC SUCCÈS!'); - console.log('='.repeat(50)); - - } catch (error) { - console.error('❌ ERREUR CRITIQUE DANS LES TESTS:', error); - } - } - - /** - * TEST 1: Opérations de création indépendante - */ - private async testCreateOperations(): Promise { - console.log('\n📝 TEST 1: Opérations CREATE INDÉPENDANTES'); - console.log('-'.repeat(40)); - - try { - // 1.1 Créer un merchant uniquement dans MerchantConfig - console.log('1.1 Création d\'un merchant dans MerchantConfig seulement...'); - - const merchantData = { - name: 'Test Merchant ' + Date.now(), - adresse: '123 Test Street', - phone: '+336' + Math.floor(10000000 + Math.random() * 90000000), - configs: [ - { - name: 'API_KEY', - value: 'test-api-key-' + Date.now(), - operatorId: 1 - } - ], - technicalContacts: [ - { - firstName: 'Test', - lastName: 'User', - phone: '+33612345678', - email: `test.${Date.now()}@example.com` - } - ] - }; - - console.log('📤 Données merchant pour MerchantConfig:', merchantData); - - const merchantConfigResult = await firstValueFrom( - this.merchantCrudService.createMerchantInConfigOnly(merchantData) - ); - - this.testData.merchantConfigId = String(merchantConfigResult.id!); - - console.log('✅ Merchant créé dans MerchantConfig uniquement!'); - console.log(' Merchant Config ID:', this.testData.merchantConfigId); - console.log(' Merchant name:', merchantConfigResult.name); - - // 1.2 Créer un utilisateur Hub dans Keycloak - console.log('\n1.2 Création d\'un utilisateur Hub dans Keycloak...'); - - const hubUserData = { - username: `testhubuser.${Date.now()}`, - email: `hubuser.${Date.now()}@example.com`, - password: 'HubPassword123!', - firstName: 'Test', - lastName: 'HubUser', - role: UserRole.DCB_SUPPORT - }; - - console.log('👤 Données Hub User pour Keycloak:', { ...hubUserData, password: '***' }); - - const hubUserResult = await firstValueFrom( - this.merchantCrudService.createKeycloakUser(hubUserData) - ); - - console.log('✅ Utilisateur Hub créé dans Keycloak:'); - console.log(' Keycloak User ID:', hubUserResult.id); - console.log(' Email:', hubUserResult.email); - console.log(' Rôle:', hubUserResult.role); - console.log(' User Type:', hubUserResult.userType); - - // 1.3 Créer un utilisateur Partenaire dans Keycloak - console.log('\n1.3 Création d\'un utilisateur Partenaire dans Keycloak...'); - - const partnerUserData = { - username: `testpartner.${Date.now()}`, - email: `partner.${Date.now()}@example.com`, - password: 'PartnerPassword123!', - firstName: 'Test', - lastName: 'Partner', - role: UserRole.DCB_PARTNER_ADMIN - }; - - console.log('👤 Données Partner User pour Keycloak:', { ...partnerUserData, password: '***' }); - - const partnerUserResult = await firstValueFrom( - this.merchantCrudService.createKeycloakUser(partnerUserData) - ); - - this.testData.testUserId = partnerUserResult.id; - - console.log('✅ Utilisateur Partenaire créé dans Keycloak:'); - console.log(' Keycloak User ID:', this.testData.testUserId); - console.log(' Email:', partnerUserResult.email); - console.log(' Rôle:', partnerUserResult.role); - console.log(' User Type:', partnerUserResult.userType); - - } catch (error) { - console.error('❌ ERREUR lors de la création:', error); - throw error; - } - } - - /** - * TEST 2: Opérations d'association - */ - private async testAssociationOperations(): Promise { - console.log('\n🔗 TEST 2: Opérations d\'ASSOCIATION'); - console.log('-'.repeat(40)); - - if (!this.testData.merchantConfigId || !this.testData.testUserId) { - console.log('⚠️ Merchant ou utilisateur non créé, passage au test suivant'); - return; - } - - try { - // 2.1 Associer l'utilisateur au marchand - console.log('2.1 Association de l\'utilisateur au marchand...'); - - const associationData = { - userId: this.testData.testUserId, - merchantConfigId: this.testData.merchantConfigId, - role: UserRole.MERCHANT_CONFIG_ADMIN - }; - - console.log('🔗 Données d\'association:', associationData); - - const associationResult = await firstValueFrom( - this.merchantCrudService.associateUserToMerchant(associationData) - ); - - this.testData.associatedUserId = associationResult.keycloakUser.id; - this.testData.testMerchantConfigUserId = String(associationResult.merchantConfigUser?.userId || ''); - - console.log('✅ Utilisateur associé au marchand:'); - console.log(' Keycloak User ID:', associationResult.keycloakUser.id); - console.log(' Merchant Config User ID:', this.testData.testMerchantConfigUserId); - console.log(' Rôle dans MerchantConfig:', associationResult.merchantConfigUser?.role); - console.log(' Associé au merchant:', this.testData.merchantConfigId); - - // 2.2 Récupérer les utilisateurs associés au marchand - console.log('\n2.2 Lecture des utilisateurs associés au marchand...'); - - const merchantUsers = await firstValueFrom( - this.merchantCrudService.getUsersByMerchant(this.testData.merchantConfigId) - ); - - console.log(`✅ ${merchantUsers.length} utilisateurs associés à ce merchant`); - - merchantUsers.forEach((user: any, index: number) => { - console.log(` ${index + 1}. ${user.email || 'Inconnu'}`); - console.log(` ID: ${user.id}`); - console.log(` Rôle: ${user.role}`); - }); - - } catch (error) { - console.error('❌ ERREUR lors de l\'association:', error); - } - } - - /** - * TEST 3: Opérations de lecture - */ - private async testReadOperations(): Promise { - console.log('\n🔍 TEST 3: Opérations READ'); - console.log('-'.repeat(40)); - - if (!this.testData.merchantConfigId) { - console.log('⚠️ Aucun merchant créé, passage au test suivant'); - return; - } - - try { - // 3.1 Lire le merchant depuis MerchantConfig - console.log('3.1 Lecture du merchant depuis MerchantConfig...'); - - const merchant = await firstValueFrom( - this.merchantCrudService.getMerchantFromConfigOnly( - this.testData.merchantConfigId - ) - ); - - console.log('✅ Merchant récupéré depuis MerchantConfig:'); - console.log(' ID:', merchant.id); - console.log(' Nom:', merchant.name); - console.log(' Adresse:', merchant.adresse); - console.log(' Configurations:', merchant.configs?.length || 0); - - // 3.2 Lire tous les merchants depuis MerchantConfig - console.log('\n3.2 Lecture de tous les merchants depuis MerchantConfig...'); - - const allMerchants = await firstValueFrom( - this.merchantCrudService.getAllMerchantsFromConfig() - ); - - console.log(`✅ ${allMerchants.length} merchants trouvés au total`); - - if (allMerchants.length > 0) { - const lastMerchant = allMerchants[allMerchants.length - 1]; - console.log(' Dernier merchant:', lastMerchant.name, '(ID:', lastMerchant.id, ')'); - } - - // 3.3 Rechercher des merchants - console.log('\n3.3 Recherche de merchants dans MerchantConfig...'); - - const searchResults = await firstValueFrom( - this.merchantCrudService.searchMerchantsInConfig('Test') - ); - - console.log(`✅ ${searchResults.length} merchants trouvés avec "Test"`); - - if (searchResults.length > 0) { - console.log(' Premier résultat:', searchResults[0].name); - } - - // 3.4 Rechercher des utilisateurs - console.log('\n3.4 Recherche d\'utilisateurs dans Keycloak...'); - - const userSearchResults = await firstValueFrom( - this.merchantCrudService.searchKeycloakUsers('test') - ); - - console.log(`✅ ${userSearchResults.length} utilisateurs trouvés avec "test"`); - - if (userSearchResults.length > 0) { - console.log(' Premier résultat:', userSearchResults[0].email); - } - - } catch (error) { - console.error('❌ ERREUR lors de la lecture:', error); - } - } - - /** - * TEST 4: Opérations de mise à jour - */ - private async testUpdateOperations(): Promise { - console.log('\n✏️ TEST 4: Opérations UPDATE'); - console.log('-'.repeat(40)); - - if (!this.testData.merchantConfigId) { - console.log('⚠️ Aucun merchant créé, passage au test suivant'); - return; - } - - try { - // 4.1 Mettre à jour le merchant dans MerchantConfig - console.log('4.1 Mise à jour du merchant dans MerchantConfig...'); - - const newName = `Updated Merchant ${Date.now()}`; - - const updateResult = await firstValueFrom( - this.merchantCrudService.updateMerchantInConfigOnly( - this.testData.merchantConfigId, - { - name: newName, - description: 'Mis à jour par les tests', - adresse: '456 Updated Street' - } - ) - ); - - console.log('✅ Merchant mis à jour dans MerchantConfig:'); - console.log(' Nouveau nom:', newName); - console.log(' Description:', updateResult.description); - - // 4.3 Changer le rôle de l'utilisateur dans MerchantConfig - console.log('\n4.3 Changement de rôle utilisateur dans MerchantConfig...'); - - if (this.testData.testUserId && this.testData.merchantConfigId && this.testData.testMerchantConfigUserId) { - const roleUpdateResult = await firstValueFrom( - this.merchantCrudService.updateUserRoleInMerchantConfig( - this.testData.merchantConfigId, - this.testData.testUserId, - UserRole.MERCHANT_CONFIG_MANAGER - ) - ); - - console.log('✅ Rôle utilisateur mis à jour dans MerchantConfig:'); - console.log(' Nouveau rôle:', UserRole.MERCHANT_CONFIG_MANAGER); - } - - } catch (error) { - console.error('❌ ERREUR lors de la mise à jour:', error); - } - } - - /** - * TEST 5: Opérations utilisateurs avancées - */ - private async testUserOperations(): Promise { - console.log('\n👥 TEST 5: Opérations utilisateurs avancées'); - console.log('-'.repeat(40)); - - if (!this.testData.testUserId) { - console.log('⚠️ Aucun utilisateur créé, passage au test suivant'); - return; - } - - try { - // 5.1 Réinitialiser le mot de passe - console.log('5.1 Réinitialisation mot de passe...'); - - const resetPasswordDto = { - newPassword: 'NewPassword123!', - temporary: false - }; - - const resetResult = await firstValueFrom( - this.merchantUsersService.resetMerchantUserPassword( - this.testData.testUserId, - resetPasswordDto - ) - ); - - console.log('✅ Mot de passe réinitialisé:'); - console.log(' Message:', resetResult.message); - - // 5.2 Dissocier l'utilisateur du marchand - console.log('\n5.2 Dissociation de l\'utilisateur du marchand...'); - - if (this.testData.testUserId && this.testData.merchantConfigId) { - const dissociateResult = await firstValueFrom( - this.merchantCrudService.dissociateUserFromMerchant( - this.testData.testUserId, - this.testData.merchantConfigId - ) - ); - - console.log('✅ Utilisateur dissocié du marchand:'); - console.log(' Succès:', dissociateResult.success); - console.log(' Message:', dissociateResult.message); - - // Vérifier que l'utilisateur n'est plus associé - const userMerchants = await firstValueFrom( - this.merchantCrudService.getUsersByMerchant(this.testData.merchantConfigId) - ); - - const userStillLinked = userMerchants.some( - user => user.id === this.testData.testUserId - ); - - console.log(` Marchands associés après dissociation: ${userMerchants.length}`); - - if (userStillLinked) { - console.error('❌ L’utilisateur est encore associé au marchand ! ❌'); - } else { - console.log('✅ L’utilisateur a été correctement dissocié du marchand.'); - } - - } - - // 5.3 Réassocier l'utilisateur (pour les tests suivants) - console.log('\n5.3 Réassociation de l\'utilisateur (pour les tests)...'); - - if (this.testData.testUserId && this.testData.merchantConfigId) { - const reassociationData = { - userId: this.testData.testUserId, - merchantConfigId: this.testData.merchantConfigId, - role: UserRole.MERCHANT_CONFIG_ADMIN - }; - - await firstValueFrom( - this.merchantCrudService.associateUserToMerchant(reassociationData) - ); - console.log('✅ Utilisateur réassocié pour les tests suivants'); - } - - } catch (error) { - console.error('❌ ERREUR opérations utilisateurs:', error); - } - } - - /** - * TEST 6: Opérations de suppression - */ - private async testDeleteOperations(): Promise { - console.log('\n🗑️ TEST 6: Opérations DELETE'); - console.log('-'.repeat(40)); - - if (!this.testData.merchantConfigId) { - console.log('⚠️ Aucun merchant créé, passage au test suivant'); - return; - } - - try { - // 6.1 Dissocier avant suppression - console.log('6.1 Dissociation de l\'utilisateur avant suppression...'); - - if (this.testData.testUserId && this.testData.merchantConfigId) { - await firstValueFrom( - this.merchantCrudService.dissociateUserFromMerchant( - this.testData.testUserId, - this.testData.merchantConfigId - ) - ); - console.log('✅ Utilisateur dissocié'); - } - - // 6.2 Supprimer l'utilisateur de Keycloak - console.log('\n6.2 Suppression de l\'utilisateur de Keycloak...'); - - if (this.testData.testUserId) { - const deleteUserResult = await firstValueFrom( - this.merchantCrudService.deleteKeycloakUser( - this.testData.testUserId - ) - ); - - console.log('✅ Utilisateur supprimé de Keycloak:'); - console.log(' Succès:', deleteUserResult.success); - console.log(' Message:', deleteUserResult.message); - - this.testData.testUserId = ''; - this.testData.testMerchantConfigUserId = ''; - } - - // 6.3 Supprimer le merchant de MerchantConfig - console.log('\n6.3 Suppression du merchant de MerchantConfig...'); - - const deleteMerchantResult = await firstValueFrom( - this.merchantCrudService.deleteMerchantFromConfigOnly( - this.testData.merchantConfigId - ) - ); - - console.log('✅ Merchant supprimé de MerchantConfig:'); - console.log(' Succès:', deleteMerchantResult.success); - console.log(' Message:', deleteMerchantResult.message); - - // 6.4 Vérifier la suppression - console.log('\n6.4 Vérification de la suppression...'); - - try { - await firstValueFrom( - this.merchantCrudService.getMerchantFromConfigOnly( - this.testData.merchantConfigId - ) - ); - console.log('❌ Le merchant existe toujours dans MerchantConfig - PROBLÈME!'); - } catch (error) { - console.log('✅ Le merchant a bien été supprimé de MerchantConfig'); - } - - // Réinitialiser toutes les données - this.testData = { - merchantConfigId: '', - keycloakUserId: '', - testUserId: '', - testMerchantConfigUserId: '', - associatedUserId: '' - }; - - console.log('🧹 Données de test réinitialisées'); - - } catch (error) { - console.error('❌ ERREUR lors de la suppression:', error); - } - } - - // ==================== MÉTHODES POUR TESTS INDIVIDUELS ==================== - - async testCreateOnly(): Promise { - console.log('🧪 Test CREATE uniquement'); - console.log('📌 Création indépendante'); - await this.testCreateOperations(); - } - - async testAssociationOnly(): Promise { - console.log('🧪 Test ASSOCIATION uniquement'); - - // Créer d'abord un merchant et un utilisateur si nécessaire - if (!this.testData.merchantConfigId) { - await this.createTestMerchant(); - } - if (!this.testData.testUserId) { - await this.createTestUser(); - } - - await this.testAssociationOperations(); - } - - async testReadOnly(): Promise { - console.log('🧪 Test READ uniquement'); - await this.testReadOperations(); - } - - async testUpdateOnly(): Promise { - console.log('🧪 Test UPDATE uniquement'); - await this.testUpdateOperations(); - } - - async testDeleteOnly(): Promise { - console.log('🧪 Test DELETE uniquement'); - await this.testDeleteOperations(); - } - - // ==================== MÉTHODES UTILITAIRES ==================== - - private async createTestMerchant(): Promise { - const merchantData = { - name: 'Test Merchant ' + Date.now(), - adresse: '123 Test Street', - phone: '+336' + Math.floor(10000000 + Math.random() * 90000000), - configs: [], - technicalContacts: [] - }; - - const merchant = await firstValueFrom( - this.merchantCrudService.createMerchantInConfigOnly(merchantData) - ); - - this.testData.merchantConfigId = String(merchant.id!); - console.log('✅ Merchant de test créé:', this.testData.merchantConfigId); - } - - private async createTestUser(): Promise { - const userData = { - username: `testuser.${Date.now()}`, - email: `user.${Date.now()}@example.com`, - password: 'TestPassword123!', - firstName: 'Test', - lastName: 'User', - role: UserRole.DCB_PARTNER_ADMIN - }; - - const user = await firstValueFrom( - this.merchantCrudService.createKeycloakUser(userData) - ); - - this.testData.testUserId = user.id; - console.log('✅ Utilisateur de test créé:', this.testData.testUserId); - } - - /** - * Afficher l'état actuel des tests - */ - showTestStatus(): void { - console.log('\n📊 ÉTAT ACTUEL DES TESTS'); - console.log('📌 NOUVELLE LOGIQUE: Association séparée'); - console.log('-'.repeat(30)); - console.log('Merchant Config ID:', this.testData.merchantConfigId || 'Non créé'); - console.log('Keycloak User ID:', this.testData.testUserId || 'Non créé'); - console.log('Associated User ID:', this.testData.associatedUserId || 'Non associé'); - console.log('Merchant Config User ID:', this.testData.testMerchantConfigUserId || 'Non créé'); - } - - /** - * Réinitialiser les données de test - */ - resetTestData(): void { - this.testData = { - merchantConfigId: '', - keycloakUserId: '', - testUserId: '', - testMerchantConfigUserId: '', - associatedUserId: '' - }; - console.log('🧹 Données de test réinitialisées'); - } -} \ No newline at end of file