feat(backend): ajout 8 endpoints manquants workflow financier

Endpoints ajoutés :
- POST /api/finance/approvals - requestApproval (avec organizationId)
- GET /api/finance/approvals/history - historique filtrable
- PUT /api/finance/budgets/{id} - updateBudget (name, description, status)
- DELETE /api/finance/budgets/{id} - deleteBudget (soft delete)
- GET /api/finance/stats - statistiques workflow global
- GET /api/finance/audit-logs - logs d'audit filtrables
- GET /api/finance/audit-logs/anomalies - détection anomalies
- POST /api/finance/audit-logs/export - export CSV/PDF

Services :
- ApprovalService.requestApproval() : logique niveaux LEVEL1/2/3 selon montant
- ApprovalService.getApprovalsHistory() : filtres date + statut
- BudgetService.updateBudget() : validation statut + approbation
- BudgetService.deleteBudget() : soft delete (actif=false)

Notes :
- Niveau approbation : <100K=NONE, 100K-1M=LEVEL1, 1M-5M=LEVEL2, >5M=LEVEL3
- organizationId optionnel dans requestApproval (pas de récup auto depuis Membre)
- FinanceWorkflowResource créé pour stats/audit (implémentation stub)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dahoud
2026-03-16 21:17:18 +00:00
parent a7bcaf9277
commit 1c096f0ee1
5 changed files with 360 additions and 0 deletions

View File

@@ -2,8 +2,10 @@ 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.Organisation;
import dev.lions.unionflow.server.entity.TransactionApproval;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
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;
@@ -40,9 +42,79 @@ public class ApprovalService {
@Inject
MembreRepository membreRepository;
@Inject
OrganisationRepository organisationRepository;
@Inject
JsonWebToken jwt;
/**
* Demande une approbation pour une transaction
*/
@Transactional
public TransactionApprovalResponse requestApproval(
UUID transactionId,
String transactionType,
Double amount,
UUID organizationId) {
LOG.infof("Demande d'approbation pour transaction %s (type: %s, montant: %.2f, org: %s)",
transactionId, transactionType, amount, organizationId);
// 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é"));
// Récupérer l'organisation si fournie
Organisation organisation = null;
if (organizationId != null) {
organisation = organisationRepository.findByIdOptional(organizationId)
.orElseThrow(() -> new NotFoundException("Organisation non trouvée: " + organizationId));
}
// Déterminer le niveau d'approbation requis selon le montant
String requiredLevel = determineRequiredLevel(amount);
// Créer la demande d'approbation
TransactionApproval approval = TransactionApproval.builder()
.transactionId(transactionId)
.transactionType(transactionType)
.amount(java.math.BigDecimal.valueOf(amount))
.currency("XOF")
.requesterId(userId)
.requesterName(membre.getNom() + " " + membre.getPrenom())
.organisation(organisation)
.requiredLevel(requiredLevel)
.status("PENDING")
.createdAt(LocalDateTime.now())
.expiresAt(LocalDateTime.now().plusDays(7)) // 7 jours par défaut
.build();
approvalRepository.persist(approval);
LOG.infof("Demande d'approbation créée avec ID: %s (niveau: %s, %d approbations requises)",
approval.getId(), requiredLevel, approval.getRequiredApprovals());
return toResponse(approval);
}
/**
* Détermine le niveau d'approbation requis selon le montant
* Utilise les niveaux standard de l'entité: NONE, LEVEL1, LEVEL2, LEVEL3
*/
private String determineRequiredLevel(Double amount) {
if (amount >= 5_000_000) { // 5M XOF
return "LEVEL3"; // 3 approbations (Board)
} else if (amount >= 1_000_000) { // 1M XOF
return "LEVEL2"; // 2 approbations (Director)
} else if (amount >= 100_000) { // 100K XOF
return "LEVEL1"; // 1 approbation (Manager)
} else {
return "NONE"; // Pas d'approbation requise
}
}
/**
* Récupère toutes les approbations en attente
*/