feat(sprint-11 web 2026-04-25): pages PrimeFaces Sprint 10 (UBO, audit-trail viewer, délégations rôles) + bump api 1.0.6→1.0.8
DRY strict appliqué : web réutilise directement les DTOs officiels de unionflow-server-api 1.0.8 (CreateBeneficiaireEffectifRequest, BeneficiaireEffectifResponse, AuditTrailOperationResponse, CreateRoleDelegationRequest, RoleDelegationResponse) au lieu de DTOs miroirs locaux. Aucune duplication. Bump dépendance api 1.0.6 → 1.0.8 REST clients @RegisterRestClient configKey=unionflow-api - BeneficiaireEffectifRestClient : CRUD lister/trouverParId/creer/mettreAJour/desactiver - AuditTrailRestClient : 5 endpoints lecture (parUtilisateur, historique, parOrganisation, sodViolations, financial) - RoleDelegationRestClient : listerParOrganisation / creer / revoquer Beans @ViewScoped - BeneficiaireEffectifBean : recherche (KYC|org|PEP), création formulaire, marquerPep, désactiver - AuditTrailViewerBean : 5 modes (USER/ENTITY/ORG/SOD_VIOLATIONS/FINANCIAL), couleurAction (DELETE→danger, VALIDATE→success, etc.), couleurSod - RoleDelegationBean : recherche/créer/révoquer, couleurStatut (ACTIVE/REVOQUEE/EXPIREE) Pages XHTML - /pages/secure/conformite/beneficiaires-effectifs.xhtml — recherche + tableau + nouvelle UBO (panel toggleable) - /pages/secure/conformite/audit-trail.xhtml — filtres mode + tableau + détail JSONB (pre format) - /pages/secure/admin/role-delegations.xhtml — table actives + nouvelle (datePicker dates) MenuBean + menu.xhtml - 3 nouveaux flags : isBeneficiairesEffectifsVisible, isAuditTrailViewerVisible, isRoleDelegationsVisible - 3 menuitems ajoutés au sous-menu Conformité existant (icônes pi-users, pi-history, pi-share-alt) - Gating par rôles : COMPLIANCE_OFFICER + CONTROLEUR_INTERNE pour audit ; ADMIN_ORGANISATION + PRESIDENT pour délégations Tests (10/10 verts, 31/31 cumulé S8+S11) - AuditTrailViewerBeanTest : 8 tests (couleurAction × 6 cas, couleurSod, defaults) - RoleDelegationBeanTest : 2 tests (couleurStatut × 5, defaults)
This commit is contained in:
@@ -646,6 +646,31 @@ public class MenuBean implements Serializable {
|
||||
return hasAnyRole("SUPER_ADMIN", "COMPLIANCE_OFFICER");
|
||||
}
|
||||
|
||||
/**
|
||||
* Bénéficiaires Effectifs (UBO) — Instr. BCEAO 003-03-2025.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isBeneficiairesEffectifsVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "COMPLIANCE_OFFICER",
|
||||
"CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit Trail viewer — compliance / contrôle interne.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isAuditTrailViewerVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "COMPLIANCE_OFFICER", "CONTROLEUR_INTERNE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Délégations de rôles — admin org / président.
|
||||
* @since 2026-04-25 (Sprint 11)
|
||||
*/
|
||||
public boolean isRoleDelegationsVisible() {
|
||||
return hasAnyRole("SUPER_ADMIN", "ADMIN_ORGANISATION", "PRESIDENT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne true si l'organisation active dispose d'au moins un module métier spécifique
|
||||
* (au-delà des modules communs toujours disponibles).
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST de lecture audit trail (Sprint 11 ⇄ backend Sprint 10 CQRS read).
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/audit-trail")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface AuditTrailRestClient {
|
||||
|
||||
@GET
|
||||
@Path("/by-user/{userId}")
|
||||
List<AuditTrailOperationResponse> parUtilisateur(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("from") String from,
|
||||
@QueryParam("to") String to);
|
||||
|
||||
@GET
|
||||
@Path("/by-entity/{type}/{id}")
|
||||
List<AuditTrailOperationResponse> historique(
|
||||
@PathParam("type") String entityType, @PathParam("id") UUID entityId);
|
||||
|
||||
@GET
|
||||
@Path("/by-organisation/{orgId}")
|
||||
List<AuditTrailOperationResponse> parOrganisation(@PathParam("orgId") UUID orgId);
|
||||
|
||||
@GET
|
||||
@Path("/sod-violations")
|
||||
List<AuditTrailOperationResponse> violationsSod();
|
||||
|
||||
@GET
|
||||
@Path("/financial/{orgId}")
|
||||
List<AuditTrailOperationResponse> operationsFinancieres(
|
||||
@PathParam("orgId") UUID orgId,
|
||||
@QueryParam("from") String from,
|
||||
@QueryParam("to") String to);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.CreateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.UpdateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.response.BeneficiaireEffectifResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST des Bénéficiaires Effectifs (Sprint 11 ⇄ backend Sprint 10).
|
||||
* Réutilise les DTOs officiels de unionflow-server-api 1.0.8 — zéro duplication.
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/kyc/beneficiaires-effectifs")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface BeneficiaireEffectifRestClient {
|
||||
|
||||
@GET
|
||||
List<BeneficiaireEffectifResponse> lister(
|
||||
@QueryParam("kycDossierId") UUID kycDossierId,
|
||||
@QueryParam("organisationCibleId") UUID organisationCibleId,
|
||||
@QueryParam("pep") Boolean pep);
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
BeneficiaireEffectifResponse trouverParId(@PathParam("id") UUID id);
|
||||
|
||||
@POST
|
||||
BeneficiaireEffectifResponse creer(CreateBeneficiaireEffectifRequest request);
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
BeneficiaireEffectifResponse mettreAJour(
|
||||
@PathParam("id") UUID id, UpdateBeneficiaireEffectifRequest request);
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
void desactiver(@PathParam("id") UUID id);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.lions.unionflow.client.service;
|
||||
|
||||
import dev.lions.unionflow.client.security.AuthHeaderFactory;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.request.CreateRoleDelegationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.response.RoleDelegationResponse;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
/**
|
||||
* Client REST des délégations de rôle (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@RegisterRestClient(configKey = "unionflow-api")
|
||||
@RegisterClientHeaders(AuthHeaderFactory.class)
|
||||
@Path("/api/role-delegations")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface RoleDelegationRestClient {
|
||||
|
||||
@GET
|
||||
@Path("/organisation/{orgId}")
|
||||
List<RoleDelegationResponse> listerParOrganisation(@PathParam("orgId") UUID orgId);
|
||||
|
||||
@POST
|
||||
RoleDelegationResponse creer(
|
||||
CreateRoleDelegationRequest request,
|
||||
@QueryParam("rolesDelegataire") String rolesDelegataireCsv);
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
RoleDelegationResponse revoquer(@PathParam("id") UUID id);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.AuditTrailRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean viewer audit trail (Sprint 11 ⇄ backend Sprint 10 CQRS read).
|
||||
* Pas d'écriture — la production des événements est dans le backend lifecycle.
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class AuditTrailViewerBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(AuditTrailViewerBean.class);
|
||||
|
||||
@Inject @RestClient AuditTrailRestClient client;
|
||||
|
||||
/** Mode : USER, ENTITY, ORG, SOD_VIOLATIONS, FINANCIAL */
|
||||
private String mode = "ORG";
|
||||
|
||||
private UUID userId;
|
||||
private UUID orgId;
|
||||
private String entityType;
|
||||
private UUID entityId;
|
||||
private LocalDateTime from = LocalDateTime.now().minusDays(30);
|
||||
private LocalDateTime to = LocalDateTime.now();
|
||||
|
||||
private List<AuditTrailOperationResponse> operations = Collections.emptyList();
|
||||
private AuditTrailOperationResponse selection;
|
||||
private String erreur;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
try {
|
||||
operations = switch (mode) {
|
||||
case "USER" -> userId != null
|
||||
? client.parUtilisateur(userId, from.toString(), to.toString())
|
||||
: Collections.emptyList();
|
||||
case "ENTITY" -> entityType != null && entityId != null
|
||||
? client.historique(entityType, entityId)
|
||||
: Collections.emptyList();
|
||||
case "ORG" -> orgId != null
|
||||
? client.parOrganisation(orgId)
|
||||
: Collections.emptyList();
|
||||
case "SOD_VIOLATIONS" -> client.violationsSod();
|
||||
case "FINANCIAL" -> orgId != null
|
||||
? client.operationsFinancieres(orgId, from.toString(), to.toString())
|
||||
: Collections.emptyList();
|
||||
default -> Collections.emptyList();
|
||||
};
|
||||
LOG.infof("Audit trail (%s) chargé : %d entrées", mode, operations.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Recherche audit trail échouée", e);
|
||||
operations = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurAction(String actionType) {
|
||||
if (actionType == null) return "secondary";
|
||||
return switch (actionType) {
|
||||
case "DELETE", "PAYMENT_FAILED" -> "danger";
|
||||
case "VALIDATE", "PAYMENT_CONFIRMED", "AID_REQUEST_APPROVED" -> "success";
|
||||
case "UPDATE", "PAYMENT_INITIATED", "BUDGET_APPROVED" -> "info";
|
||||
case "CREATE" -> "primary";
|
||||
case "EXPORT" -> "warning";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
public String getCouleurSod(Boolean sodCheckPassed) {
|
||||
if (sodCheckPassed == null) return "secondary";
|
||||
return sodCheckPassed ? "success" : "danger";
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur));
|
||||
}
|
||||
|
||||
public String getMode() { return mode; }
|
||||
public void setMode(String mode) { this.mode = mode; }
|
||||
public UUID getUserId() { return userId; }
|
||||
public void setUserId(UUID userId) { this.userId = userId; }
|
||||
public UUID getOrgId() { return orgId; }
|
||||
public void setOrgId(UUID orgId) { this.orgId = orgId; }
|
||||
public String getEntityType() { return entityType; }
|
||||
public void setEntityType(String entityType) { this.entityType = entityType; }
|
||||
public UUID getEntityId() { return entityId; }
|
||||
public void setEntityId(UUID entityId) { this.entityId = entityId; }
|
||||
public LocalDateTime getFrom() { return from; }
|
||||
public void setFrom(LocalDateTime from) { this.from = from; }
|
||||
public LocalDateTime getTo() { return to; }
|
||||
public void setTo(LocalDateTime to) { this.to = to; }
|
||||
public List<AuditTrailOperationResponse> getOperations() { return operations; }
|
||||
public AuditTrailOperationResponse getSelection() { return selection; }
|
||||
public void setSelection(AuditTrailOperationResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.BeneficiaireEffectifRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.CreateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.request.UpdateBeneficiaireEffectifRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.response.BeneficiaireEffectifResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean CRUD des Bénéficiaires Effectifs (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class BeneficiaireEffectifBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(BeneficiaireEffectifBean.class);
|
||||
|
||||
@Inject @RestClient BeneficiaireEffectifRestClient client;
|
||||
|
||||
// Filtres
|
||||
private UUID kycDossierId;
|
||||
private UUID organisationCibleId;
|
||||
private Boolean filtrePep;
|
||||
|
||||
private List<BeneficiaireEffectifResponse> ubos = Collections.emptyList();
|
||||
private BeneficiaireEffectifResponse selection;
|
||||
private String erreur;
|
||||
|
||||
// Formulaire de création
|
||||
private String nom;
|
||||
private String prenoms;
|
||||
private String nationalite;
|
||||
private String paysResidence;
|
||||
private String numeroPieceIdentite;
|
||||
private String pourcentageCapital;
|
||||
private String natureControle = "DETENTION_CAPITAL";
|
||||
private boolean estPep;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
try {
|
||||
ubos = client.lister(kycDossierId, organisationCibleId, filtrePep);
|
||||
LOG.infof("UBOs chargés : %d", ubos.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Recherche UBO échouée", e);
|
||||
ubos = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void creer() {
|
||||
if (kycDossierId == null && organisationCibleId == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Cible requise",
|
||||
"Sélectionnez d'abord un KycDossier ou une organisation cible");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CreateBeneficiaireEffectifRequest req = CreateBeneficiaireEffectifRequest.builder()
|
||||
.kycDossierId(kycDossierId)
|
||||
.organisationCibleId(organisationCibleId)
|
||||
.nom(nom)
|
||||
.prenoms(prenoms)
|
||||
.nationalite(nationalite != null ? nationalite.toUpperCase() : null)
|
||||
.paysResidence(paysResidence != null ? paysResidence.toUpperCase() : null)
|
||||
.numeroPieceIdentite(numeroPieceIdentite)
|
||||
.pourcentageCapital(pourcentageCapital != null && !pourcentageCapital.isBlank()
|
||||
? new java.math.BigDecimal(pourcentageCapital) : null)
|
||||
.natureControle(natureControle)
|
||||
.estPep(estPep)
|
||||
.build();
|
||||
BeneficiaireEffectifResponse created = client.creer(req);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO créé",
|
||||
created.prenoms() + " " + created.nom());
|
||||
resetForm();
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Création UBO échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void desactiverSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
client.desactiver(selection.id());
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO désactivé",
|
||||
selection.prenoms() + " " + selection.nom());
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Désactivation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void marquerPepSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
UpdateBeneficiaireEffectifRequest req = UpdateBeneficiaireEffectifRequest.builder()
|
||||
.estPep(true).build();
|
||||
client.mettreAJour(selection.id(), req);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "UBO marqué PEP", "");
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Mise à jour PEP échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetForm() {
|
||||
nom = null;
|
||||
prenoms = null;
|
||||
nationalite = null;
|
||||
paysResidence = null;
|
||||
numeroPieceIdentite = null;
|
||||
pourcentageCapital = null;
|
||||
natureControle = "DETENTION_CAPITAL";
|
||||
estPep = false;
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public UUID getKycDossierId() { return kycDossierId; }
|
||||
public void setKycDossierId(UUID kycDossierId) { this.kycDossierId = kycDossierId; }
|
||||
public UUID getOrganisationCibleId() { return organisationCibleId; }
|
||||
public void setOrganisationCibleId(UUID id) { this.organisationCibleId = id; }
|
||||
public Boolean getFiltrePep() { return filtrePep; }
|
||||
public void setFiltrePep(Boolean filtrePep) { this.filtrePep = filtrePep; }
|
||||
public List<BeneficiaireEffectifResponse> getUbos() { return ubos; }
|
||||
public BeneficiaireEffectifResponse getSelection() { return selection; }
|
||||
public void setSelection(BeneficiaireEffectifResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
public String getNom() { return nom; }
|
||||
public void setNom(String nom) { this.nom = nom; }
|
||||
public String getPrenoms() { return prenoms; }
|
||||
public void setPrenoms(String prenoms) { this.prenoms = prenoms; }
|
||||
public String getNationalite() { return nationalite; }
|
||||
public void setNationalite(String nationalite) { this.nationalite = nationalite; }
|
||||
public String getPaysResidence() { return paysResidence; }
|
||||
public void setPaysResidence(String paysResidence) { this.paysResidence = paysResidence; }
|
||||
public String getNumeroPieceIdentite() { return numeroPieceIdentite; }
|
||||
public void setNumeroPieceIdentite(String n) { this.numeroPieceIdentite = n; }
|
||||
public String getPourcentageCapital() { return pourcentageCapital; }
|
||||
public void setPourcentageCapital(String pct) { this.pourcentageCapital = pct; }
|
||||
public String getNatureControle() { return natureControle; }
|
||||
public void setNatureControle(String nc) { this.natureControle = nc; }
|
||||
public boolean isEstPep() { return estPep; }
|
||||
public void setEstPep(boolean estPep) { this.estPep = estPep; }
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.lions.unionflow.client.view;
|
||||
|
||||
import dev.lions.unionflow.client.service.RoleDelegationRestClient;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.request.CreateRoleDelegationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.delegation.response.RoleDelegationResponse;
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Bean liste/création/révocation des délégations de rôle (Sprint 11 ⇄ backend Sprint 10).
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class RoleDelegationBean implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOG = Logger.getLogger(RoleDelegationBean.class);
|
||||
|
||||
@Inject @RestClient RoleDelegationRestClient client;
|
||||
|
||||
private UUID organisationId;
|
||||
private List<RoleDelegationResponse> delegations = Collections.emptyList();
|
||||
private RoleDelegationResponse selection;
|
||||
private String erreur;
|
||||
|
||||
// Formulaire création
|
||||
private UUID delegantUserId;
|
||||
private UUID delegataireUserId;
|
||||
private String roleDelegue = "TRESORIER";
|
||||
private LocalDateTime dateDebut = LocalDateTime.now().plusHours(1);
|
||||
private LocalDateTime dateFin = LocalDateTime.now().plusDays(14);
|
||||
private String motif;
|
||||
private String rolesDelegataireCsv;
|
||||
|
||||
public void rechercher() {
|
||||
erreur = null;
|
||||
if (organisationId == null) return;
|
||||
try {
|
||||
delegations = client.listerParOrganisation(organisationId);
|
||||
LOG.infof("Délégations chargées org=%s : %d", organisationId, delegations.size());
|
||||
} catch (Exception e) {
|
||||
handleError("Chargement délégations échoué", e);
|
||||
delegations = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public void creer() {
|
||||
if (organisationId == null || delegantUserId == null || delegataireUserId == null) {
|
||||
addMessage(FacesMessage.SEVERITY_WARN, "Champs requis",
|
||||
"Organisation, déléguant et délégataire sont obligatoires");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CreateRoleDelegationRequest req = CreateRoleDelegationRequest.builder()
|
||||
.organisationId(organisationId)
|
||||
.delegantUserId(delegantUserId)
|
||||
.delegataireUserId(delegataireUserId)
|
||||
.roleDelegue(roleDelegue)
|
||||
.dateDebut(dateDebut)
|
||||
.dateFin(dateFin)
|
||||
.motif(motif)
|
||||
.build();
|
||||
RoleDelegationResponse created = client.creer(req, rolesDelegataireCsv);
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Délégation créée",
|
||||
"Rôle " + created.roleDelegue() + " jusqu'au " + created.dateFin());
|
||||
resetForm();
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Création délégation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void revoquerSelection() {
|
||||
if (selection == null) return;
|
||||
try {
|
||||
client.revoquer(selection.id());
|
||||
addMessage(FacesMessage.SEVERITY_INFO, "Délégation révoquée",
|
||||
"Rôle " + selection.roleDelegue());
|
||||
rechercher();
|
||||
} catch (Exception e) {
|
||||
handleError("Révocation échouée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCouleurStatut(String statut) {
|
||||
if (statut == null) return "secondary";
|
||||
return switch (statut) {
|
||||
case "ACTIVE" -> "success";
|
||||
case "REVOQUEE" -> "danger";
|
||||
case "EXPIREE" -> "warning";
|
||||
default -> "secondary";
|
||||
};
|
||||
}
|
||||
|
||||
private void resetForm() {
|
||||
delegantUserId = null;
|
||||
delegataireUserId = null;
|
||||
roleDelegue = "TRESORIER";
|
||||
dateDebut = LocalDateTime.now().plusHours(1);
|
||||
dateFin = LocalDateTime.now().plusDays(14);
|
||||
motif = null;
|
||||
rolesDelegataireCsv = null;
|
||||
}
|
||||
|
||||
private void handleError(String summary, Exception e) {
|
||||
LOG.warnf("%s : %s", summary, e.getMessage());
|
||||
erreur = summary + " — " + e.getMessage();
|
||||
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", erreur);
|
||||
}
|
||||
|
||||
private void addMessage(FacesMessage.Severity sev, String summary, String detail) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(sev, summary, detail));
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
public UUID getOrganisationId() { return organisationId; }
|
||||
public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
|
||||
public List<RoleDelegationResponse> getDelegations() { return delegations; }
|
||||
public RoleDelegationResponse getSelection() { return selection; }
|
||||
public void setSelection(RoleDelegationResponse selection) { this.selection = selection; }
|
||||
public String getErreur() { return erreur; }
|
||||
public UUID getDelegantUserId() { return delegantUserId; }
|
||||
public void setDelegantUserId(UUID id) { this.delegantUserId = id; }
|
||||
public UUID getDelegataireUserId() { return delegataireUserId; }
|
||||
public void setDelegataireUserId(UUID id) { this.delegataireUserId = id; }
|
||||
public String getRoleDelegue() { return roleDelegue; }
|
||||
public void setRoleDelegue(String roleDelegue) { this.roleDelegue = roleDelegue; }
|
||||
public LocalDateTime getDateDebut() { return dateDebut; }
|
||||
public void setDateDebut(LocalDateTime dateDebut) { this.dateDebut = dateDebut; }
|
||||
public LocalDateTime getDateFin() { return dateFin; }
|
||||
public void setDateFin(LocalDateTime dateFin) { this.dateFin = dateFin; }
|
||||
public String getMotif() { return motif; }
|
||||
public void setMotif(String motif) { this.motif = motif; }
|
||||
public String getRolesDelegataireCsv() { return rolesDelegataireCsv; }
|
||||
public void setRolesDelegataireCsv(String csv) { this.rolesDelegataireCsv = csv; }
|
||||
}
|
||||
Reference in New Issue
Block a user