diff --git a/src/app/core/directive/has-role.directive.ts b/src/app/core/directive/has-role.directive.ts index fb4bc8d..b508a7d 100644 --- a/src/app/core/directive/has-role.directive.ts +++ b/src/app/core/directive/has-role.directive.ts @@ -18,7 +18,7 @@ export class HasRoleDirective implements OnDestroy { const userRoles = this.authService.getCurrentUserRoles(); const hasAccess = requiredRoles.some(role => userRoles.includes( - UserRole.DCB_ADMIN || UserRole.DCB_PARTNER || UserRole.DCB_SUPPORT + UserRole.DCB_ADMIN || UserRole.DCB_SUPPORT || UserRole.DCB_PARTNER_ADMIN || UserRole.DCB_PARTNER_MANAGER || UserRole.DCB_PARTNER_SUPPORT )); diff --git a/src/app/core/models/dcb-bo-hub-user.model.ts b/src/app/core/models/dcb-bo-hub-user.model.ts index 86195ba..f5fd487 100644 --- a/src/app/core/models/dcb-bo-hub-user.model.ts +++ b/src/app/core/models/dcb-bo-hub-user.model.ts @@ -5,12 +5,11 @@ export enum UserType { } export enum UserRole { - // Rôles Hub (sans merchantPartnerId) + // Rôles Hub DCB_ADMIN = 'dcb-admin', DCB_SUPPORT = 'dcb-support', - DCB_PARTNER = 'dcb-partner', // Propriétaire de merchants - // Rôles Merchant Partner (avec merchantPartnerId obligatoire = ID du DCB_PARTNER) + // Rôles Merchant User DCB_PARTNER_ADMIN = 'dcb-partner-admin', DCB_PARTNER_MANAGER = 'dcb-partner-manager', DCB_PARTNER_SUPPORT = 'dcb-partner-support', @@ -58,12 +57,8 @@ export interface User { emailVerified: boolean; userType: UserType; // HUB ou MERCHANT_PARTNER - // C'est l'ID du "merchant" (DCB_PARTNER) qui est propriétaire - merchantPartnerId?: string; // Référence à l'ID Keycloak du DCB_PARTNER - role: UserRole; - // Merchant Config - Stocker l'ID du merchant dans l'autre système - merchantConfigId?: string; // ID INT dans Merchant Config pour CE merchant + createdBy?: string; createdByUsername?: string; createdTimestamp: number; @@ -89,8 +84,6 @@ export interface CreateUserDto { role: UserRole; enabled?: boolean; emailVerified?: boolean; - merchantPartnerId?: string; - merchantConfigId?: string; } export interface UpdateUserDto { @@ -149,8 +142,6 @@ export interface SearchUsersParams { role?: UserRole; enabled?: boolean; userType?: UserType; - merchantPartnerId?: string; - merchantConfigId?: string; page?: number; limit?: number; } @@ -161,7 +152,7 @@ export class UserUtils { return user.userType === UserType.HUB; } - static isMerchantPartnerUser(user: User): boolean { + static isMerchantUser(user: User): boolean { return user.userType === UserType.MERCHANT_PARTNER; } @@ -174,7 +165,7 @@ export class UserUtils { const roleNames = { [UserRole.DCB_ADMIN]: 'DCB Admin', [UserRole.DCB_SUPPORT]: 'DCB Support', - [UserRole.DCB_PARTNER]: 'DCB Partner', + [UserRole.DCB_PARTNER_ADMIN]: 'Partner Admin', [UserRole.DCB_PARTNER_MANAGER]: 'Partner Manager', [UserRole.DCB_PARTNER_SUPPORT]: 'Partner Support', @@ -209,21 +200,12 @@ export class UserUtils { static validateUserCreation(user: CreateUserDto): string[] { const errors: string[] = []; - // Validation merchantPartnerId - if (user.userType === UserType.MERCHANT_PARTNER && !user.merchantPartnerId) { - errors.push('merchantPartnerId est obligatoire pour les utilisateurs Merchant Partner'); - } - - if (user.userType === UserType.HUB && user.merchantPartnerId) { - errors.push('merchantPartnerId ne doit pas être défini pour les utilisateurs Hub'); - } - if (!user.role) { errors.push('Un rôle doit être assigné'); } // Validation cohérence rôle/type - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT] const merchantRoles = [UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT]; if (user.userType === UserType.HUB && user.role) { diff --git a/src/app/core/models/merchant-config.model.ts b/src/app/core/models/merchant-config.model.ts index 867396a..9c1e0c2 100644 --- a/src/app/core/models/merchant-config.model.ts +++ b/src/app/core/models/merchant-config.model.ts @@ -5,16 +5,16 @@ export enum UserType { } export enum UserRole { - // Rôles Hub (sans merchantPartnerId) + // Rôles Hub DCB_ADMIN = 'dcb-admin', DCB_SUPPORT = 'dcb-support', - DCB_PARTNER = 'dcb-partner', - // Rôles Merchant Partner (avec merchantPartnerId obligatoire) + // Rôles Merchant User DCB_PARTNER_ADMIN = 'dcb-partner-admin', DCB_PARTNER_MANAGER = 'dcb-partner-manager', DCB_PARTNER_SUPPORT = 'dcb-partner-support', + // Rôles de configuration Marchand (MerchantConfig) MERCHANT_CONFIG_ADMIN = 'ADMIN', MERCHANT_CONFIG_MANAGER = 'MANAGER', MERCHANT_CONFIG_TECHNICAL = 'TECHNICAL', @@ -59,12 +59,15 @@ export interface TechnicalContact { export interface MerchantUser { userId: string; - role: UserRole; + role: UserRole; username?: string; email?: string; firstName?: string; lastName?: string; merchantPartnerId?: number + merchantConfigId?: string; // Référence au merchant dans MerchantConfig + createdAt?: string; + updatedAt?: string; } export interface Merchant { @@ -111,6 +114,9 @@ export interface ApiMerchantUser { firstName?: string; lastName?: string; merchantPartnerId?: number; + merchantConfigId?: string; + createdAt?: string; + updatedAt?: string; } export interface ApiMerchant { @@ -147,6 +153,14 @@ export interface UpdateMerchantConfigDto { operatorId?: Operator | 1; } +// DTO mise à jour d'un contact technique +export interface UpdateTechnicalContactDto { + firstName?: string; + lastName?: string; + phone?: string; + email?: string; +} + export interface AddUserToMerchantDto { userId: string; role: UserRole; @@ -157,6 +171,17 @@ export interface UpdateUserRoleDto { role: UserRole; } +// DTO pour associer/dissocier un utilisateur +export interface AssociateUserToMerchantDto { + userId: string; + merchantConfigId: string; + role: UserRole; +} + +export interface DissociateUserFromMerchantDto { + userId: string; + merchantConfigId: string; +} // === RÉPONSES API === export interface ApiResponse { @@ -190,6 +215,15 @@ export interface SearchMerchantsParams { limit?: number; } +// === TYPES POUR GESTION DES RÔLES === + +export interface UserRoleInfo { + value: UserRole; + label: string; + description: string; + type: 'hub' | 'merchant' | 'config'; +} + // === UTILITAIRES === export class MerchantUtils { static getOperatorName(operatorId: Operator): string { @@ -229,23 +263,4 @@ export class MerchantUtils { return errors; } - - // Méthode pour obtenir les rôles disponibles pour les merchants - static getAvailableMerchantRoles(): UserRole[] { - return [ - UserRole.DCB_PARTNER_ADMIN, - UserRole.DCB_PARTNER_MANAGER, - UserRole.DCB_PARTNER_SUPPORT - ]; - } - - // Vérifier si un rôle est valide pour un merchant - static isValidMerchantRole(role: UserRole): boolean { - const merchantRoles = [ - UserRole.DCB_PARTNER_ADMIN, - UserRole.DCB_PARTNER_MANAGER, - UserRole.DCB_PARTNER_SUPPORT - ]; - return merchantRoles.includes(role); - } } \ No newline at end of file diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts index b4eb4cf..0ceb7d2 100644 --- a/src/app/core/services/auth.service.ts +++ b/src/app/core/services/auth.service.ts @@ -209,7 +209,7 @@ export class AuthService { private determineUserType(apiUser: any): UserType { const hubRoles = [UserRole.DCB_ADMIN || UserRole.DCB_SUPPORT]; - const merchantRoles = [UserRole.DCB_PARTNER || UserRole.DCB_PARTNER_ADMIN || UserRole.DCB_PARTNER_MANAGER || UserRole.DCB_PARTNER_SUPPORT]; + const merchantRoles = [UserRole.DCB_PARTNER_ADMIN || UserRole.DCB_PARTNER_MANAGER || UserRole.DCB_PARTNER_SUPPORT]; // Logique pour déterminer le type d'utilisateur if (apiUser.clientRoles?.[0].includes(merchantRoles)) { @@ -232,8 +232,6 @@ export class AuthService { enabled: apiUser.enabled ?? apiUser.active ?? true, emailVerified: apiUser.emailVerified ?? apiUser.email_verified ?? false, userType: userType, - merchantPartnerId: apiUser.merchantPartnerId || apiUser.partnerId || apiUser.merchantId || null, - merchantConfigId: apiUser.merchantConfigId || apiUser.configId || apiUser.merchantId || null, role: apiUser.clientRoles || apiUser.clientRoles?.[0] || '', // Gérer rôle unique ou tableau createdBy: apiUser.createdBy || apiUser.creatorId || null, createdByUsername: apiUser.createdByUsername || apiUser.creatorUsername || null, @@ -303,10 +301,6 @@ export class AuthService { 'support': UserRole.DCB_SUPPORT, 'dcb-support': UserRole.DCB_SUPPORT, - // Rôles partenaire - 'partner': UserRole.DCB_PARTNER, - 'dcb-partner': UserRole.DCB_PARTNER, - // Rôles admin partenaire 'partner-admin': UserRole.DCB_PARTNER_ADMIN, 'dcb-partner-admin': UserRole.DCB_PARTNER_ADMIN, @@ -363,7 +357,7 @@ export class AuthService { if (!role) return null; // Déterminer le type d'utilisateur basé sur le rôle - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT]; const merchantRoles = [UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT]; if (hubRoles.includes(role)) { @@ -434,7 +428,7 @@ export class AuthService { * Vérifie si l'utilisateur courant peut gérer les utilisateurs Marchands */ canManageMerchantUsers(): boolean { - return this.hasAnyRole(UserRole.DCB_ADMIN) || this.hasAnyRole(UserRole.DCB_PARTNER); + return this.hasAnyRole(UserRole.DCB_ADMIN) || this.hasAnyRole(UserRole.DCB_PARTNER_ADMIN); } // === MÉTHODES UTILITAIRES === @@ -455,14 +449,6 @@ export class AuthService { return profile?.id || null; } - /** - * Récupère le merchantPartnerId de l'utilisateur courant (si marchand) - */ - getCurrentMerchantPartnerId(): string | null { - const profile = this.userProfile$.value; - return profile?.merchantPartnerId || null; - } - /** * Vérifie si le profil fourni est celui de l'utilisateur courant */ @@ -475,7 +461,7 @@ export class AuthService { * Vérifie si l'utilisateur peut visualiser tous les marchands */ canViewAllMerchants(): boolean { - return this.hasAnyRole(UserRole.DCB_ADMIN) || this.hasAnyRole(UserRole.DCB_PARTNER); + return this.hasAnyRole(UserRole.DCB_ADMIN) || this.hasAnyRole(UserRole.DCB_PARTNER_ADMIN); } // === TOKENS === diff --git a/src/app/core/services/hub-users-roles-management.service.ts b/src/app/core/services/hub-users-roles-management.service.ts index 6e7c268..9059a89 100644 --- a/src/app/core/services/hub-users-roles-management.service.ts +++ b/src/app/core/services/hub-users-roles-management.service.ts @@ -81,29 +81,6 @@ const ROLE_CONFIG: Record = { canAccessPartner: true, assignableRoles: [ UserRole.DCB_SUPPORT, - UserRole.DCB_PARTNER, - UserRole.DCB_PARTNER_ADMIN, - UserRole.DCB_PARTNER_MANAGER, - UserRole.DCB_PARTNER_SUPPORT - ] - } - }, - [UserRole.DCB_PARTNER]: { - label: 'Partenaire DCB', - description: 'Partenaire commercial principal', - badgeClass: 'bg-primary', - icon: 'lucideBuilding', - permissions: { - canCreateUsers: true, - canEditUsers: true, - canDeleteUsers: true, - canManageRoles: true, - canViewStats: true, - canManageMerchants: false, - canAccessAdmin: false, - canAccessSupport: false, - canAccessPartner: false, - assignableRoles: [ UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT @@ -198,7 +175,6 @@ const ROLE_CONFIG: Record = { const HUB_ROLES = [ UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, - UserRole.DCB_PARTNER ] as const; // Rôles Marchands (pour les filtres) @@ -303,8 +279,7 @@ export class RoleManagementService { const fullPermissionRoles = [ UserRole.DCB_ADMIN, - UserRole.DCB_SUPPORT, - UserRole.DCB_PARTNER + UserRole.DCB_SUPPORT ]; if (fullPermissionRoles.includes(currentUserRole)) { @@ -398,10 +373,6 @@ export class RoleManagementService { return role === UserRole.DCB_SUPPORT; } - isPartnerRole(role: UserRole): boolean { - return role === UserRole.DCB_PARTNER; - } - isMerchantUserRole(role: UserRole): boolean { return role === UserRole.DCB_PARTNER_ADMIN || role === UserRole.DCB_PARTNER_MANAGER diff --git a/src/app/core/services/permissions.service.ts b/src/app/core/services/permissions.service.ts index aca31c8..626a9f0 100644 --- a/src/app/core/services/permissions.service.ts +++ b/src/app/core/services/permissions.service.ts @@ -45,10 +45,10 @@ export class PermissionsService { // Webhooks - Admin et Partner { module: 'webhooks', - roles: [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER], + roles: [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER_ADMIN], children: { - 'history': [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER], - 'status': [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER], + 'history': [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER_ADMIN], + 'status': [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER_ADMIN], 'retry': [UserRole.DCB_ADMIN] } }, diff --git a/src/app/layouts/components/sidenav/components/user-profile/user-profile.component.ts b/src/app/layouts/components/sidenav/components/user-profile/user-profile.component.ts index ad723ed..83d4a02 100644 --- a/src/app/layouts/components/sidenav/components/user-profile/user-profile.component.ts +++ b/src/app/layouts/components/sidenav/components/user-profile/user-profile.component.ts @@ -85,7 +85,6 @@ export class UserProfileComponent implements OnInit, OnDestroy { const roleDisplayNames: { [key in UserRole]: string } = { [UserRole.DCB_ADMIN]: 'Administrateur système avec tous les accès', [UserRole.DCB_SUPPORT]: 'Support technique avec accès étendus', - [UserRole.DCB_PARTNER]: 'Partenaire commercial principal', [UserRole.DCB_PARTNER_ADMIN]: 'Administrateur de partenaire marchand', [UserRole.DCB_PARTNER_MANAGER]: 'Manager opérationnel partenaire', [UserRole.DCB_PARTNER_SUPPORT]: 'Support technique partenaire', diff --git a/src/app/modules/hub-users-management/hub-users-list/hub-users-list.html b/src/app/modules/hub-users-management/hub-users-list/hub-users-list.html index e89f47e..5e128e2 100644 --- a/src/app/modules/hub-users-management/hub-users-list/hub-users-list.html +++ b/src/app/modules/hub-users-management/hub-users-list/hub-users-list.html @@ -66,14 +66,6 @@ > DCB Support ({{ getUsersCountByRole(UserRole.DCB_SUPPORT) }}) - - } @@ -460,11 +444,6 @@ Support : {{ getUsersCountByRole(UserRole.DCB_SUPPORT) }} -
- - Partenaires : {{ getUsersCountByRole(UserRole.DCB_PARTNER) }} - -
} diff --git a/src/app/modules/hub-users-management/hub-users-list/hub-users-list.ts b/src/app/modules/hub-users-management/hub-users-list/hub-users-list.ts index d0918f3..0efaaa7 100644 --- a/src/app/modules/hub-users-management/hub-users-list/hub-users-list.ts +++ b/src/app/modules/hub-users-management/hub-users-list/hub-users-list.ts @@ -161,7 +161,7 @@ export class HubUsersList implements OnInit, OnDestroy { { value: 'all', label: 'Tous les rôles', description: 'Tous les Roles' }, { value: UserRole.DCB_ADMIN, label: 'DCB Admin', description: 'Administrateur système' }, { value: UserRole.DCB_SUPPORT, label: 'DCB Support', description: 'Support technique' }, - { value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' }, + //{ value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' }, ]; } diff --git a/src/app/modules/hub-users-management/hub-users-profile/hub-users-profile.ts b/src/app/modules/hub-users-management/hub-users-profile/hub-users-profile.ts index c9d6633..de3baf2 100644 --- a/src/app/modules/hub-users-management/hub-users-profile/hub-users-profile.ts +++ b/src/app/modules/hub-users-management/hub-users-profile/hub-users-profile.ts @@ -124,7 +124,7 @@ export class HubUserProfile implements OnInit, OnDestroy { this.availableRoles = [ { value: UserRole.DCB_ADMIN, label: 'DCB Admin', description: 'Administrateur système' }, { value: UserRole.DCB_SUPPORT, label: 'DCB Support', description: 'Support technique' }, - { value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' } + //{ value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' } ]; this.cdRef.detectChanges(); } @@ -480,7 +480,7 @@ export class HubUserProfile implements OnInit, OnDestroy { } private isValidHubRole(role: UserRole): boolean { - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT]; return hubRoles.includes(role); } @@ -526,7 +526,7 @@ export class HubUserProfile implements OnInit, OnDestroy { } getAssignableRoles(): UserRole[] { - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT]; return hubRoles.filter(role => this.roleService.canAssignRole(this.currentUserRole, role)); } diff --git a/src/app/modules/hub-users-management/hub-users.service.ts b/src/app/modules/hub-users-management/hub-users.service.ts index 50e178d..b869c07 100644 --- a/src/app/modules/hub-users-management/hub-users.service.ts +++ b/src/app/modules/hub-users-management/hub-users.service.ts @@ -39,7 +39,6 @@ export interface UserProfileResponse { emailVerified: boolean; enabled: boolean; role: string[]; - merchantPartnerId?: string; createdBy?: string; createdByUsername?: string; } @@ -48,19 +47,10 @@ export interface MessageResponse { message: string; } -export interface MerchantPartnerIdResponse { - merchantPartnerId: string | null; -} - -export interface MerchantConfigIdResponse { - merchantPartnerId: number; -} - @Injectable({ providedIn: 'root' }) export class HubUsersService { private http = inject(HttpClient); private baseIamApiUrl = `${environment.iamApiUrl}/hub-users`; - private baseConfigApiUrl = `${environment.configApiUrl}/merchants`; // === MÉTHODES SPÉCIFIQUES HUB === @@ -123,19 +113,6 @@ export class HubUsersService { return throwError(() => 'Password must be at least 8 characters'); } - if (createUserDto.role === UserRole.DCB_PARTNER && !createUserDto.merchantConfigId) { - return throwError(() => 'Merchant Config is required and cannot be empty'); - } - - if (createUserDto.userType === UserType.MERCHANT_PARTNER && !createUserDto.merchantConfigId) { - return throwError(() => 'Merchant Config is required and cannot be empty'); - } - - // Avant de créer le payload, valider les données - if (createUserDto.userType === UserType.MERCHANT_PARTNER && !createUserDto.merchantPartnerId) { - return throwError(() => 'merchantPartnerId is required for merchant users'); - } - const payload = { username: createUserDto.username.trim(), email: createUserDto.email.trim().toLowerCase(), // Normaliser l'email @@ -145,16 +122,9 @@ export class HubUsersService { role: createUserDto.role, enabled: createUserDto.enabled ?? true, emailVerified: createUserDto.emailVerified ?? true, - merchantPartnerId: createUserDto.merchantPartnerId, - merchantConfigId: createUserDto.merchantConfigId, userType: createUserDto.userType.trim() }; - // Validation supplémentaire - if (payload.userType === UserType.HUB && payload.merchantPartnerId) { - return throwError(() => 'merchantPartnerId should not be provided for hub users'); - } - console.log(payload) return this.http.post(`${this.baseIamApiUrl}`, payload).pipe( @@ -179,19 +149,6 @@ export class HubUsersService { ); } - getAllDcbPartners(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable { - return this.http.get(`${this.baseIamApiUrl}/partners/dcb-partners`).pipe( - map(users => { - const mappedUsers = users.map(user => this.mapToUserModel(user, UserType.HUB)); - return this.filterAndPaginateUsers(mappedUsers, page, limit, filters); - }), - catchError(error => { - console.error('Error loading merchant hub users:', error); - return throwError(() => error); - }) - ); - } - getHubUserById(id: string): Observable { return this.http.get(`${this.baseIamApiUrl}/${id}`).pipe( map(user => this.mapToUserModel(user, UserType.HUB)), @@ -220,7 +177,7 @@ export class HubUsersService { } updateHubUserRole(id: string, role: UserRole): Observable { - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT]; if (!hubRoles.includes(role)) { return throwError(() => 'Invalid role for Hub user'); } @@ -285,13 +242,6 @@ export class HubUsersService { allowedForCreation: true, userType: UserType.HUB }, - { - value: UserRole.DCB_PARTNER, - label: 'DCB Partner', - description: 'Partner access to merchant management', - allowedForCreation: true, - userType: UserType.HUB - } ] } as AvailableRolesResponse); } @@ -327,7 +277,7 @@ export class HubUsersService { // === MÉTHODES UTILITAIRES === isValidRoleForHub(role: UserRole): boolean { - const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_PARTNER, UserRole.DCB_SUPPORT]; + const hubRoles = [UserRole.DCB_ADMIN, UserRole.DCB_SUPPORT]; return hubRoles.includes(role); } @@ -343,8 +293,6 @@ export class HubUsersService { enabled: apiUser.enabled, emailVerified: apiUser.emailVerified, userType: userType, - merchantPartnerId: apiUser.merchantPartnerId, - merchantConfigId: apiUser.merchantConfigId, role: apiUser.role, createdBy: apiUser.createdBy, createdByUsername: apiUser.createdByUsername, @@ -384,9 +332,6 @@ export class HubUsersService { filteredUsers = filteredUsers.filter(user => user.userType === filters.userType); } - if (filters.merchantPartnerId) { - filteredUsers = filteredUsers.filter(user => user.merchantPartnerId === filters.merchantPartnerId); - } } // Pagination côté client diff --git a/src/app/modules/hub-users-management/hub-users.ts b/src/app/modules/hub-users-management/hub-users.ts index 039d2fa..3390a2f 100644 --- a/src/app/modules/hub-users-management/hub-users.ts +++ b/src/app/modules/hub-users-management/hub-users.ts @@ -76,7 +76,7 @@ export class HubUsersManagement implements OnInit, OnDestroy { userType: UserType; } = this.getDefaultUserForm(); - // Formulaire de création marchand (pour DCB_PARTNER) + // Formulaire de création marchand (pour les utilisateurs avec rôle permettant de créer des marchands) newMerchant: CreateMerchantDto = this.getDefaultMerchantForm(); // États des opérations @@ -101,8 +101,15 @@ export class HubUsersManagement implements OnInit, OnDestroy { showPassword = false; showNewPassword = false; + // Mode de création (nouveau) + creationMode: 'userOnly' | 'userAndMerchant' = 'userOnly'; + + // Liste des marchands disponibles pour association + availableMerchants: any[] = []; + // Références aux templates de modals @ViewChild('createUserModal') createUserModal!: TemplateRef; + @ViewChild('createMerchantModal') createMerchantModal!: TemplateRef; @ViewChild('resetPasswordModal') resetPasswordModal!: TemplateRef; @ViewChild('deleteUserModal') deleteUserModal!: TemplateRef; @@ -208,7 +215,9 @@ export class HubUsersManagement implements OnInit, OnDestroy { return [ { value: UserRole.DCB_ADMIN, label: 'DCB Admin', description: 'Administrateur système' }, { value: UserRole.DCB_SUPPORT, label: 'DCB Support', description: 'Support technique' }, - { value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' } + { value: UserRole.DCB_PARTNER_ADMIN, label: 'DCB Partner Admin', description: 'Administrateur partenaire' }, + { value: UserRole.DCB_PARTNER_MANAGER, label: 'DCB Partner Manager', description: 'Manager partenaire' }, + { value: UserRole.DCB_PARTNER_SUPPORT, label: 'DCB Partner Support', description: 'Support partenaire' } ]; } @@ -233,7 +242,7 @@ export class HubUsersManagement implements OnInit, OnDestroy { description: '', adresse: '', phone: '', - configs:[], + configs: [], technicalContacts: [] }; } @@ -332,10 +341,22 @@ export class HubUsersManagement implements OnInit, OnDestroy { this.openModal(this.createUserModal); } + // Méthode pour ouvrir le modal de création de marchand + openCreateMerchantModal() { + this.resetMerchantForm(); + this.openModal(this.createMerchantModal); + } + private resetUserForm() { this.newUser = this.getDefaultUserForm(); + this.creationMode = 'userOnly'; + console.log('🔄 User form reset'); + } + + private resetMerchantForm() { this.newMerchant = this.getDefaultMerchantForm(); - console.log('🔄 Hub user form reset'); + this.creatingMerchant = false; + console.log('🔄 Merchant form reset'); } // Méthode pour ouvrir le modal de réinitialisation de mot de passe @@ -387,6 +408,7 @@ export class HubUsersManagement implements OnInit, OnDestroy { onRoleSelectionChange(selectedRole: UserRole) { this.newUser.role = selectedRole; + // Le mode reste 'userOnly' par défaut, l'association au marchand se fera plus tard } // ==================== OPÉRATIONS CRUD ==================== @@ -416,106 +438,48 @@ export class HubUsersManagement implements OnInit, OnDestroy { console.log('📤 Creating hub user:', { username: this.newUser.username, - role: this.newUser.role, - type: this.newUser.role === UserRole.DCB_PARTNER ? 'DCB Partner (avec marchand)' : 'Utilisateur Hub standard' + role: this.newUser.role }); - // Pour DCB_PARTNER: créer d'abord le marchand - if (this.newUser.role === UserRole.DCB_PARTNER) { - // Validation du formulaire marchand pour DCB_PARTNER - const merchantValidation = this.validateMerchantForm(); - if (!merchantValidation.isValid) { - this.createUserError = merchantValidation.error!; - console.error('❌ Merchant form validation failed:', merchantValidation.error); - this.creatingUser = false; - return; - } - - this.createMerchantForDcbPartner(); - } else { - // Pour les autres rôles: créer uniquement l'utilisateur Hub - this.createHubUserOnly(); + // Déterminer le userType en fonction du rôle + let userType = UserType.HUB; + if (this.isPartnerRole(this.newUser.role)) { + userType = UserType.MERCHANT_PARTNER; } + + const userData = { + ...this.newUser, + userType: userType + }; + + // Créer uniquement l'utilisateur dans Keycloak + this.createKeycloakUserOnly(userData); } /** - * Crée un marchand avec un DCB Partner (ordre: merchantConfig -> Keycloak) + * Crée uniquement l'utilisateur dans Keycloak */ - private createMerchantForDcbPartner() { - this.creatingMerchant = true; - console.log('📤 Creating merchant for DCB_PARTNER:', this.newMerchant); + private createKeycloakUserOnly(userData: any) { + console.log('👤 Creating user in Keycloak only...'); - // Données du DCB Partner - const dcbPartnerData = { - username: this.newUser.username, - email: this.newUser.email, - password: this.newUser.password, - firstName: this.newUser.firstName, - lastName: this.newUser.lastName, - enabled: this.newUser.enabled, - emailVerified: this.newUser.emailVerified - }; - - // Données du marchand - const merchantData: CreateMerchantDto = { - ...this.newMerchant - - }; - - console.log('🔧 Données envoyées:'); - console.log(' Merchant:', merchantData); - console.log(' DCB Partner:', { ...dcbPartnerData, password: '***' }); - - // Utiliser le MerchantSyncService pour créer d'abord dans merchantConfig puis dans Keycloak - this.merchantSyncService.createMerchant(merchantData, dcbPartnerData) - .pipe(takeUntil(this.destroy$)) - .subscribe({ - next: (createResult) => { - console.log('✅ Merchant and DCB Partner created successfully:', createResult); - this.creatingUser = false; - this.creatingMerchant = false; - - console.log('📊 Résultat création:'); - console.log(' Merchant Config ID:', createResult.merchantConfig?.id); - console.log(' Keycloak Merchant ID:', createResult.keycloakMerchant?.id); - console.log(' Merchant name:', createResult.merchantConfig?.name); - console.log(' DCB Partner email:', createResult.keycloakMerchant?.email); - - this.modalService.dismissAll(); - this.refreshUsersList(); - this.cdRef.detectChanges(); - }, - error: (error) => { - console.error('❌ Error creating merchant and DCB Partner:', error); - this.creatingUser = false; - this.creatingMerchant = false; - - this.createUserError = this.getMerchantErrorMessage(error); - console.error('❌ Détails de l\'erreur:', error); - - this.cdRef.detectChanges(); - } - }); - } - - /** - * Crée uniquement un utilisateur Hub (pas de marchand) - */ - private createHubUserOnly() { - console.log('📤 Creating hub user only (no merchant)'); - - this.hubUsersService.createHubUser(this.newUser) + this.hubUsersService.createHubUser(userData) .pipe(takeUntil(this.destroy$)) .subscribe({ next: (createdUser) => { - console.log('✅ Hub user created successfully:', createdUser); + console.log('✅ User created successfully in Keycloak:', createdUser); this.creatingUser = false; + + // Si l'utilisateur a un rôle de partenaire, proposer de créer un marchand + if (this.isPartnerRole(this.newUser.role)) { + console.log('ℹ️ User with partner role created. Merchant creation can be done separately.'); + } + this.modalService.dismissAll(); this.refreshUsersList(); this.cdRef.detectChanges(); }, error: (error) => { - console.error('❌ Error creating hub user:', error); + console.error('❌ Error creating user in Keycloak:', error); this.creatingUser = false; this.createUserError = this.getErrorMessage(error); this.cdRef.detectChanges(); @@ -523,6 +487,56 @@ 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 + */ + private isPartnerRole(role: UserRole): boolean { + return [ + UserRole.DCB_PARTNER_ADMIN, + UserRole.DCB_PARTNER_MANAGER, + UserRole.DCB_PARTNER_SUPPORT + ].includes(role); + } + /** * Vérifie si l'utilisateur peut attribuer un rôle spécifique */ @@ -585,11 +599,19 @@ export class HubUsersManagement implements OnInit, OnDestroy { this.deletingUser = true; this.deleteUserError = ''; + // Supprimer uniquement l'utilisateur de Keycloak + this.deleteKeycloakUserOnly(); + } + + /** + * Supprime uniquement l'utilisateur de Keycloak + */ + private deleteKeycloakUserOnly() { this.hubUsersService.deleteHubUser(this.selectedUserForDelete.id) .pipe(takeUntil(this.destroy$)) .subscribe({ next: () => { - console.log('✅ Hub user deleted successfully'); + console.log('✅ User deleted successfully from Keycloak'); this.deletingUser = false; this.modalService.dismissAll(); this.refreshUsersList(); @@ -661,18 +683,18 @@ export class HubUsersManagement implements OnInit, OnDestroy { return error.error.message; } if (error.status === 400) { - return 'Données du marchand ou du DCB Partner invalides.'; + return 'Données du marchand invalides.'; } if (error.status === 409) { - return 'Un marchand avec ce nom ou un utilisateur avec ces identifiants existe déjà.'; + return 'Un marchand avec ce nom existe déjà.'; } if (error.status === 403) { - return 'Vous n\'avez pas les permissions pour créer un marchand ou un DCB Partner.'; + return 'Vous n\'avez pas les permissions pour créer un marchand.'; } if (error.status === 404) { return 'Service de création de marchand non disponible.'; } - return 'Erreur lors de la création du marchand et du DCB Partner.'; + return 'Erreur lors de la création du marchand.'; } private getResetPasswordErrorMessage(error: any): string { @@ -754,7 +776,7 @@ export class HubUsersManagement implements OnInit, OnDestroy { for (const { field, name } of requiredFields) { if (!field) { - return { isValid: false, error: `${name} est requis pour un DCB Partner` }; + return { isValid: false, error: `${name} est requis` }; } } 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 index 9090842..0f2cbaa 100644 --- a/src/app/modules/hub-users-management/merchant-sync-orchestrator.service.ts +++ b/src/app/modules/hub-users-management/merchant-sync-orchestrator.service.ts @@ -1,15 +1,12 @@ import { Injectable, inject } from '@angular/core'; -import { Observable, forkJoin, map, switchMap, catchError, of, throwError, tap } from 'rxjs'; +import { Observable, forkJoin, map, switchMap, catchError, of, throwError } from 'rxjs'; import { CreateUserDto, - UpdateUserDto, User, UserType, UserRole, - UserUtils } from '@core/models/dcb-bo-hub-user.model'; -import { HubUsersService} from '@modules/hub-users-management/hub-users.service'; import { MerchantUsersService} from '@modules/hub-users-management/merchant-users.service'; import { MerchantConfigService } from '@modules/merchant-config/merchant-config.service'; @@ -25,12 +22,12 @@ import { export interface MerchantSyncResult { merchantConfig: Merchant; - keycloakMerchant: User; + keycloakUser?: User; // Utilisateur associé (optionnel) } export interface MerchantUserSyncResult { keycloakUser: User; - merchantConfigUser: MerchantUser; + merchantConfigUser?: MerchantUser; } export interface MerchantSyncStatus { @@ -41,9 +38,14 @@ export interface MerchantSyncStatus { totalUserCount: number; } +export interface UserMerchantAssociation { + userId: string; + merchantConfigId: string; + role: UserRole; // Rôle dans MerchantConfig +} + @Injectable({ providedIn: 'root' }) export class MerchantSyncService { - private hubUsersService = inject(HubUsersService); private merchantUsersService = inject(MerchantUsersService); private merchantConfigService = inject(MerchantConfigService); @@ -75,610 +77,431 @@ export class MerchantSyncService { [UserRole.MERCHANT_CONFIG_VIEWER, UserRole.DCB_PARTNER_SUPPORT] ]); - // ==================== CREATE ==================== - - /** - * CREATE - Créer un merchant complet dans les deux systèmes - */ - createMerchant( - merchantData: CreateMerchantDto, - dcbPartnerData: { - username: string; - email: string; - password: string; - firstName: string; - lastName: string; - } - ): Observable { - console.log('📝 CREATE Merchant...'); + // ==================== 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( - switchMap(merchantConfig => { - console.log('✅ Merchant créé dans Merchant Config:', merchantConfig); - - const merchantConfigId = String(merchantConfig.id); - - const dcbPartnerDto: CreateUserDto = { - username: dcbPartnerData.username, - email: dcbPartnerData.email, - firstName: dcbPartnerData.firstName, - lastName: dcbPartnerData.lastName, - password: dcbPartnerData.password, - userType: UserType.HUB, - role: UserRole.DCB_PARTNER, - enabled: true, - emailVerified: true, - merchantConfigId: merchantConfigId - }; - - return this.hubUsersService.createHubUser(dcbPartnerDto).pipe( - map(keycloakMerchant => ({ - merchantConfig, - keycloakMerchant: { - ...keycloakMerchant, - merchantConfigId: merchantConfigId // <<<<< ✔ string - } - })), - catchError(keycloakError => { - console.error('❌ Échec Keycloak, rollback...'); - return this.rollbackMerchantCreation(String(merchantConfig.id!), keycloakError); - }) - ); - }), - catchError(error => { - console.error('❌ Échec création merchant:', error); + 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 merchant + * CREATE - Créer un utilisateur dans Keycloak (sans association) */ - createMerchantUser( - merchantConfigId: string, - keycloakMerchantId: string, + createKeycloakUser( userData: { username: string; email: string; password: string; firstName: string; lastName: string; - role: UserRole; // Rôle Keycloak + role: UserRole; + enabled?: boolean; + emailVerified?: boolean; } - ): Observable { - console.log('📝 CREATE Merchant User...'); + ): Observable { + console.log('📝 CREATE User dans Keycloak...'); - // Validation rôle Keycloak - if (!this.KEYCLOAK_MERCHANT_ROLES.includes(userData.role)) { - return throwError(() => new Error('Rôle Keycloak invalide')); - } + // Déterminer le type d'utilisateur selon le rôle + const userType = this.isMerchantRole(userData.role) + ? UserType.MERCHANT_PARTNER + : UserType.HUB; - // 1. Créer dans Keycloak const keycloakUserDto: CreateUserDto = { - ...userData, - userType: UserType.MERCHANT_PARTNER, - merchantPartnerId: keycloakMerchantId, - merchantConfigId: merchantConfigId, - enabled: true, - emailVerified: true + 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 }; - return this.merchantUsersService.createMerchantUser(keycloakUserDto).pipe( - switchMap(keycloakUser => { - console.log('✅ Utilisateur créé dans Keycloak:', keycloakUser); + // Sélectionner le service approprié selon le type d'utilisateur + const createService = this.merchantUsersService.createMerchantUser(keycloakUserDto); - // 2. Convertir rôle et ajouter à Merchant Config - const merchantConfigRole = this.mapToMerchantConfigRole(userData.role); - const merchantConfigUserId = keycloakUser.id; - - const merchantConfigUserDto: AddUserToMerchantDto = { - userId: keycloakUser.id, - role: merchantConfigRole, - merchantPartnerId: Number(merchantConfigId), - }; - - return this.merchantConfigService.addUserToMerchant(merchantConfigUserDto).pipe( - map(merchantConfigUser => { - console.log('✅ Utilisateur ajouté à Merchant Config:', merchantConfigUser); - - return { - keycloakUser: { - ...keycloakUser, - merchantConfigUserId, - merchantConfigId - }, - merchantConfigUser - }; - }), - catchError(merchantConfigError => { - console.error('❌ Échec Merchant Config, rollback Keycloak...'); - return this.rollbackUserCreation(keycloakUser.id, merchantConfigError); - }) - ); + return createService.pipe( + map(user => { + console.log('✅ Utilisateur créé dans Keycloak:', user); + return user; }), catchError(error => { - console.error('❌ Échec création utilisateur:', error); - return throwError(() => error); - }) - ); - } - - // ==================== READ ==================== - - /** - * READ - Récupérer un merchant par ID - */ - getMerchant( - merchantConfigId: string, - keycloakMerchantId: string - ): Observable { - console.log('🔍 READ Merchant...'); - - return forkJoin({ - merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)), - keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId) - }).pipe( - map(({ merchantConfig, keycloakMerchant }) => { - - // Vérifier la cohérence des IDs - if (String(merchantConfig.id) !== keycloakMerchant.merchantConfigId) { - console.warn('⚠️ Incohérence: merchantConfigId ne correspond pas'); - } - - return { - merchantConfig, - keycloakMerchant: { - ...keycloakMerchant, - merchantConfigId: String(merchantConfig.id) - } - }; - }), - catchError(error => { - console.error('❌ Erreur récupération merchant:', error); - return throwError(() => error); - }) - ); - } - - /** - * READ - Récupérer tous les merchants - */ - getAllMerchants(): Observable { - console.log('🔍 READ All Merchants...'); - - return forkJoin({ - merchants: this.merchantConfigService.getAllMerchants(), - dcbPartners: this.hubUsersService.getAllDcbPartners() - }).pipe( - map(({ merchants, dcbPartners }) => { - return merchants - .map(merchant => { - const dcbPartner = dcbPartners.users.find( - partner => partner.merchantConfigId === merchant.id - ); - - if (!dcbPartner) { - console.warn(`⚠️ Merchant ${merchant.id} n'a pas de DCB_PARTNER correspondant`); - return null; - } - - return { - merchantConfig: merchant, - keycloakMerchant: dcbPartner - }; - }) - .filter((merchant): merchant is MerchantSyncResult => merchant !== null); - }), - catchError(error => { - console.error('❌ Erreur récupération merchants:', error); + console.error('❌ Échec création utilisateur dans Keycloak:', error); return throwError(() => error); }) ); } /** - * READ - Récupérer tous les utilisateurs d'un merchant + * READ - Récupérer un merchant depuis MerchantConfig seulement */ - getMerchantUsers( - merchantConfigId: string, - keycloakMerchantId: string - ): Observable<{ - merchantConfig: Merchant; - keycloakMerchant: User; - users: Array<{ - keycloakUser: User; - merchantConfigUser?: MerchantUser; - synced: boolean; - }>; - }> { + 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({ - merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)), - keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId), - merchantConfigUsers: this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)), - keycloakUsers: this.merchantUsersService.getMerchantUsersByPartner(keycloakMerchantId) + user: this.getUserById(association.userId), + merchant: this.merchantConfigService.getMerchantById(Number(association.merchantConfigId)) }).pipe( - map(({ merchantConfig, keycloakMerchant, merchantConfigUsers, keycloakUsers }) => { + switchMap(({ user, merchant }) => { + console.log(`🔗 Associating user ${user.username} to merchant ${merchant.name}`); - const users = keycloakUsers.map(keycloakUser => { - const merchantConfigUser = merchantConfigUsers.find( - mcUser => mcUser.userId === keycloakUser.id - ); - - return { - keycloakUser, - merchantConfigUser, - synced: !!merchantConfigUser + // 2. Ajouter l'utilisateur à MerchantConfig + const merchantConfigUserDto: AddUserToMerchantDto = { + userId: user.id, + role: association.role, + merchantPartnerId: Number(association.merchantConfigId), }; - }); - return { - merchantConfig, - keycloakMerchant: { - ...keycloakMerchant, - merchantConfigId: String(merchantConfig.id) - }, - users - }; - }) - ); - } - - // ==================== UPDATE ==================== - - /** - * UPDATE - Mettre à jour un merchant - */ - updateMerchant( - merchantConfigId: string, - keycloakMerchantId: string, - updates: { - merchantConfig?: UpdateMerchantDto; - dcbPartner?: UpdateUserDto; - } - ): Observable { - console.log('✏️ UPDATE Merchant...'); - - const operations: Observable[] = []; - - // Mise à jour MerchantConfig - if (updates.merchantConfig) { - operations.push( - this.merchantConfigService.updateMerchant( - Number(merchantConfigId), - updates.merchantConfig - ) - ); - } - - // Mise à jour du user DCB_PARTNER - if (updates.dcbPartner && keycloakMerchantId) { - operations.push( - this.hubUsersService.updateHubUser(keycloakMerchantId, updates.dcbPartner) - ); - } - - if (operations.length === 0) { - return throwError(() => new Error('Aucune donnée à mettre à jour')); - } - - return forkJoin(operations).pipe( - switchMap(() => { - // Recharger les données mises à jour - return this.getMerchant( - merchantConfigId, - keycloakMerchantId - ); - }), - catchError(error => { - console.error('❌ Erreur mise à jour merchant:', error); - return throwError(() => error); - }) - ); - } - - /** - * UPDATE - Mettre à jour un utilisateur merchant - */ - updateMerchantUser( - keycloakUserId: string, - merchantConfigUserId: string, - merchantConfigId: string, - updates: { - keycloak?: UpdateUserDto; - merchantConfig?: { - role?: UserRole; - }; - } - ): Observable<{ - keycloakUser?: User; - merchantConfigUser?: MerchantUser; - }> { - console.log('✏️ UPDATE Merchant User...'); - - const operations: Observable[] = []; - - // Mise à jour Keycloak - if (updates.keycloak) { - operations.push( - this.merchantUsersService.updateMerchantUser(keycloakUserId, updates.keycloak) - ); - } - - // Mise à jour Merchant Config - if (updates.merchantConfig?.role) { - if (!this.MERCHANT_CONFIG_ROLES.includes(updates.merchantConfig.role)) { - return throwError(() => new Error('Rôle Merchant Config invalide')); - } - - const updateRoleDto: UpdateUserRoleDto = { - role: updates.merchantConfig.role - }; - - operations.push( - this.merchantConfigService.updateUserRole( - Number(merchantConfigId), - merchantConfigUserId, - updateRoleDto - ) - ); - } - - if (operations.length === 0) { - return throwError(() => new Error('Aucune donnée à mettre à jour')); - } - - return forkJoin(operations).pipe( - map(([keycloakUpdate, merchantConfigUpdate]) => ({ - keycloakUser: keycloakUpdate, - merchantConfigUser: merchantConfigUpdate - })), - catchError(error => { - console.error('❌ Erreur mise à jour utilisateur:', error); - return throwError(() => error); - }) - ); - } - - /** - * UPDATE - Activer/désactiver un utilisateur - */ - toggleUserStatus( - keycloakUserId: string, - enabled: boolean - ): Observable { - console.log(`🔄 ${enabled ? 'Activer' : 'Désactiver'} utilisateur...`); - - return this.merchantUsersService.updateMerchantUser(keycloakUserId, { enabled }); - } - - // ==================== DELETE ==================== - - /** - * DELETE - Supprimer un merchant - */ - deleteMerchant( - merchantConfigId: string, - keycloakMerchantId: string - ): Observable<{ success: boolean; message: string }> { - console.log('🗑️ DELETE Merchant...'); - - // 1. D'abord supprimer tous les utilisateurs - return this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe( - switchMap(merchantConfigUsers => { - const userDeletions: Observable[] = []; - - // Supprimer chaque utilisateur - merchantConfigUsers.forEach(mcUser => { - if (mcUser.email) { - userDeletions.push( - this.merchantUsersService.searchMerchantUsers({ query: mcUser.email }).pipe( - switchMap(users => { - if (users.length > 0) { - return this.deleteMerchantUser( - users[0].id, - mcUser.userId, - merchantConfigId - ).pipe(catchError(() => of(null))); - } - return of(null); - }) - ) - ); - } - }); - - // Supprimer le merchant - userDeletions.push( - this.merchantConfigService.deleteMerchant(Number(merchantConfigId)) - ); - - // Supprimer le DCB_PARTNER - userDeletions.push( - this.hubUsersService.deleteHubUser(keycloakMerchantId).pipe( - catchError(() => of({ ignored: true })) - ) - ); - - return forkJoin(userDeletions).pipe( - map(() => ({ - success: true, - message: 'Merchant et utilisateurs supprimés avec succès' - })) - ); + 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(async (error) => ({ - success: false, - message: `Erreur suppression: ${error.message}` - })) + catchError(error => { + console.error('❌ Error in association process:', error); + return throwError(() => error); + }) ); } /** - * DELETE - Supprimer un utilisateur merchant + * Dissocier un utilisateur d'un marchand */ - deleteMerchantUser( - keycloakUserId: string, - merchantConfigUserId: string, - merchantConfigId: string, + dissociateUserFromMerchant( + userId: string, + merchantConfigId: string ): Observable<{ success: boolean; message: string }> { - console.log('🗑️ DELETE Merchant User...'); + console.log('🔗 DISSOCIATE User from Merchant...'); return forkJoin({ - keycloakDeletion: this.merchantUsersService.deleteMerchantUser(keycloakUserId), - merchantConfigDeletion: this.merchantConfigService.removeUserFromMerchant( + // Retirer l'utilisateur de MerchantConfig + merchantConfigRemoval: this.merchantConfigService.removeUserFromMerchant( Number(merchantConfigId), - merchantConfigUserId + userId ).pipe(catchError(() => of({ ignored: true }))) }).pipe( map(() => ({ success: true, - message: 'Utilisateur supprimé des deux systèmes' + message: 'Utilisateur dissocié du marchand avec succès' })), catchError(async (error) => ({ - success: false, - message: `Erreur suppression: ${error.message}` - })) + success: false, + message: `Erreur lors de la dissociation: ${error.message}` + })) ); } - // ==================== UTILITAIRES ==================== + /** + * 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.length === 0) { + return of([]); + } + + // Récupérer les détails de chaque utilisateur depuis Keycloak + const userObservables = merchantConfigUsers.map(mcUser => + 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 ==================== /** - * Vérifier le statut de synchronisation + * UPDATE - Mettre à jour un merchant dans MerchantConfig seulement */ - checkSyncStatus( + updateMerchantInConfigOnly( merchantConfigId: string, - keycloakMerchantId: string - ): Observable { + 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({ - merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)).pipe( - catchError(() => of(null)) - ), - keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId).pipe( - catchError(() => of(null)) - ), - merchantConfigUsers: this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe( - catchError(() => of([])) - ), - keycloakUsers: this.merchantUsersService.getMerchantUsersByPartner(keycloakMerchantId).pipe( + merchantUsers: this.merchantUsersService.searchMerchantUsers({ query }).pipe( catchError(() => of([])) ) }).pipe( - map(({ merchantConfig, keycloakMerchant, merchantConfigUsers, keycloakUsers }) => { - - const syncedUsers = keycloakUsers.filter(kcUser => - merchantConfigUsers.some(mcUser => mcUser.email === kcUser.email) - ); - - return { - existsInKeycloak: !!keycloakMerchant, - existsInMerchantConfig: !!merchantConfig, - usersSynced: syncedUsers.length === keycloakUsers.length && - syncedUsers.length === merchantConfigUsers.length, - syncedUserCount: syncedUsers.length, - totalUserCount: Math.max(keycloakUsers.length, merchantConfigUsers.length) - }; + 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); }) ); } - /** - * Rechercher des merchants - */ - searchMerchants(query: string): Observable { - return this.merchantConfigService.getAllMerchants({ query }).pipe( - switchMap(merchants => { - if (merchants.length === 0) return of([]); - - return this.hubUsersService.getAllDcbPartners().pipe( - map(dcbPartners => { - return merchants - .map(merchant => { - const dcbPartner = dcbPartners.users.find( - partner => partner.merchantConfigId === merchant.id - ); - - return dcbPartner ? { - merchantConfig: merchant, - keycloakMerchant: dcbPartner - } : null; - }) - .filter((merchant): merchant is MerchantSyncResult => merchant !== null); - }) - ); - }) - ); - } - - /** - * Réinitialiser le mot de passe d'un utilisateur - */ - resetUserPassword( - keycloakUserId: string, - newPassword: string, - temporary: boolean = true - ): Observable<{ success: boolean; message: string }> { - return this.merchantUsersService.resetMerchantUserPassword( - keycloakUserId, - { newPassword, temporary } - ).pipe( - map(() => ({ - success: true, - message: 'Mot de passe réinitialisé avec succès' - })), - catchError(async (error) => ({ - success: false, - message: `Erreur réinitialisation: ${error.message}` - })) - ); - } - // ==================== 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; } - private mapToKeycloakRole(merchantConfigRole: UserRole): UserRole { - // Recherche inverse - for (const [key, value] of this.ROLE_MAPPING.entries()) { - if (value === merchantConfigRole) { - return key; - } - } - return UserRole.DCB_PARTNER_SUPPORT; - } - - private generateMerchantConfigId(keycloakId: string): number { - let hash = 0; - for (let i = 0; i < keycloakId.length; i++) { - hash = ((hash << 5) - hash) + keycloakId.charCodeAt(i); - hash = hash & hash; - } - return Math.abs(hash) % 1000000; - } - - private rollbackMerchantCreation( - merchantConfigId: string, - originalError: any - ): Observable { - return this.merchantConfigService.deleteMerchant(Number(merchantConfigId)).pipe( - switchMap(() => throwError(() => new Error( - `Échec création DCB_PARTNER: ${originalError.message}. Rollback effectué.` - ))) - ); - } - - private rollbackUserCreation( - keycloakUserId: string, - originalError: any - ): Observable { - return this.merchantUsersService.deleteMerchantUser(keycloakUserId).pipe( - switchMap(() => throwError(() => new Error( - `Échec création Merchant Config: ${originalError.message}. Rollback effectué.` - ))) - ); - } } \ No newline at end of file diff --git a/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.html b/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.html index 2d4e83a..7aca466 100644 --- a/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.html +++ b/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.html @@ -153,10 +153,6 @@ - - @if (showMerchantPartnerColumn) { - - } @for (user of displayedUsers; track user.id) { - - @if (showMerchantPartnerColumn) { - - }
Merchant Partner
Utilisateur @@ -188,21 +184,6 @@
-
-
- -
-
- - {{ (user.merchantPartnerId || 'N/A').substring(0, 8) }}... - -
-
-
diff --git a/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.ts b/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.ts index 8dbbcf7..318c155 100644 --- a/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.ts +++ b/src/app/modules/hub-users-management/merchant-users-list/merchant-users-list.ts @@ -116,7 +116,6 @@ export class MerchantUsersList implements OnInit, OnDestroy { .subscribe({ next: (user) => { this.currentUserRole = this.extractUserRole(user); - this.currentMerchantPartnerId = this.extractMerchantPartnerId(user); this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole); console.log('Merchant User Context Loaded:', { @@ -143,13 +142,6 @@ export class MerchantUsersList implements OnInit, OnDestroy { return null; } - private extractMerchantPartnerId(user: any): string { - if (user?.merchantPartnerId) { - return user.merchantPartnerId; - } - return this.authService.getCurrentMerchantPartnerId() || ''; - } - private canViewAllMerchantsCheck(role: UserRole | null): boolean { if (!role) return false; @@ -164,7 +156,6 @@ export class MerchantUsersList implements OnInit, OnDestroy { private fallbackPermissions(): void { this.currentUserRole = this.authService.getCurrentUserRole(); - this.currentMerchantPartnerId = this.authService.getCurrentMerchantPartnerId() || ''; this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole); } @@ -181,20 +172,12 @@ export class MerchantUsersList implements OnInit, OnDestroy { this.loading = true; this.error = ''; - let usersObservable: Observable; + const usersObservable: Observable = this.canViewAllMerchants + ? this.merchantUsersService + .getMerchantUsers(this.currentPage, this.itemsPerPage) + .pipe(map((response: PaginatedUserResponse) => response.users)) + : of([]); // fallback propre - if (this.canViewAllMerchants) { - // Admin/Support accédant au contexte Merchant - usersObservable = this.merchantUsersService.getMerchantUsers(this.currentPage, this.itemsPerPage).pipe( - map((response: PaginatedUserResponse) => response.users) - ); - } else if (this.currentMerchantPartnerId) { - // Merchant régulier voyant son équipe - usersObservable = this.merchantUsersService.getMerchantUsersByPartner(this.currentMerchantPartnerId); - } else { - // Fallback - usersObservable = this.merchantUsersService.getMyMerchantUsers(); - } usersObservable .pipe( diff --git a/src/app/modules/hub-users-management/merchant-users-profile/merchant-users-profile.ts b/src/app/modules/hub-users-management/merchant-users-profile/merchant-users-profile.ts index 45b3f85..bf84972 100644 --- a/src/app/modules/hub-users-management/merchant-users-profile/merchant-users-profile.ts +++ b/src/app/modules/hub-users-management/merchant-users-profile/merchant-users-profile.ts @@ -66,7 +66,7 @@ export class MerchantUserProfile implements OnInit, OnDestroy { // Getters pour la logique conditionnelle get isMerchantPartnerUser(): boolean { - return UserUtils.isMerchantPartnerUser(this.user!); + return UserUtils.isMerchantUser(this.user!); } userHasRole(user: User, role: UserRole): boolean { diff --git a/src/app/modules/hub-users-management/merchant-users.html b/src/app/modules/hub-users-management/merchant-users.html index 91666e5..ff7093f 100644 --- a/src/app/modules/hub-users-management/merchant-users.html +++ b/src/app/modules/hub-users-management/merchant-users.html @@ -136,81 +136,7 @@
- - @if (showMerchantPartnerIdField) { -
-
-
-
- - Votre Organisation Marchande -
-
-
- -
-
-
- VOUS -
-
- -
- -
- @if (currentMerchantPartnerId) { - {{ currentMerchantPartnerId }} - - } @else { - Chargement... - - } -
-
-
- - - - - - @if (!currentMerchantPartnerId) { -
-
- -
- - Merchant Partner ID non disponible
- Impossible de récupérer votre identifiant de partenaire. - Veuillez contacter l'administrateur. -
-
-
-
- } @else { -
-
- -
- - Information : - Cet utilisateur sera automatiquement associé à votre organisation marchande. - Vous ne pouvez pas modifier cette association. - -
-
-
- } -
-
-
- } - +
} - - @if (showMerchantPartnerField) { -
- - - - @if (loadingMerchantPartners) { -
-
-
- Chargement... -
- Chargement des partenaires marchands... -
-
- } - - - @else if (merchantPartnersError) { -
-
-
- - {{ merchantPartnersError }} -
- -
-
- } - - - @else if (merchantPartners.length > 0) { - - -
- {{ merchantSelectionHelpText }} - - @if (merchantPartners.length === 1) { - - - Un seul partenaire disponible - sélectionné automatiquement - - } -
- } - - - @else { -
- - Aucun partenaire marchand disponible. - Veuillez contacter l'administrateur système. -
- } - - - @if (requireMerchantPartnerSelection && !selectedMerchantPartnerId && !loadingMerchantPartners) { -
- - La sélection d'un partenaire marchand est obligatoire -
- } -
- } - - @if (newUser.role) {
@@ -575,7 +399,6 @@
Informations système :
- • Merchant Partner ID : {{ currentMerchantPartnerId || 'Chargement...' }}
• Type d'utilisateur : MERCHANT
• Créé par : Utilisateur courant
• Votre rôle : {{ currentUserRole || 'Non défini' }} @@ -597,7 +420,7 @@ - -
- } @else { - - } - } -
-
-
-
-
- } - - - - @if (totalPages > 1) { -
- -
- } - } @else { -
-
- -
Aucun utilisateur
-

Aucun utilisateur n'est associé à ce marchand.

-
-
- } - - -
diff --git a/src/app/modules/merchant-config/merchant-config-view/merchant-config-view.ts b/src/app/modules/merchant-config/merchant-config-view/merchant-config-view.ts index c99c255..c0559c5 100644 --- a/src/app/modules/merchant-config/merchant-config-view/merchant-config-view.ts +++ b/src/app/modules/merchant-config/merchant-config-view/merchant-config-view.ts @@ -290,7 +290,6 @@ export class MerchantConfigView implements OnInit, OnDestroy { .subscribe({ next: (profile) => { this.currentUserRole = this.authService.getCurrentUserRole(); - this.currentMerchantPartnerId = this.authService.getCurrentMerchantPartnerId() || ''; this.cdRef.detectChanges(); }, error: (error) => { @@ -693,7 +692,7 @@ export class MerchantConfigView implements OnInit, OnDestroy { } getAvailableUserRoles(): { value: UserRole, label: string }[] { return [ - { value: UserRole.DCB_PARTNER, label: 'Partenaire' }, + //{ value: UserRole.DCB_PARTNER, label: 'Partenaire' }, { value: UserRole.DCB_PARTNER_ADMIN, label: 'Admin Partenaire' }, { value: UserRole.DCB_ADMIN, label: 'Administrateur' }, { value: UserRole.DCB_SUPPORT, label: 'Support' } @@ -711,7 +710,7 @@ export class MerchantConfigView implements OnInit, OnDestroy { // Un admin partenaire peut assigner des rôles partenaire if (this.currentUserRole === UserRole.DCB_PARTNER_ADMIN) { - return [UserRole.DCB_PARTNER, UserRole.DCB_PARTNER_ADMIN].includes(role); + return [UserRole.DCB_PARTNER_ADMIN].includes(role); } return false; diff --git a/src/app/modules/merchant-config/merchant-config.ts b/src/app/modules/merchant-config/merchant-config.ts index 9a28699..25fd8b7 100644 --- a/src/app/modules/merchant-config/merchant-config.ts +++ b/src/app/modules/merchant-config/merchant-config.ts @@ -162,7 +162,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy { this.currentUserRole = this.authService.getCurrentUserRole(); // Déterminer si c'est un utilisateur marchand - if (this.currentUserRole === UserRole.DCB_PARTNER || + if (//this.currentUserRole === UserRole.DCB_PARTNER || this.currentUserRole === UserRole.DCB_PARTNER_ADMIN || this.currentUserRole === UserRole.DCB_PARTNER_MANAGER || this.currentUserRole === UserRole.DCB_PARTNER_SUPPORT || @@ -251,9 +251,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy { this.loadingMerchantUsers = true; this.merchantUsersError = ''; - this.merchantSyncService.getMerchantUsers( - this.currentMerchantConfigId, - this.currentMerchantPartnerId + this.merchantSyncService.getUsersByMerchant( + this.currentMerchantConfigId ) .pipe( takeUntil(this.destroy$), @@ -419,15 +418,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy { next: (user) => { this.currentUserRole = this.extractUserRole(user); - if (user?.role.includes(UserRole.DCB_PARTNER)) { - this.currentMerchantPartnerId = user.id; - this.currentMerchantConfigId = user.merchantConfigId; - } else { - - this.currentMerchantPartnerId = user?.merchantPartnerId || ''; - this.currentMerchantConfigId = this.extractMerchantConfigId(user); - } - + this.currentMerchantConfigId = this.extractMerchantConfigId(user); + if (this.currentUserRole) { this.userPermissions = this.roleService.getPermissionsForRole(this.currentUserRole); this.canCreateMerchants = this.canManageMerchant(); @@ -496,7 +488,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy { // ==================== GESTION DES MARCHANDS ==================== private initializeMerchantPartnerContext(): void { - if ((this.currentUserRole === UserRole.DCB_PARTNER || + if ((//this.currentUserRole === UserRole.DCB_PARTNER || this.currentUserRole === UserRole.DCB_PARTNER_ADMIN) && this.currentMerchantConfigId) { this.selectedMerchantConfigId = this.currentMerchantConfigId; @@ -551,8 +543,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy { if (!this.isMerchantUser) return false; // Seuls certains rôles marchands peuvent créer des utilisateurs - return this.currentUserRole === UserRole.DCB_PARTNER || - this.currentUserRole === UserRole.DCB_PARTNER_ADMIN; + return this.currentUserRole === UserRole.DCB_PARTNER_ADMIN; //this.currentUserRole === UserRole.DCB_PARTNER || } /** diff --git a/src/app/modules/profile/profile.html b/src/app/modules/profile/profile.html index f97292a..9021230 100644 --- a/src/app/modules/profile/profile.html +++ b/src/app/modules/profile/profile.html @@ -125,12 +125,6 @@ Membre depuis {{ getCreationDate() }} - @if (isMerchantUser()) { -
- - Partner ID: {{ getMerchantPartnerId() }} -
- } @@ -193,14 +187,6 @@ {{ getUserTypeDisplay() }} - @if (isMerchantUser()) { -
- Merchant Partner ID : -
- {{ getMerchantPartnerId() }} -
-
- } @@ -394,14 +380,6 @@ {{ getUserTypeDisplay() }} - @if (isMerchantUser()) { -
- -
- {{ getMerchantPartnerId() }} -
-
- } } diff --git a/src/app/modules/profile/profile.ts b/src/app/modules/profile/profile.ts index 3584aa8..2f6b5b8 100644 --- a/src/app/modules/profile/profile.ts +++ b/src/app/modules/profile/profile.ts @@ -319,7 +319,7 @@ export class MyProfile implements OnInit, OnDestroy { } isMerchantUser(): boolean { - return UserUtils.isMerchantPartnerUser(this.user!); + return UserUtils.isMerchantUser(this.user!); } getUserTypeDisplay(): string { @@ -332,10 +332,6 @@ export class MyProfile implements OnInit, OnDestroy { return this.formatTimestamp(this.user.createdTimestamp); } - getMerchantPartnerId(): string { - return this.user?.merchantPartnerId || 'Non applicable'; - } - refresh() { this.loadUserProfile(); } diff --git a/src/app/modules/subscriptions/subscription-payments-list/subscription-payments-list.ts b/src/app/modules/subscriptions/subscription-payments-list/subscription-payments-list.ts index b6d705e..98f13bc 100644 --- a/src/app/modules/subscriptions/subscription-payments-list/subscription-payments-list.ts +++ b/src/app/modules/subscriptions/subscription-payments-list/subscription-payments-list.ts @@ -118,8 +118,7 @@ export class SubscriptionPaymentsList implements OnInit, OnDestroy { .subscribe({ next: (user) => { this.currentUserRole = this.extractUserRole(user); - this.currentMerchantPartnerId = this.extractMerchantPartnerId(user); - + console.log('Payments Context Loaded:', { role: this.currentUserRole, merchantPartnerId: this.currentMerchantPartnerId @@ -127,7 +126,6 @@ export class SubscriptionPaymentsList implements OnInit, OnDestroy { }, error: (error) => { console.error('Error loading current user permissions:', error); - this.fallbackPermissions(); } }); } @@ -140,17 +138,6 @@ export class SubscriptionPaymentsList implements OnInit, OnDestroy { return null; } - private extractMerchantPartnerId(user: any): string { - if (user?.merchantPartnerId) { - return user.merchantPartnerId; - } - return this.authService.getCurrentMerchantPartnerId() || ''; - } - - private fallbackPermissions(): void { - this.currentUserRole = this.authService.getCurrentUserRole(); - this.currentMerchantPartnerId = this.authService.getCurrentMerchantPartnerId() || ''; - } private loadSubscriptionDetails() { if (this.subscriptionId) { diff --git a/src/app/modules/subscriptions/subscriptions-list/subscriptions-list.ts b/src/app/modules/subscriptions/subscriptions-list/subscriptions-list.ts index fa24c3a..32c9246 100644 --- a/src/app/modules/subscriptions/subscriptions-list/subscriptions-list.ts +++ b/src/app/modules/subscriptions/subscriptions-list/subscriptions-list.ts @@ -111,7 +111,6 @@ export class SubscriptionsList implements OnInit, OnDestroy { .subscribe({ next: (user) => { this.currentUserRole = this.extractUserRole(user); - this.currentMerchantPartnerId = this.extractMerchantPartnerId(user); this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole); console.log('Subscriptions Context Loaded:', { @@ -138,13 +137,6 @@ export class SubscriptionsList implements OnInit, OnDestroy { return null; } - private extractMerchantPartnerId(user: any): string { - if (user?.merchantPartnerId) { - return user.merchantPartnerId; - } - return this.authService.getCurrentMerchantPartnerId() || ''; - } - private canViewAllMerchantsCheck(role: string | null): boolean { if (!role) return false; @@ -159,7 +151,6 @@ export class SubscriptionsList implements OnInit, OnDestroy { private fallbackPermissions(): void { this.currentUserRole = this.authService.getCurrentUserRole(); - this.currentMerchantPartnerId = this.authService.getCurrentMerchantPartnerId() || ''; this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole); } diff --git a/src/app/modules/subscriptions/subscriptions.ts b/src/app/modules/subscriptions/subscriptions.ts index 62dc9c3..0c4c461 100644 --- a/src/app/modules/subscriptions/subscriptions.ts +++ b/src/app/modules/subscriptions/subscriptions.ts @@ -92,14 +92,11 @@ export class SubscriptionsManagement implements OnInit, OnDestroy { .subscribe({ next: (user) => { this.currentUserRole = this.extractUserRole(user); - this.currentMerchantPartnerId = this.extractMerchantPartnerId(user); console.log(`User ROLE: ${this.currentUserRole}`); - console.log(`Merchant Partner ID: ${this.currentMerchantPartnerId}`); }, error: (error) => { console.error('Error loading user profile:', error); - this.fallbackPermissions(); } }); } @@ -115,24 +112,6 @@ export class SubscriptionsManagement implements OnInit, OnDestroy { return null; } - /** - * Extraire le merchantPartnerId - */ - private extractMerchantPartnerId(user: any): string { - if (user?.merchantPartnerId) { - return user.merchantPartnerId; - } - return this.authService.getCurrentMerchantPartnerId() || ''; - } - - /** - * Fallback en cas d'erreur de chargement du profil - */ - private fallbackPermissions(): void { - this.currentUserRole = this.authService.getCurrentUserRole(); - this.currentMerchantPartnerId = this.authService.getCurrentMerchantPartnerId() || ''; - } - // ==================== MÉTHODES D'INTERFACE ==================== // Méthode pour changer d'onglet diff --git a/src/app/modules/support/support.ts b/src/app/modules/support/support.ts index 3c74a2b..1f61904 100644 --- a/src/app/modules/support/support.ts +++ b/src/app/modules/support/support.ts @@ -2,6 +2,7 @@ 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', @@ -11,16 +12,22 @@ export class Support implements OnInit { private testData = { merchantConfigId: '', - keycloakMerchantId: '', + keycloakUserId: '', testUserId: '', - testMerchantConfigUserId:'' + testMerchantConfigUserId: '', + associatedUserId: '' // ID d'un utilisateur associé au marchand }; - constructor(private merchantCrudService: MerchantSyncService) {} + 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(); @@ -32,21 +39,25 @@ export class Support implements OnInit { 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 + // Test 1: Création indépendante await this.testCreateOperations(); - // Test 2: Lecture + // Test 2: Association + await this.testAssociationOperations(); + + // Test 3: Lecture await this.testReadOperations(); - // Test 3: Mise à jour + // Test 4: Mise à jour await this.testUpdateOperations(); - // Test 4: Gestion utilisateurs + // Test 5: Gestion utilisateurs await this.testUserOperations(); - // Test 5: Suppression + // Test 6: Suppression await this.testDeleteOperations(); console.log('\n✅ TOUS LES TESTS TERMINÉS AVEC SUCCÈS!'); @@ -58,15 +69,15 @@ export class Support implements OnInit { } /** - * TEST 1: Opérations de création + * TEST 1: Opérations de création indépendante */ private async testCreateOperations(): Promise { - console.log('\n📝 TEST 1: Opérations CREATE'); + console.log('\n📝 TEST 1: Opérations CREATE INDÉPENDANTES'); console.log('-'.repeat(40)); try { - // 1.1 Créer un merchant de test - console.log('1.1 Création d\'un merchant test...'); + // 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(), @@ -89,59 +100,67 @@ export class Support implements OnInit { ] }; - const ownerData = { - username: `testowner.${Date.now()}`, - email: `owner.${Date.now()}@test-merchant.com`, - password: 'TestPassword123!', - firstName: 'Test', - lastName: 'Owner' - }; + console.log('📤 Données merchant pour MerchantConfig:', merchantData); - console.log('📤 Données merchant:', merchantData); - console.log('👤 Données owner:', { ...ownerData, password: '***' }); - - const createResult = await firstValueFrom( - this.merchantCrudService.createMerchant(merchantData, ownerData) + const merchantConfigResult = await firstValueFrom( + this.merchantCrudService.createMerchantInConfigOnly(merchantData) ); - this.testData.merchantConfigId = String(createResult.merchantConfig.id!); - this.testData.keycloakMerchantId = createResult.keycloakMerchant.id; + this.testData.merchantConfigId = String(merchantConfigResult.id!); - console.log('✅ Merchant créé avec succès!'); + console.log('✅ Merchant créé dans MerchantConfig uniquement!'); console.log(' Merchant Config ID:', this.testData.merchantConfigId); - console.log(' Keycloak Merchant ID:', this.testData.keycloakMerchantId); - console.log(' Merchant name:', createResult.merchantConfig.name); - console.log(' DCB Partner email:', createResult.keycloakMerchant.email); + console.log(' Merchant name:', merchantConfigResult.name); - // 1.2 Créer un utilisateur pour le merchant - console.log('\n1.2 Création d\'un utilisateur test...'); + // 1.2 Créer un utilisateur Hub dans Keycloak + console.log('\n1.2 Création d\'un utilisateur Hub dans Keycloak...'); - const userData = { - username: `testuser.${Date.now()}`, - email: `user.${Date.now()}@test-merchant.com`, - password: 'UserPassword123!', + const hubUserData = { + username: `testhubuser.${Date.now()}`, + email: `hubuser.${Date.now()}@example.com`, + password: 'HubPassword123!', firstName: 'Test', - lastName: 'User', + 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 }; - const userResult = await firstValueFrom( - this.merchantCrudService.createMerchantUser( - this.testData.merchantConfigId, - this.testData.keycloakMerchantId, - userData - ) + console.log('👤 Données Partner User pour Keycloak:', { ...partnerUserData, password: '***' }); + + const partnerUserResult = await firstValueFrom( + this.merchantCrudService.createKeycloakUser(partnerUserData) ); - this.testData.testUserId = userResult.keycloakUser.id; - this.testData.testMerchantConfigUserId = String(userResult.merchantConfigUser.userId); + this.testData.testUserId = partnerUserResult.id; - console.log('✅ Utilisateur créé avec succès!'); + console.log('✅ Utilisateur Partenaire créé dans Keycloak:'); console.log(' Keycloak User ID:', this.testData.testUserId); - console.log(' Merchant Config User ID:', this.testData.testMerchantConfigUserId); - console.log(' Email:', userResult.keycloakUser.email); - console.log(' Rôle Keycloak:', userResult.keycloakUser.role); - console.log(' Rôle Merchant Config:', userResult.merchantConfigUser.role); + 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); @@ -150,10 +169,67 @@ export class Support implements OnInit { } /** - * TEST 2: Opérations de lecture + * 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 2: Opérations READ'); + console.log('\n🔍 TEST 3: Opérations READ'); console.log('-'.repeat(40)); if (!this.testData.merchantConfigId) { @@ -162,68 +238,60 @@ export class Support implements OnInit { } try { - // 2.1 Lire le merchant par ID - console.log('2.1 Lecture du merchant par ID...'); + // 3.1 Lire le merchant depuis MerchantConfig + console.log('3.1 Lecture du merchant depuis MerchantConfig...'); const merchant = await firstValueFrom( - this.merchantCrudService.getMerchant( - this.testData.merchantConfigId, - this.testData.keycloakMerchantId + this.merchantCrudService.getMerchantFromConfigOnly( + this.testData.merchantConfigId ) ); - console.log('✅ Merchant récupéré:'); - console.log(' ID:', merchant.merchantConfig.id); - console.log(' Nom:', merchant.merchantConfig.name); - console.log(' Adresse:', merchant.merchantConfig.adresse); - console.log(' Configurations:', merchant.merchantConfig.configs?.length || 0); - console.log(' Contacts techniques:', merchant.merchantConfig.technicalContacts?.length || 0); + 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); - // 2.2 Lire tous les merchants - console.log('\n2.2 Lecture de tous les merchants...'); + // 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.getAllMerchants() + this.merchantCrudService.getAllMerchantsFromConfig() ); console.log(`✅ ${allMerchants.length} merchants trouvés au total`); if (allMerchants.length > 0) { - console.log(' Dernier merchant:', allMerchants[allMerchants.length - 1].merchantConfig.name); + const lastMerchant = allMerchants[allMerchants.length - 1]; + console.log(' Dernier merchant:', lastMerchant.name, '(ID:', lastMerchant.id, ')'); } - // 2.3 Lire les utilisateurs du merchant - console.log('\n2.3 Lecture des utilisateurs du merchant...'); + // 3.3 Rechercher des merchants + console.log('\n3.3 Recherche de merchants dans MerchantConfig...'); - const merchantUsers = await firstValueFrom( - this.merchantCrudService.getMerchantUsers( - this.testData.merchantConfigId, - this.testData.keycloakMerchantId - ) + const searchResults = await firstValueFrom( + this.merchantCrudService.searchMerchantsInConfig('Test') ); - console.log(`✅ ${merchantUsers.users.length} utilisateurs dans ce merchant`); + console.log(`✅ ${searchResults.length} merchants trouvés avec "Test"`); - merchantUsers.users.forEach((user: any, index: number) => { - console.log(` ${index + 1}. ${user.keycloakUser?.email || 'Inconnu'}`); - console.log(` Synchronisé: ${user.synced ? '✅' : '❌'}`); - }); + if (searchResults.length > 0) { + console.log(' Premier résultat:', searchResults[0].name); + } - // 2.4 Vérifier le statut de synchronisation - console.log('\n2.4 Vérification statut synchronisation...'); + // 3.4 Rechercher des utilisateurs + console.log('\n3.4 Recherche d\'utilisateurs dans Keycloak...'); - const syncStatus = await firstValueFrom( - this.merchantCrudService.checkSyncStatus( - String(this.testData.merchantConfigId), - this.testData.keycloakMerchantId - ) + const userSearchResults = await firstValueFrom( + this.merchantCrudService.searchKeycloakUsers('test') ); - console.log('✅ Statut de synchronisation:'); - console.log(' Existe dans Keycloak:', syncStatus.existsInKeycloak ? '✅' : '❌'); - console.log(' Existe dans Merchant Config:', syncStatus.existsInMerchantConfig ? '✅' : '❌'); - console.log(' Utilisateurs synchronisés:', syncStatus.usersSynced ? '✅' : '❌'); - console.log(` ${syncStatus.syncedUserCount}/${syncStatus.totalUserCount} utilisateurs synchronisés`); + 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); @@ -231,10 +299,10 @@ export class Support implements OnInit { } /** - * TEST 3: Opérations de mise à jour + * TEST 4: Opérations de mise à jour */ private async testUpdateOperations(): Promise { - console.log('\n✏️ TEST 3: Opérations UPDATE'); + console.log('\n✏️ TEST 4: Opérations UPDATE'); console.log('-'.repeat(40)); if (!this.testData.merchantConfigId) { @@ -243,83 +311,40 @@ export class Support implements OnInit { } try { - // 3.1 Mettre à jour le merchant - console.log('3.1 Mise à jour du merchant...'); + // 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 newEmail = `updated.${Date.now()}@test-merchant.com`; const updateResult = await firstValueFrom( - this.merchantCrudService.updateMerchant( + this.merchantCrudService.updateMerchantInConfigOnly( this.testData.merchantConfigId, - this.testData.keycloakMerchantId, - { - merchantConfig: { - name: newName, - description: 'Mis à jour par les tests' - }, - dcbPartner: { - email: newEmail - } + { + name: newName, + description: 'Mis à jour par les tests', + adresse: '456 Updated Street' } ) ); - console.log('✅ Merchant mis à jour:'); + console.log('✅ Merchant mis à jour dans MerchantConfig:'); console.log(' Nouveau nom:', newName); - console.log(' Nouvel email DCB Partner:', newEmail); + console.log(' Description:', updateResult.description); - // 3.2 Mettre à jour l'utilisateur - console.log('\n3.2 Mise à jour de l\'utilisateur...'); + // 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) { - const userUpdateResult = await firstValueFrom( - this.merchantCrudService.updateMerchantUser( - this.testData.testUserId, - this.testData.testMerchantConfigUserId, + if (this.testData.testUserId && this.testData.merchantConfigId && this.testData.testMerchantConfigUserId) { + const roleUpdateResult = await firstValueFrom( + this.merchantCrudService.updateUserRoleInMerchantConfig( this.testData.merchantConfigId, - { - keycloak: { - firstName: 'Updated', - lastName: 'User' - }, - merchantConfig: { - role: UserRole.MERCHANT_CONFIG_MANAGER - } - } + this.testData.testUserId, + UserRole.MERCHANT_CONFIG_MANAGER ) ); - console.log('✅ Utilisateur mis à jour:'); - console.log(' Nouveau nom:', 'Updated User'); + console.log('✅ Rôle utilisateur mis à jour dans MerchantConfig:'); console.log(' Nouveau rôle:', UserRole.MERCHANT_CONFIG_MANAGER); - - } else { - console.log('⚠️ Aucun utilisateur à mettre à jour'); - } - - // 3.3 Activer/désactiver un utilisateur - console.log('\n3.3 Changement statut utilisateur...'); - - if (this.testData.testUserId) { - const toggleResult = await firstValueFrom( - this.merchantCrudService.toggleUserStatus( - this.testData.testUserId, - false // Désactiver - ) - ); - - console.log('✅ Statut utilisateur changé:'); - console.log(' Activé:', toggleResult.enabled ? '✅' : '❌'); - - // Réactiver pour les tests suivants - await firstValueFrom( - this.merchantCrudService.toggleUserStatus( - this.testData.testUserId, - true - ) - ); - console.log(' Réactivé pour les tests suivants'); } } catch (error) { @@ -328,10 +353,10 @@ export class Support implements OnInit { } /** - * TEST 4: Opérations utilisateurs avancées + * TEST 5: Opérations utilisateurs avancées */ private async testUserOperations(): Promise { - console.log('\n👥 TEST 4: Opérations utilisateurs avancées'); + console.log('\n👥 TEST 5: Opérations utilisateurs avancées'); console.log('-'.repeat(40)); if (!this.testData.testUserId) { @@ -340,32 +365,72 @@ export class Support implements OnInit { } try { - // 4.1 Réinitialiser le mot de passe - console.log('4.1 Réinitialisation mot de passe...'); + // 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.merchantCrudService.resetUserPassword( + this.merchantUsersService.resetMerchantUserPassword( this.testData.testUserId, - 'NewPassword123!', - true + resetPasswordDto ) ); console.log('✅ Mot de passe réinitialisé:'); - console.log(' Succès:', resetResult.success); console.log(' Message:', resetResult.message); - // 4.2 Rechercher des merchants - console.log('\n4.2 Recherche de merchants...'); + // 5.2 Dissocier l'utilisateur du marchand + console.log('\n5.2 Dissociation de l\'utilisateur du marchand...'); - const searchResults = await firstValueFrom( - this.merchantCrudService.searchMerchants('Test') - ); + if (this.testData.testUserId && this.testData.merchantConfigId) { + const dissociateResult = await firstValueFrom( + this.merchantCrudService.dissociateUserFromMerchant( + this.testData.testUserId, + this.testData.merchantConfigId + ) + ); - console.log(`✅ ${searchResults.length} merchants trouvés avec "Test"`); + 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 (searchResults.length > 0) { - console.log(' Premier résultat:', searchResults[0].merchantConfig.name); + 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) { @@ -374,10 +439,10 @@ export class Support implements OnInit { } /** - * TEST 5: Opérations de suppression + * TEST 6: Opérations de suppression */ private async testDeleteOperations(): Promise { - console.log('\n🗑️ TEST 5: Opérations DELETE'); + console.log('\n🗑️ TEST 6: Opérations DELETE'); console.log('-'.repeat(40)); if (!this.testData.merchantConfigId) { @@ -386,64 +451,71 @@ export class Support implements OnInit { } try { - // 5.1 Supprimer l'utilisateur test - console.log('5.1 Suppression de l\'utilisateur test...'); + // 6.1 Dissocier avant suppression + console.log('6.1 Dissociation de l\'utilisateur avant suppression...'); - if (this.testData.testUserId) { - const deleteUserResult = await firstValueFrom( - this.merchantCrudService.deleteMerchantUser( + if (this.testData.testUserId && this.testData.merchantConfigId) { + await firstValueFrom( + this.merchantCrudService.dissociateUserFromMerchant( this.testData.testUserId, - this.testData.testMerchantConfigUserId, this.testData.merchantConfigId ) ); + console.log('✅ Utilisateur dissocié'); + } - console.log('✅ Utilisateur supprimé:'); + // 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); - // Réinitialiser les IDs utilisateur this.testData.testUserId = ''; this.testData.testMerchantConfigUserId = ''; - } else { - console.log('⚠️ Aucun utilisateur à supprimer'); } - // 5.2 Supprimer le merchant test - console.log('\n5.2 Suppression du merchant test...'); + // 6.3 Supprimer le merchant de MerchantConfig + console.log('\n6.3 Suppression du merchant de MerchantConfig...'); const deleteMerchantResult = await firstValueFrom( - this.merchantCrudService.deleteMerchant( - this.testData.merchantConfigId, - this.testData.keycloakMerchantId + this.merchantCrudService.deleteMerchantFromConfigOnly( + this.testData.merchantConfigId ) ); - console.log('✅ Merchant supprimé:'); + console.log('✅ Merchant supprimé de MerchantConfig:'); console.log(' Succès:', deleteMerchantResult.success); console.log(' Message:', deleteMerchantResult.message); - // 5.3 Vérifier la suppression - console.log('\n5.3 Vérification de la suppression...'); + // 6.4 Vérifier la suppression + console.log('\n6.4 Vérification de la suppression...'); try { await firstValueFrom( - this.merchantCrudService.getMerchant( - this.testData.merchantConfigId, - this.testData.keycloakMerchantId + this.merchantCrudService.getMerchantFromConfigOnly( + this.testData.merchantConfigId ) ); - console.log('❌ Le merchant existe toujours - PROBLÈME!'); + console.log('❌ Le merchant existe toujours dans MerchantConfig - PROBLÈME!'); } catch (error) { - console.log('✅ Le merchant a bien été supprimé'); + console.log('✅ Le merchant a bien été supprimé de MerchantConfig'); } // Réinitialiser toutes les données this.testData = { merchantConfigId: '', - keycloakMerchantId: '', + keycloakUserId: '', testUserId: '', - testMerchantConfigUserId: '' + testMerchantConfigUserId: '', + associatedUserId: '' }; console.log('🧹 Données de test réinitialisées'); @@ -453,14 +525,28 @@ export class Support implements OnInit { } } - /** - * Méthodes pour exécuter des tests individuels - */ + // ==================== 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(); @@ -476,16 +562,54 @@ export class Support implements OnInit { 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 Merchant ID:', this.testData.keycloakMerchantId || 'Non créé'); - console.log('Test User ID:', this.testData.testUserId || 'Non créé'); - console.log('Test Merchant Config User ID:', this.testData.testMerchantConfigUserId || '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éé'); } /** @@ -494,9 +618,10 @@ export class Support implements OnInit { resetTestData(): void { this.testData = { merchantConfigId: '', - keycloakMerchantId: '', + keycloakUserId: '', testUserId: '', - testMerchantConfigUserId: '' + testMerchantConfigUserId: '', + associatedUserId: '' }; console.log('🧹 Données de test réinitialisées'); }