feat: add DCB User Service API - Authentication system with KEYCLOAK - Modular architecture with services for each feature
This commit is contained in:
parent
b81710fa59
commit
25d99b4edc
@ -200,19 +200,6 @@ export class TransactionDetails implements OnInit {
|
|||||||
if (diffDays < 7) return `Il y a ${diffDays} j`;
|
if (diffDays < 7) return `Il y a ${diffDays} j`;
|
||||||
return this.formatDate(date);
|
return this.formatDate(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
canRefund(): boolean {
|
|
||||||
return this.access.canRefund && this.transaction?.status === 'SUCCESS';
|
|
||||||
}
|
|
||||||
|
|
||||||
canRetry(): boolean {
|
|
||||||
return this.access.canRetry && this.transaction?.status === 'FAILED';
|
|
||||||
}
|
|
||||||
|
|
||||||
canCancel(): boolean {
|
|
||||||
return this.access.canCancel && this.transaction?.status === 'PENDING';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthodes pour le template
|
// Méthodes pour le template
|
||||||
getUserBadgeClass(): string {
|
getUserBadgeClass(): string {
|
||||||
return this.access.isHubUser ? 'bg-primary' : 'bg-success';
|
return this.access.isHubUser ? 'bg-primary' : 'bg-success';
|
||||||
|
|||||||
@ -21,7 +21,8 @@ import {
|
|||||||
lucideStore,
|
lucideStore,
|
||||||
lucideCalendar,
|
lucideCalendar,
|
||||||
lucideRepeat,
|
lucideRepeat,
|
||||||
lucideCreditCard
|
lucideCreditCard,
|
||||||
|
lucideInfo
|
||||||
} from '@ng-icons/lucide';
|
} from '@ng-icons/lucide';
|
||||||
import { NgbPaginationModule, NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbPaginationModule, NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
@ -125,12 +126,9 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
private initializePermissions() {
|
private initializePermissions() {
|
||||||
this.access = this.accessService.getTransactionAccess();
|
this.access = this.accessService.getTransactionAccess();
|
||||||
|
|
||||||
// Ajouter le merchant ID aux filtres si nécessaire
|
// IMPORTANT: Toujours filtrer par merchant pour les merchant users
|
||||||
if (this.access.isMerchantUser && this.access.allowedMerchantIds.length > 0) {
|
if (this.access.isMerchantUser && this.access.allowedMerchantIds.length > 0) {
|
||||||
this.filters.merchantPartnerId = this.access.allowedMerchantIds[0];
|
this.filters.merchantPartnerId = this.access.allowedMerchantIds[0];
|
||||||
} else if (this.access.isHubUser && this.access.merchantId) {
|
|
||||||
// Pour les hub users qui veulent voir un merchant spécifique
|
|
||||||
this.filters.merchantPartnerId = this.access.merchantId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Définir le rôle pour l'affichage
|
// Définir le rôle pour l'affichage
|
||||||
@ -147,27 +145,31 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.error = '';
|
this.error = '';
|
||||||
|
|
||||||
// Mettre à jour les filtres avec la recherche
|
// Préparer les filtres pour l'API
|
||||||
if (this.searchTerm) {
|
const apiFilters = this.prepareFiltersForApi();
|
||||||
this.filters.search = this.searchTerm;
|
|
||||||
} else {
|
|
||||||
delete this.filters.search;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appliquer le tri
|
|
||||||
this.filters.sortBy = this.sortField;
|
|
||||||
this.filters.sortOrder = this.sortDirection;
|
|
||||||
|
|
||||||
// Appliquer les restrictions de merchant pour les non-admin hub users
|
console.log('Chargement transactions avec filtres:', apiFilters);
|
||||||
if (!this.access.canManageAll && this.access.allowedMerchantIds.length > 0) {
|
|
||||||
this.filters.merchantPartnerId = this.access.allowedMerchantIds[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.transactionsService.getTransactions(this.filters).subscribe({
|
this.transactionsService.getTransactions(apiFilters).subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
this.paginatedData = data;
|
this.paginatedData = data;
|
||||||
this.transactions = data.data;
|
this.transactions = data.data;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
|
// Log pour debug
|
||||||
|
if (data.data.length > 0) {
|
||||||
|
console.log(`Chargement réussi: ${data.data.length} transactions`);
|
||||||
|
if (this.access.isMerchantUser && this.currentMerchantId) {
|
||||||
|
// Vérifier que toutes les transactions appartiennent bien au marchand
|
||||||
|
const wrongMerchantTx = data.data.filter(tx =>
|
||||||
|
tx.merchantPartnerId !== this.currentMerchantId
|
||||||
|
);
|
||||||
|
if (wrongMerchantTx.length > 0) {
|
||||||
|
console.warn('ATTENTION: certaines transactions ne sont pas du bon marchand:', wrongMerchantTx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
@ -179,12 +181,39 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private prepareFiltersForApi(): TransactionQuery {
|
||||||
|
const apiFilters: TransactionQuery = { ...this.filters };
|
||||||
|
|
||||||
|
// Ajouter la recherche si présente
|
||||||
|
if (this.searchTerm) {
|
||||||
|
apiFilters.search = this.searchTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appliquer le tri
|
||||||
|
apiFilters.sortBy = this.sortField;
|
||||||
|
apiFilters.sortOrder = this.sortDirection;
|
||||||
|
|
||||||
|
// Nettoyer les filtres (enlever les undefined)
|
||||||
|
Object.keys(apiFilters).forEach(key => {
|
||||||
|
if (apiFilters[key as keyof TransactionQuery] === undefined) {
|
||||||
|
delete apiFilters[key as keyof TransactionQuery];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return apiFilters;
|
||||||
|
}
|
||||||
|
|
||||||
// Recherche et filtres
|
// Recherche et filtres
|
||||||
onSearch() {
|
onSearch() {
|
||||||
this.filters.page = 1;
|
this.filters.page = 1;
|
||||||
this.loadTransactions();
|
this.loadTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearSearch() {
|
||||||
|
this.searchTerm = '';
|
||||||
|
this.onSearch();
|
||||||
|
}
|
||||||
|
|
||||||
onClearFilters() {
|
onClearFilters() {
|
||||||
this.searchTerm = '';
|
this.searchTerm = '';
|
||||||
this.filters = {
|
this.filters = {
|
||||||
@ -197,7 +226,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
sortOrder: 'desc'
|
sortOrder: 'desc'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Réappliquer les restrictions de merchant
|
// IMPORTANT: Toujours réappliquer le filtrage par marchand pour les merchant users
|
||||||
if (this.access.isMerchantUser && this.access.allowedMerchantIds.length > 0) {
|
if (this.access.isMerchantUser && this.access.allowedMerchantIds.length > 0) {
|
||||||
this.filters.merchantPartnerId = this.access.allowedMerchantIds[0];
|
this.filters.merchantPartnerId = this.access.allowedMerchantIds[0];
|
||||||
}
|
}
|
||||||
@ -225,7 +254,10 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Permissions pour les filtres
|
// Permissions pour les filtres
|
||||||
canUseMerchantFilter(): boolean {
|
canUseMerchantFilter(): boolean {
|
||||||
return this.access.canFilterByMerchant && this.access.allowedMerchantIds.length > 1;
|
// Uniquement pour les hub users avec plusieurs merchants autorisés
|
||||||
|
return this.access.canFilterByMerchant &&
|
||||||
|
this.access.isHubUser &&
|
||||||
|
this.access.allowedMerchantIds.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
canUseAllFilters(): boolean {
|
canUseAllFilters(): boolean {
|
||||||
@ -240,6 +272,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
this.sortField = field;
|
this.sortField = field;
|
||||||
this.sortDirection = 'desc';
|
this.sortDirection = 'desc';
|
||||||
}
|
}
|
||||||
|
this.filters.page = 1;
|
||||||
this.loadTransactions();
|
this.loadTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,15 +289,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
viewTransactionDetails(transactionId: string) {
|
viewTransactionDetails(transactionId: string) {
|
||||||
// Vérifier les permissions avant d'afficher
|
this.transactionSelected.emit(transactionId);
|
||||||
this.accessService.canAccessTransaction().subscribe(canAccess => {
|
|
||||||
if (canAccess) {
|
|
||||||
this.transactionSelected.emit(transactionId);
|
|
||||||
} else {
|
|
||||||
this.error = 'Vous n\'avez pas la permission de voir les détails de cette transaction';
|
|
||||||
this.cdRef.detectChanges();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sélection multiple
|
// Sélection multiple
|
||||||
@ -283,6 +308,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
this.selectedTransactions.clear();
|
this.selectedTransactions.clear();
|
||||||
}
|
}
|
||||||
|
this.updateSelectAllState();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectAllState() {
|
updateSelectAllState() {
|
||||||
@ -290,7 +316,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
this.selectedTransactions.size === this.transactions.length;
|
this.selectedTransactions.size === this.transactions.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utilitaires d'affichage MIS À JOUR
|
// Utilitaires d'affichage
|
||||||
getStatusBadgeClass(status: TransactionStatus): string {
|
getStatusBadgeClass(status: TransactionStatus): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'SUCCESS': return 'badge bg-success';
|
case 'SUCCESS': return 'badge bg-success';
|
||||||
@ -321,7 +347,7 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPeriodicityBadgeClass(periodicity?: string): string {
|
getPeriodicityBadgeClass(periodicity?: string): string {
|
||||||
if (!periodicity) return 'badge bg-secondary';
|
if (!periodicity) return 'badge bg-secondary';
|
||||||
|
|
||||||
switch (periodicity.toLowerCase()) {
|
switch (periodicity.toLowerCase()) {
|
||||||
@ -338,101 +364,6 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCurrency(amount: number, currency: Currency = Currency.XOF): string {
|
|
||||||
return TransactionUtils.formatAmount(amount, currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
formatDate(date: Date | string | undefined | null): string {
|
|
||||||
// Si la date est null/undefined, retourner une chaîne vide
|
|
||||||
if (!date) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si c'est déjà une Date valide
|
|
||||||
if (date instanceof Date) {
|
|
||||||
// Vérifier si la Date est valide
|
|
||||||
if (isNaN(date.getTime())) {
|
|
||||||
return 'Date invalide';
|
|
||||||
}
|
|
||||||
return new Intl.DateTimeFormat('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
}).format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si c'est une chaîne, essayer de la convertir
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
const dateObj = new Date(date);
|
|
||||||
|
|
||||||
// Vérifier si la conversion a réussi
|
|
||||||
if (isNaN(dateObj.getTime())) {
|
|
||||||
// Essayer d'autres formats
|
|
||||||
const alternativeDate = this.parseDateString(date);
|
|
||||||
if (alternativeDate && !isNaN(alternativeDate.getTime())) {
|
|
||||||
return new Intl.DateTimeFormat('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
}).format(alternativeDate);
|
|
||||||
}
|
|
||||||
return 'Date invalide';
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Intl.DateTimeFormat('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
}).format(dateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pour tout autre type, retourner '-'
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseDateString(dateString: string): Date | null {
|
|
||||||
try {
|
|
||||||
// Essayer différents formats de date
|
|
||||||
const formats = [
|
|
||||||
dateString, // Format ISO original
|
|
||||||
dateString.replace(' ', 'T'), // Remplacer espace par T
|
|
||||||
dateString.split('.')[0], // Enlever les millisecondes
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const format of formats) {
|
|
||||||
const date = new Date(format);
|
|
||||||
if (!isNaN(date.getTime())) {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAmountColor(amount: number): string {
|
|
||||||
if (amount >= 10000) return 'text-danger fw-bold';
|
|
||||||
if (amount >= 5000) return 'text-warning fw-semibold';
|
|
||||||
return 'text-success';
|
|
||||||
}
|
|
||||||
|
|
||||||
// utilitaires pour les abonnements
|
|
||||||
getStatusDisplayName(status: TransactionStatus): string {
|
|
||||||
return TransactionUtils.getStatusDisplayName(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTypeDisplayName(type: TransactionType): string {
|
|
||||||
return TransactionUtils.getTypeDisplayName(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPeriodicityDisplayName(periodicity?: string): string {
|
getPeriodicityDisplayName(periodicity?: string): string {
|
||||||
if (!periodicity) return '';
|
if (!periodicity) return '';
|
||||||
|
|
||||||
@ -445,6 +376,43 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
return periodicityNames[periodicity.toLowerCase()] || periodicity;
|
return periodicityNames[periodicity.toLowerCase()] || periodicity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatCurrency(amount: number, currency: Currency = Currency.XOF): string {
|
||||||
|
return TransactionUtils.formatAmount(amount, currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date: Date | string | undefined | null): string {
|
||||||
|
if (!date) return '-';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const dateObj = date instanceof Date ? date : new Date(date);
|
||||||
|
if (isNaN(dateObj.getTime())) return 'Date invalide';
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat('fr-FR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
}).format(dateObj);
|
||||||
|
} catch {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAmountColor(amount: number): string {
|
||||||
|
if (amount >= 10000) return 'text-danger fw-bold';
|
||||||
|
if (amount >= 5000) return 'text-warning fw-semibold';
|
||||||
|
return 'text-success';
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusDisplayName(status: TransactionStatus): string {
|
||||||
|
return TransactionUtils.getStatusDisplayName(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeDisplayName(type: TransactionType): string {
|
||||||
|
return TransactionUtils.getTypeDisplayName(type);
|
||||||
|
}
|
||||||
|
|
||||||
// Méthodes pour sécuriser l'accès aux stats
|
// Méthodes pour sécuriser l'accès aux stats
|
||||||
getTotal(): number {
|
getTotal(): number {
|
||||||
@ -482,8 +450,28 @@ export class TransactionsList implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
getScopeText(): string {
|
getScopeText(): string {
|
||||||
if (this.access.isMerchantUser && this.currentMerchantId) {
|
if (this.access.isMerchantUser && this.currentMerchantId) {
|
||||||
return `Merchant ${this.currentMerchantId}`;
|
return `Marchand ${this.currentMerchantId}`;
|
||||||
|
} else if (this.access.isHubUser && this.filters.merchantPartnerId) {
|
||||||
|
return `Marchand ${this.filters.merchantPartnerId}`;
|
||||||
}
|
}
|
||||||
return this.access.canManageAll ? 'Tous les merchants' : 'Merchants autorisés';
|
return 'Tous les marchands';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode pour recharger les données
|
||||||
|
refreshData() {
|
||||||
|
this.loadTransactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
showDebugInfo() {
|
||||||
|
console.log('=== DEBUG INFO ===');
|
||||||
|
console.log('Permissions:', {
|
||||||
|
isMerchantUser: this.access.isMerchantUser,
|
||||||
|
isHubUser: this.access.isHubUser,
|
||||||
|
merchantId: this.currentMerchantId,
|
||||||
|
allowedMerchantIds: this.access.allowedMerchantIds,
|
||||||
|
filters: this.filters
|
||||||
|
});
|
||||||
|
console.log('Transactions chargées:', this.transactions.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
// [file name]: transactions/services/transaction-access.service.ts
|
|
||||||
import { Injectable, Injector, inject } from '@angular/core';
|
import { Injectable, Injector, inject } from '@angular/core';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { RoleManagementService, UserRole } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService, UserRole } from '@core/services/hub-users-roles-management.service';
|
||||||
@ -10,12 +9,6 @@ export interface TransactionAccess {
|
|||||||
canViewAllTransactions: boolean; // Toutes vs seulement les siennes
|
canViewAllTransactions: boolean; // Toutes vs seulement les siennes
|
||||||
canViewDetails: boolean;
|
canViewDetails: boolean;
|
||||||
|
|
||||||
// Permissions d'actions
|
|
||||||
canRefund: boolean;
|
|
||||||
canRetry: boolean;
|
|
||||||
canCancel: boolean;
|
|
||||||
canExport: boolean;
|
|
||||||
|
|
||||||
// Permissions administratives
|
// Permissions administratives
|
||||||
canManageAll: boolean; // Toutes les transactions
|
canManageAll: boolean; // Toutes les transactions
|
||||||
canFilterByMerchant: boolean;
|
canFilterByMerchant: boolean;
|
||||||
@ -41,7 +34,6 @@ export class TransactionAccessService {
|
|||||||
private roleService: RoleManagementService
|
private roleService: RoleManagementService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
||||||
getTransactionAccess(): TransactionAccess {
|
getTransactionAccess(): TransactionAccess {
|
||||||
if (this.accessCache) {
|
if (this.accessCache) {
|
||||||
return this.accessCache;
|
return this.accessCache;
|
||||||
@ -57,19 +49,13 @@ export class TransactionAccessService {
|
|||||||
canViewAllTransactions: this.canViewAllTransactions(userRole, isHubUser),
|
canViewAllTransactions: this.canViewAllTransactions(userRole, isHubUser),
|
||||||
canViewDetails: this.canViewDetails(userRole, isHubUser),
|
canViewDetails: this.canViewDetails(userRole, isHubUser),
|
||||||
|
|
||||||
// Actions selon le rôle
|
|
||||||
canRefund: this.canPerformRefund(userRole, isHubUser),
|
|
||||||
canRetry: this.canPerformRetry(userRole, isHubUser),
|
|
||||||
canCancel: this.canPerformCancel(userRole, isHubUser),
|
|
||||||
canExport: this.canExport(userRole, isHubUser),
|
|
||||||
|
|
||||||
// Permissions administratives
|
// Permissions administratives
|
||||||
canManageAll: this.canManageAll(userRole, isHubUser),
|
canManageAll: this.canManageAll(userRole, isHubUser),
|
||||||
canFilterByMerchant: this.canFilterByMerchant(userRole, isHubUser),
|
canFilterByMerchant: this.canFilterByMerchant(userRole, isHubUser),
|
||||||
canViewSensitiveData: this.canViewSensitiveData(userRole, isHubUser),
|
canViewSensitiveData: this.canViewSensitiveData(userRole, isHubUser),
|
||||||
|
|
||||||
// Scope
|
// Scope
|
||||||
allowedMerchantIds: this.getAllowedMerchantIds(isHubUser, merchantId),
|
allowedMerchantIds: this.getAllowedMerchantIds(isHubUser, merchantId, userRole),
|
||||||
isHubUser,
|
isHubUser,
|
||||||
isMerchantUser: !isHubUser,
|
isMerchantUser: !isHubUser,
|
||||||
|
|
||||||
@ -86,13 +72,26 @@ export class TransactionAccessService {
|
|||||||
// === MÉTHODES DE DÉTERMINATION DES PERMISSIONS ===
|
// === MÉTHODES DE DÉTERMINATION DES PERMISSIONS ===
|
||||||
|
|
||||||
private canViewTransactions(userRole: UserRole, isHubUser: boolean): boolean {
|
private canViewTransactions(userRole: UserRole, isHubUser: boolean): boolean {
|
||||||
// Tous les rôles peuvent voir les transactions
|
// Tous les rôles peuvent voir les transactions (marchands et hub)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canViewAllTransactions(userRole: UserRole, isHubUser: boolean): boolean {
|
private canViewAllTransactions(userRole: UserRole, isHubUser: boolean): boolean {
|
||||||
// Hub users et DCB_PARTNER_ADMIN peuvent voir toutes les transactions
|
// Hub users et DCB_PARTNER_ADMIN peuvent voir toutes les transactions
|
||||||
return isHubUser || userRole === UserRole.DCB_PARTNER_ADMIN;
|
if (isHubUser) {
|
||||||
|
return true; // Les utilisateurs hub voient toutes les transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les utilisateurs marchands :
|
||||||
|
switch (userRole) {
|
||||||
|
case UserRole.DCB_PARTNER_ADMIN:
|
||||||
|
case UserRole.DCB_PARTNER_MANAGER:
|
||||||
|
case UserRole.DCB_PARTNER_SUPPORT:
|
||||||
|
// Les admins/support du marchand voient toutes les transactions de leur marchand
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private canViewDetails(userRole: UserRole, isHubUser: boolean): boolean {
|
private canViewDetails(userRole: UserRole, isHubUser: boolean): boolean {
|
||||||
@ -100,32 +99,6 @@ export class TransactionAccessService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canPerformRefund(userRole: UserRole, isHubUser: boolean): boolean {
|
|
||||||
// DCB_ADMIN: peut rembourser toutes les transactions
|
|
||||||
// DCB_SUPPORT: peut rembourser les transactions de son périmètre
|
|
||||||
// DCB_PARTNER_ADMIN: peut rembourser les transactions de son merchant
|
|
||||||
return isHubUser
|
|
||||||
? userRole === UserRole.DCB_ADMIN || userRole === UserRole.DCB_SUPPORT
|
|
||||||
: userRole === UserRole.DCB_PARTNER_ADMIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
private canPerformRetry(userRole: UserRole, isHubUser: boolean): boolean {
|
|
||||||
// Mêmes permissions que le remboursement
|
|
||||||
return this.canPerformRefund(userRole, isHubUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private canPerformCancel(userRole: UserRole, isHubUser: boolean): boolean {
|
|
||||||
// Plus restrictif - seulement les admins peuvent annuler
|
|
||||||
return isHubUser
|
|
||||||
? userRole === UserRole.DCB_ADMIN
|
|
||||||
: userRole === UserRole.DCB_PARTNER_ADMIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
private canExport(userRole: UserRole, isHubUser: boolean): boolean {
|
|
||||||
// Tous peuvent exporter leurs propres données
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private canManageAll(userRole: UserRole, isHubUser: boolean): boolean {
|
private canManageAll(userRole: UserRole, isHubUser: boolean): boolean {
|
||||||
// Seulement DCB_ADMIN hub peut tout gérer
|
// Seulement DCB_ADMIN hub peut tout gérer
|
||||||
return isHubUser && userRole === UserRole.DCB_ADMIN;
|
return isHubUser && userRole === UserRole.DCB_ADMIN;
|
||||||
@ -137,8 +110,13 @@ export class TransactionAccessService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private canViewSensitiveData(userRole: UserRole, isHubUser: boolean): boolean {
|
private canViewSensitiveData(userRole: UserRole, isHubUser: boolean): boolean {
|
||||||
// Hub users et DCB_PARTNER_ADMIN peuvent voir les données sensibles
|
if (isHubUser) {
|
||||||
return isHubUser || userRole === UserRole.DCB_PARTNER_ADMIN;
|
// Tous les utilisateurs hub peuvent voir les données sensibles
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Pour les marchands, seulement les rôles avec autorisation
|
||||||
|
return userRole === UserRole.DCB_PARTNER_ADMIN || userRole === UserRole.DCB_PARTNER_SUPPORT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === GESTION DU SCOPE ===
|
// === GESTION DU SCOPE ===
|
||||||
@ -156,16 +134,33 @@ export class TransactionAccessService {
|
|||||||
return isNaN(merchantId) ? undefined : merchantId;
|
return isNaN(merchantId) ? undefined : merchantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAllowedMerchantIds(isHubUser: boolean, merchantId?: number): number[] {
|
private getAllowedMerchantIds(isHubUser: boolean, merchantId?: number, userRole?: UserRole): number[] {
|
||||||
if (isHubUser) {
|
if (isHubUser) {
|
||||||
// Hub users peuvent voir tous les merchants
|
// Hub users peuvent voir tous les merchants
|
||||||
return []; // Tableau vide = tous les merchants
|
return []; // Tableau vide = tous les merchants
|
||||||
} else {
|
} else {
|
||||||
// Merchant users: seulement leur merchant
|
// Merchant users: seulement leur merchant
|
||||||
return merchantId ? [merchantId] : [];
|
// Mais seulement si l'utilisateur a accès aux transactions
|
||||||
|
if (merchantId && this.hasMerchantAccess(userRole)) {
|
||||||
|
return [merchantId];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasMerchantAccess(userRole?: UserRole): boolean {
|
||||||
|
if (!userRole) return false;
|
||||||
|
|
||||||
|
// Rôles marchands qui ont accès aux transactions
|
||||||
|
const merchantRolesWithAccess = [
|
||||||
|
UserRole.DCB_PARTNER_ADMIN,
|
||||||
|
UserRole.DCB_PARTNER_MANAGER,
|
||||||
|
UserRole.DCB_PARTNER_SUPPORT
|
||||||
|
];
|
||||||
|
|
||||||
|
return merchantRolesWithAccess.includes(userRole);
|
||||||
|
}
|
||||||
|
|
||||||
// === MÉTHODES PUBLIQUES ===
|
// === MÉTHODES PUBLIQUES ===
|
||||||
|
|
||||||
// Vérifie si l'utilisateur peut accéder à une transaction spécifique
|
// Vérifie si l'utilisateur peut accéder à une transaction spécifique
|
||||||
@ -184,6 +179,7 @@ export class TransactionAccessService {
|
|||||||
|
|
||||||
// Merchant users: seulement leur merchant
|
// Merchant users: seulement leur merchant
|
||||||
if (access.isMerchantUser) {
|
if (access.isMerchantUser) {
|
||||||
|
// Vérifier si l'utilisateur marchand a accès au merchant de la transaction
|
||||||
return of(access.allowedMerchantIds.includes(transactionMerchantId));
|
return of(access.allowedMerchantIds.includes(transactionMerchantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +191,22 @@ export class TransactionAccessService {
|
|||||||
return of(access.allowedMerchantIds.includes(transactionMerchantId));
|
return of(access.allowedMerchantIds.includes(transactionMerchantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Obtenir le scope des transactions pour les requêtes API
|
||||||
|
getTransactionScope(): { merchantIds?: number[] } {
|
||||||
|
const access = this.getTransactionAccess();
|
||||||
|
|
||||||
|
if (access.isHubUser) {
|
||||||
|
// Hub users peuvent voir tous les merchants
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
// Merchant users: seulement leur(s) merchant(s)
|
||||||
|
if (access.allowedMerchantIds.length > 0) {
|
||||||
|
return { merchantIds: access.allowedMerchantIds };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Nettoyer le cache
|
// Nettoyer le cache
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.accessCache = null;
|
this.accessCache = null;
|
||||||
@ -204,4 +216,10 @@ export class TransactionAccessService {
|
|||||||
refreshAccess(): void {
|
refreshAccess(): void {
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Méthode utilitaire pour afficher les permissions (debug)
|
||||||
|
logAccessInfo(): void {
|
||||||
|
const access = this.getTransactionAccess();
|
||||||
|
console.log('Transaction Access Info:', access);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user