Files
unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/resource/ApprovalResource.java

233 lines
10 KiB
Java

package dev.lions.unionflow.server.resource;
import dev.lions.unionflow.server.service.ApprovalService;
import dev.lions.unionflow.server.common.ErrorResponse;
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.TransactionApprovalResponse;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Resource REST pour la gestion des approbations de transactions
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Path("/api/finance/approvals")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Finance - Approvals", description = "Gestion des approbations de transactions financières")
public class ApprovalResource {
private static final Logger LOG = Logger.getLogger(ApprovalResource.class);
@Inject
ApprovalService approvalService;
@POST
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN", "MEMBRE"})
@Operation(summary = "Demande une approbation de transaction",
description = "Crée une demande d'approbation pour une transaction financière")
public Response requestApproval(Map<String, Object> request) {
LOG.infof("POST /api/finance/approvals - Request approval");
try {
String transactionId = (String) request.get("transactionId");
String transactionType = (String) request.get("transactionType");
Number rawAmount = (Number) request.get("amount");
Double amount = rawAmount != null ? rawAmount.doubleValue() : null;
String organizationIdStr = (String) request.get("organizationId");
if (transactionId == null || transactionType == null || amount == null) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(ErrorResponse.of("transactionId, transactionType et amount sont requis"))
.build();
}
UUID organizationId = organizationIdStr != null ? UUID.fromString(organizationIdStr) : null;
TransactionApprovalResponse approval = approvalService.requestApproval(
UUID.fromString(transactionId), transactionType, amount, organizationId);
return Response.status(Response.Status.CREATED).entity(approval).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (Exception e) {
LOG.error("Erreur lors de la création de la demande d'approbation", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@GET
@Path("/pending")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Récupère les approbations en attente",
description = "Liste toutes les approbations de transactions en attente pour une organisation")
public Response getPendingApprovals(@QueryParam("organizationId") UUID organizationId) {
LOG.infof("GET /api/finance/approvals/pending?organizationId=%s", organizationId);
try {
List<TransactionApprovalResponse> approvals = approvalService.getPendingApprovals(organizationId);
return Response.ok(approvals).build();
} catch (Exception e) {
LOG.error("Erreur lors de la récupération des approbations en attente", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@GET
@Path("/{approvalId}")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Récupère une approbation par ID",
description = "Retourne les détails d'une approbation spécifique")
public Response getApprovalById(@PathParam("approvalId") UUID approvalId) {
LOG.infof("GET /api/finance/approvals/%s", approvalId);
try {
TransactionApprovalResponse approval = approvalService.getApprovalById(approvalId);
return Response.ok(approval).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (Exception e) {
LOG.error("Erreur lors de la récupération de l'approbation", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@POST
@Path("/{approvalId}/approve")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Approuve une transaction",
description = "Approuve une demande de transaction avec un commentaire optionnel")
public Response approveTransaction(
@PathParam("approvalId") UUID approvalId,
@Valid ApproveTransactionRequest request) {
LOG.infof("POST /api/finance/approvals/%s/approve", approvalId);
try {
TransactionApprovalResponse approval = approvalService.approveTransaction(approvalId, request);
return Response.ok(approval).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (ForbiddenException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (Exception e) {
LOG.error("Erreur lors de l'approbation de la transaction", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@POST
@Path("/{approvalId}/reject")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Rejette une transaction",
description = "Rejette une demande de transaction avec une raison obligatoire")
public Response rejectTransaction(
@PathParam("approvalId") UUID approvalId,
@Valid RejectTransactionRequest request) {
LOG.infof("POST /api/finance/approvals/%s/reject", approvalId);
try {
TransactionApprovalResponse approval = approvalService.rejectTransaction(approvalId, request);
return Response.ok(approval).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (ForbiddenException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (Exception e) {
LOG.error("Erreur lors du rejet de la transaction", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@GET
@Path("/history")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Récupère l'historique des approbations",
description = "Liste l'historique des approbations avec filtres optionnels")
public Response getApprovalsHistory(
@QueryParam("organizationId") UUID organizationId,
@QueryParam("startDate") String startDateStr,
@QueryParam("endDate") String endDateStr,
@QueryParam("status") String status) {
LOG.infof("GET /api/finance/approvals/history?organizationId=%s&status=%s",
organizationId, status);
try {
LocalDateTime startDate = startDateStr != null ? LocalDateTime.parse(startDateStr) : null;
LocalDateTime endDate = endDateStr != null ? LocalDateTime.parse(endDateStr) : null;
List<TransactionApprovalResponse> approvals = approvalService.getApprovalsHistory(
organizationId, startDate, endDate, status);
return Response.ok(approvals).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(ErrorResponse.of(e.getMessage()))
.build();
} catch (Exception e) {
LOG.error("Erreur lors de la récupération de l'historique", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
@GET
@Path("/count/pending")
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
@Operation(summary = "Compte les approbations en attente",
description = "Retourne le nombre d'approbations en attente pour une organisation")
public Response countPendingApprovals(@QueryParam("organizationId") UUID organizationId) {
LOG.infof("GET /api/finance/approvals/count/pending?organizationId=%s", organizationId);
try {
long count = approvalService.countPendingApprovals(organizationId);
return Response.ok(new CountResponse(count)).build();
} catch (Exception e) {
LOG.error("Erreur lors du comptage des approbations", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(ErrorResponse.of(e.getMessage()))
.build();
}
}
// Classe interne pour le comptage
record CountResponse(long count) {}
}