feat(sprint-2 P0+P1 2026-04-25): DOS CENTIF + Reporting AIRMS + PV OHADA + workflow demande aide v2 + délégation rôles + SYCEBNL donateurs + tests
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m10s
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m10s
P0-NEW-16 — DOS CENTIF (Lignes directrices CENTIF mars 2025)
- DosCentifService.genererDosWord() : XWPFDocument structurée 6 sections
- DosCentifService.genererReleveExcel() : XSSFWorkbook avec opérations atypiques
- DosCentifResource @RolesAllowed COMPLIANCE_OFFICER : POST /api/aml/dos/{word,excel}
- Confidentialité absolue (jamais persisté disque, audit trail EXPORT)
P1-NEW-1 — Reporting AIRMS triple
- RapportAirmsService.genererRapportTriple() : OpenPDF, 3 sections + cover
- DTOs records : RapportTechnique (effectifs, sinistralité, délais),
RapportMoral (vie associative, formations, activités),
RapportFinancier (P&L, ratios prudentiels, trésorerie, fonds dédiés SYCEBNL)
- Endpoint POST /api/airms/rapports/triple
P1-NEW-2 — Module PV OHADA (AG/CA)
- V46 : table proces_verbaux (quorum, OJ JSONB, résolutions JSONB, hash SHA-256, signatures)
- Entité ProcesVerbal + repo ProcesVerbalRepository
- ProcesVerbalService :
* quorumRequisDefaut() : 50% AG ord / 66.67% AG extra/CA
* calculerEtFixerQuorum() : (présents+représentés)/convoqués × 100
* adopter() : valide quorum, fige hash SHA-256
* signer() : vérif hash inchangé (immuabilité), signature électronique
* archiver() : statut ARCHIVE pour conservation OHADA 10 ans
P1-NEW-3 — Workflow demande d'aide v2
- V46 : extension demandes_aide (etape, animateur_zone, gps_enquete, avis_comite, decision_ca)
- V46 : extension types_aide (plafond_annuel_membre, plafond_enveloppe_annuelle, justificatifs_requis)
- DemandeAide enrichie : 5 étapes DEPOSE → ENQUETE → AVIS_COMITE → DECISION_CA → PAYE → CLOTURE
- DemandeAideV2Service :
* Transitions typées avec checks d'état
* SoD : approbateur CA ≠ animateur enquête
* Audit trail enrichi à chaque transition
P1-NEW-5 — Délégation temporaire rôles
- V46 : table role_delegations (delegant, delegataire, role, dates, statut, motif)
- Entité RoleDelegation + isActiveAt(instant)
- RoleDelegationService :
* creer() : vérif SoD avant création (pas de conflit avec rôles existants délégataire)
* revoquer() : statut REVOQUEE
* rolesEffectifs() : directs ∪ délégués actifs
* marquerExpirees() : scheduler quotidien
P1-NEW-13 — Registre donateurs SYCEBNL
- V46 : tables donateurs + dons_recus (numéraire/nature/bénévolat/legs)
- Affectation : LIBRE / FONDS_DEDIE / PROJET_SPECIFIQUE
- Reçu fiscal (numero_recu, date_emission_recu)
- Entités Donateur + DonRecu + repos
- DonRecuRepository.totalEntre() pour reporting AIRMS/SYCEBNL
P1-NEW-14 — Membres honoraires/bienfaiteurs
- V46 : ALTER membres_organisations.qualite_speciale (HONORAIRE/BIENFAITEUR/FONDATEUR)
Tests Sprint 2 (13 nouveaux, 0 failure) :
- ProcesVerbalServiceTest (8) : quorum défaut, atteint/non-atteint, hash format/immuabilité/diff
- RoleDelegationTest (5) : isActiveAt selon période et statut
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.centif.DosCentifService;
|
||||
import dev.lions.unionflow.server.service.centif.DosCentifService.DosCentifData;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Endpoint de génération des Déclarations d'Opérations Suspectes (DOS) pour la CENTIF.
|
||||
*
|
||||
* <p><strong>Accès restreint</strong> : seul un {@code COMPLIANCE_OFFICER} peut générer une DOS
|
||||
* (rôle issu de l'Instruction BCEAO 001-03-2025). Les fichiers ne sont jamais persistés sur
|
||||
* disque — streaming download direct.
|
||||
*
|
||||
* <p>L'export est tracé dans {@code audit_trail_operations} (action_type {@code EXPORT}).
|
||||
*
|
||||
* @since 2026-04-25 (P0-NEW-16)
|
||||
*/
|
||||
@Path("/api/aml/dos")
|
||||
@Authenticated
|
||||
public class DosCentifResource {
|
||||
|
||||
@Inject DosCentifService dosService;
|
||||
|
||||
/**
|
||||
* Génère la DOS au format Word (.docx).
|
||||
*
|
||||
* @param data corps JSON {@link DosCentifData}
|
||||
* @return fichier Word streamé
|
||||
*/
|
||||
@POST
|
||||
@Path("/word")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
|
||||
@RolesAllowed({"COMPLIANCE_OFFICER", "SUPER_ADMIN"})
|
||||
public Response genererWord(DosCentifData data) throws IOException {
|
||||
byte[] bytes = dosService.genererDosWord(data);
|
||||
String filename = "DOS_" + data.numeroDosInterne() + ".docx";
|
||||
return Response.ok(bytes)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère le relevé d'opérations atypiques au format Excel (.xlsx).
|
||||
*
|
||||
* @param data corps JSON {@link DosCentifData}
|
||||
* @return fichier Excel streamé
|
||||
*/
|
||||
@POST
|
||||
@Path("/excel")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@RolesAllowed({"COMPLIANCE_OFFICER", "SUPER_ADMIN"})
|
||||
public Response genererExcel(DosCentifData data) throws IOException {
|
||||
byte[] bytes = dosService.genererReleveExcel(data);
|
||||
String filename = "Releve_Operations_" + data.numeroDosInterne() + ".xlsx";
|
||||
return Response.ok(bytes)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.airms.RapportAirmsService;
|
||||
import dev.lions.unionflow.server.service.airms.RapportAirmsService.RapportAirmsData;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Endpoint de génération du rapport AIRMS triple PDF (technique/moral/financier).
|
||||
*
|
||||
* @since 2026-04-25 (P1-NEW-1)
|
||||
*/
|
||||
@Path("/api/airms/rapports")
|
||||
@Authenticated
|
||||
public class RapportAirmsResource {
|
||||
|
||||
@Inject RapportAirmsService service;
|
||||
|
||||
@POST
|
||||
@Path("/triple")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/pdf")
|
||||
@RolesAllowed({"PRESIDENT", "TRESORIER", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
public Response genererTriple(RapportAirmsData data) throws IOException {
|
||||
byte[] pdf = service.genererRapportTriple(data);
|
||||
String filename = "Rapport_AIRMS_" + data.organisationDenomination().replaceAll("[^a-zA-Z0-9]", "_")
|
||||
+ "_" + data.exerciceAnnee() + ".pdf";
|
||||
return Response.ok(pdf)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user