Sync: code local unifié
Synchronisation du code source local (fait foi). Signed-off-by: lions dev Team
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.entity.ApproverAction;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.TransactionApproval;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.TransactionApprovalRepository;
|
||||
import dev.lions.unionflow.server.api.dto.finance_workflow.request.ApproveTransactionRequest;
|
||||
import dev.lions.unionflow.server.api.dto.finance_workflow.request.RejectTransactionRequest;
|
||||
import dev.lions.unionflow.server.api.dto.finance_workflow.response.ApproverActionResponse;
|
||||
import dev.lions.unionflow.server.api.dto.finance_workflow.response.TransactionApprovalResponse;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service métier pour la gestion des approbations de transactions
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class ApprovalService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ApprovalService.class);
|
||||
|
||||
@Inject
|
||||
TransactionApprovalRepository approvalRepository;
|
||||
|
||||
@Inject
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
JsonWebToken jwt;
|
||||
|
||||
/**
|
||||
* Récupère toutes les approbations en attente
|
||||
*/
|
||||
public List<TransactionApprovalResponse> getPendingApprovals(UUID organizationId) {
|
||||
LOG.infof("Récupération des approbations en attente pour l'organisation %s", organizationId);
|
||||
|
||||
List<TransactionApproval> approvals = organizationId != null
|
||||
? approvalRepository.findPendingByOrganisation(organizationId)
|
||||
: approvalRepository.findPending();
|
||||
|
||||
return approvals.stream()
|
||||
.map(this::toResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une approbation par ID
|
||||
*/
|
||||
public TransactionApprovalResponse getApprovalById(UUID approvalId) {
|
||||
LOG.infof("Récupération de l'approbation %s", approvalId);
|
||||
|
||||
TransactionApproval approval = approvalRepository.findByIdOptional(approvalId)
|
||||
.orElseThrow(() -> new NotFoundException("Approbation non trouvée: " + approvalId));
|
||||
|
||||
return toResponse(approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approuve une transaction
|
||||
*/
|
||||
@Transactional
|
||||
public TransactionApprovalResponse approveTransaction(UUID approvalId, ApproveTransactionRequest request) {
|
||||
LOG.infof("Approbation de la transaction %s", approvalId);
|
||||
|
||||
// Récupérer l'approbation
|
||||
TransactionApproval approval = approvalRepository.findByIdOptional(approvalId)
|
||||
.orElseThrow(() -> new NotFoundException("Approbation non trouvée: " + approvalId));
|
||||
|
||||
// Vérifier que l'approbation est en attente
|
||||
if (!"PENDING".equals(approval.getStatus())) {
|
||||
throw new ForbiddenException("Cette approbation n'est plus en attente");
|
||||
}
|
||||
|
||||
// Vérifier que l'approbation n'est pas expirée
|
||||
if (approval.isExpired()) {
|
||||
approval.setStatus("EXPIRED");
|
||||
approvalRepository.persist(approval);
|
||||
throw new ForbiddenException("Cette approbation est expirée");
|
||||
}
|
||||
|
||||
// Récupérer l'utilisateur courant
|
||||
String userEmail = jwt.getClaim("email");
|
||||
UUID userId = UUID.fromString(jwt.getClaim("sub"));
|
||||
Membre membre = membreRepository.findByEmail(userEmail)
|
||||
.orElseThrow(() -> new ForbiddenException("Utilisateur non trouvé"));
|
||||
|
||||
// Vérifier que l'utilisateur n'est pas le demandeur
|
||||
if (approval.getRequesterId().equals(userId)) {
|
||||
throw new ForbiddenException("Vous ne pouvez pas approuver votre propre demande");
|
||||
}
|
||||
|
||||
// Créer l'action d'approbation
|
||||
ApproverAction action = ApproverAction.builder()
|
||||
.approval(approval)
|
||||
.approverId(userId)
|
||||
.approverName(membre.getNom() + " " + membre.getPrenom())
|
||||
.approverRole(jwt.getClaim("role")) // Récupérer le rôle depuis JWT
|
||||
.decision("APPROVED")
|
||||
.comment(request.getComment())
|
||||
.decidedAt(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
approval.addApproverAction(action);
|
||||
|
||||
// Vérifier si toutes les approbations requises sont reçues
|
||||
if (approval.hasAllApprovals()) {
|
||||
approval.setStatus("VALIDATED");
|
||||
approval.setCompletedAt(LocalDateTime.now());
|
||||
LOG.infof("Transaction %s validée avec toutes les approbations", approval.getTransactionId());
|
||||
} else {
|
||||
approval.setStatus("APPROVED");
|
||||
LOG.infof("Transaction %s approuvée (%d/%d)",
|
||||
approval.getTransactionId(),
|
||||
approval.countApprovals(),
|
||||
approval.getRequiredApprovals());
|
||||
}
|
||||
|
||||
approvalRepository.persist(approval);
|
||||
|
||||
return toResponse(approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejette une transaction
|
||||
*/
|
||||
@Transactional
|
||||
public TransactionApprovalResponse rejectTransaction(UUID approvalId, RejectTransactionRequest request) {
|
||||
LOG.infof("Rejet de la transaction %s", approvalId);
|
||||
|
||||
// Récupérer l'approbation
|
||||
TransactionApproval approval = approvalRepository.findByIdOptional(approvalId)
|
||||
.orElseThrow(() -> new NotFoundException("Approbation non trouvée: " + approvalId));
|
||||
|
||||
// Vérifier que l'approbation est en attente
|
||||
if (!"PENDING".equals(approval.getStatus()) && !"APPROVED".equals(approval.getStatus())) {
|
||||
throw new ForbiddenException("Cette approbation ne peut plus être rejetée");
|
||||
}
|
||||
|
||||
// Récupérer l'utilisateur courant
|
||||
String userEmail = jwt.getClaim("email");
|
||||
UUID userId = UUID.fromString(jwt.getClaim("sub"));
|
||||
Membre membre = membreRepository.findByEmail(userEmail)
|
||||
.orElseThrow(() -> new ForbiddenException("Utilisateur non trouvé"));
|
||||
|
||||
// Créer l'action de rejet
|
||||
ApproverAction action = ApproverAction.builder()
|
||||
.approval(approval)
|
||||
.approverId(userId)
|
||||
.approverName(membre.getNom() + " " + membre.getPrenom())
|
||||
.approverRole(jwt.getClaim("role"))
|
||||
.decision("REJECTED")
|
||||
.comment(request.getReason())
|
||||
.decidedAt(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
approval.addApproverAction(action);
|
||||
approval.setStatus("REJECTED");
|
||||
approval.setRejectionReason(request.getReason());
|
||||
approval.setCompletedAt(LocalDateTime.now());
|
||||
|
||||
approvalRepository.persist(approval);
|
||||
|
||||
LOG.infof("Transaction %s rejetée: %s", approval.getTransactionId(), request.getReason());
|
||||
|
||||
return toResponse(approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'historique des approbations
|
||||
*/
|
||||
public List<TransactionApprovalResponse> getApprovalsHistory(
|
||||
UUID organizationId,
|
||||
LocalDateTime startDate,
|
||||
LocalDateTime endDate,
|
||||
String status) {
|
||||
LOG.infof("Récupération de l'historique des approbations pour l'organisation %s", organizationId);
|
||||
|
||||
if (organizationId == null) {
|
||||
throw new IllegalArgumentException("L'ID de l'organisation est requis");
|
||||
}
|
||||
|
||||
List<TransactionApproval> approvals = approvalRepository.findHistory(
|
||||
organizationId, startDate, endDate, status);
|
||||
|
||||
return approvals.stream()
|
||||
.map(this::toResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les approbations en attente
|
||||
*/
|
||||
public long countPendingApprovals(UUID organizationId) {
|
||||
if (organizationId == null) {
|
||||
return approvalRepository.count("status", "PENDING");
|
||||
}
|
||||
return approvalRepository.countPendingByOrganisation(organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit une entité en DTO de réponse
|
||||
*/
|
||||
private TransactionApprovalResponse toResponse(TransactionApproval approval) {
|
||||
List<ApproverActionResponse> approversResponse = approval.getApprovers().stream()
|
||||
.map(action -> ApproverActionResponse.builder()
|
||||
.id(action.getId())
|
||||
.approverId(action.getApproverId())
|
||||
.approverName(action.getApproverName())
|
||||
.approverRole(action.getApproverRole())
|
||||
.decision(action.getDecision())
|
||||
.comment(action.getComment())
|
||||
.decidedAt(action.getDecidedAt())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return TransactionApprovalResponse.builder()
|
||||
.id(approval.getId())
|
||||
.transactionId(approval.getTransactionId())
|
||||
.transactionType(approval.getTransactionType())
|
||||
.amount(approval.getAmount())
|
||||
.currency(approval.getCurrency())
|
||||
.requesterId(approval.getRequesterId())
|
||||
.requesterName(approval.getRequesterName())
|
||||
.organizationId(approval.getOrganisation() != null ? approval.getOrganisation().getId() : null)
|
||||
.requiredLevel(approval.getRequiredLevel())
|
||||
.status(approval.getStatus())
|
||||
.approvers(approversResponse)
|
||||
.rejectionReason(approval.getRejectionReason())
|
||||
.createdAt(approval.getCreatedAt())
|
||||
.expiresAt(approval.getExpiresAt())
|
||||
.completedAt(approval.getCompletedAt())
|
||||
.metadata(approval.getMetadata())
|
||||
// Champs calculés
|
||||
.approvalCount((int) approval.countApprovals())
|
||||
.requiredApprovals(approval.getRequiredApprovals())
|
||||
.hasAllApprovals(approval.hasAllApprovals())
|
||||
.isExpired(approval.isExpired())
|
||||
.isPending(approval.isPending())
|
||||
.isCompleted(approval.isCompleted())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user