feat(sprint-5 P2-NEW-3 2026-04-25): reporting trimestriel ControleurInterne automatisé + scheduler + tests
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m26s
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m26s
Rapport trimestriel agrégé pour le Contrôleur Interne (source AG + inspections BCEAO/ARTCI).
Entités & migration
- RapportTrimestrielControleurInterne (BaseEntity + JSONB contenu + bytea PDF + hash SHA-256)
- V48 : table rapports_trimestriels_controleur_interne, contraintes, indexes
- Repository : trouverParOrgAnneeTrimestre, listerParOrgAnnee, listerNonSignes
Service RapportTrimestrielService
- genererRapport(orgId, annee, trimestre) : agrège ComplianceSnapshot + DOS CENTIF + formations LBC/FT + anomalies SoD audit trail + demandes aide
- construireJson : structure conformite/activite/alertes
- genererPdf : OpenPDF A4 avec sections 1.Conformité 2.Activité 3.Alertes + bloc signature
- signer(rapportId, signataireId) : calcule SHA-256 du JSON, fige le statut
- archiver(rapportId) : passe SIGNE → ARCHIVE
- Idempotent en DRAFT (régénération possible) ; immuable en SIGNE/ARCHIVE
Resource RapportTrimestrielResource
- GET /api/rapports/trimestriel?orgId&annee — lister
- POST /api/rapports/trimestriel/generer — CONTROLEUR_INTERNE / SUPER_ADMIN
- POST /api/rapports/trimestriel/{id}/signer — CONTROLEUR_INTERNE
- POST /api/rapports/trimestriel/{id}/archiver — CONTROLEUR_INTERNE / PRESIDENT
- GET /api/rapports/trimestriel/{id}/pdf — application/pdf
Scheduler RapportTrimestrielScheduler
- @Scheduled cron 0 17 2 1 1,4,7,10 ? — 1er jan/avr/jul/oct à 02:17
- Génère pour toutes les orgs actives le trimestre précédent
- Override possible via unionflow.reporting.trimestriel.cron
- ConcurrentExecution.SKIP
RoleConstant
- Ajout CONTROLEUR_INTERNE, COMPLIANCE_OFFICER, COMMISSAIRE_COMPTES (utilisés depuis V45)
Tests Sprint 5 (20/20 verts)
- RapportTrimestrielServiceTest : 15 tests (debutTrimestre, finTrimestre, hash SHA-256, JSON alertes, PDF non vide)
- RapportTrimestrielSchedulerTest : 5 tests (trimestrePrecedent — incluant rollover année)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.entity.RapportTrimestrielControleurInterne;
|
||||
import dev.lions.unionflow.server.repository.RapportTrimestrielControleurInterneRepository;
|
||||
import dev.lions.unionflow.server.security.OrganisationContextHolder;
|
||||
import dev.lions.unionflow.server.service.reporting.RapportTrimestrielService;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
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 jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Endpoints du Reporting trimestriel ControleurInterne (P2-NEW-3).
|
||||
*
|
||||
* <p>Accès restreint au {@code CONTROLEUR_INTERNE} de l'organisation et au {@code SUPER_ADMIN}.
|
||||
* La sélection de l'organisation active passe par le filtre {@link OrganisationContextHolder}
|
||||
* (header {@code X-Active-Organisation-Id}) ou via paramètre {@code orgId} pour SUPER_ADMIN.
|
||||
*/
|
||||
@Path("/api/rapports/trimestriel")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Authenticated
|
||||
public class RapportTrimestrielResource {
|
||||
|
||||
@Inject RapportTrimestrielService service;
|
||||
@Inject RapportTrimestrielControleurInterneRepository repository;
|
||||
@Inject OrganisationContextHolder orgContext;
|
||||
|
||||
@GET
|
||||
@RolesAllowed({"CONTROLEUR_INTERNE", "PRESIDENT", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
public List<RapportTrimestrielControleurInterne> lister(
|
||||
@QueryParam("orgId") UUID orgId,
|
||||
@QueryParam("annee") Integer annee) {
|
||||
UUID effectiveOrg = orgId != null ? orgId : orgContext.getOrganisationId();
|
||||
int effectiveAnnee = annee != null ? annee : java.time.Year.now().getValue();
|
||||
return repository.listerParOrgAnnee(effectiveOrg, effectiveAnnee);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/generer")
|
||||
@RolesAllowed({"CONTROLEUR_INTERNE", "SUPER_ADMIN"})
|
||||
public RapportTrimestrielControleurInterne generer(
|
||||
@QueryParam("orgId") UUID orgId,
|
||||
@QueryParam("annee") int annee,
|
||||
@QueryParam("trimestre") int trimestre) {
|
||||
UUID effectiveOrg = orgId != null ? orgId : orgContext.getOrganisationId();
|
||||
return service.genererRapport(effectiveOrg, annee, trimestre);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{id}/signer")
|
||||
@RolesAllowed({"CONTROLEUR_INTERNE", "SUPER_ADMIN"})
|
||||
public RapportTrimestrielControleurInterne signer(
|
||||
@PathParam("id") UUID id, @QueryParam("signataireId") UUID signataireId) {
|
||||
return service.signer(id, signataireId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{id}/archiver")
|
||||
@RolesAllowed({"CONTROLEUR_INTERNE", "PRESIDENT", "SUPER_ADMIN"})
|
||||
public RapportTrimestrielControleurInterne archiver(@PathParam("id") UUID id) {
|
||||
return service.archiver(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}/pdf")
|
||||
@Produces("application/pdf")
|
||||
@RolesAllowed({"CONTROLEUR_INTERNE", "PRESIDENT", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
public Response telechargerPdf(@PathParam("id") UUID id) {
|
||||
RapportTrimestrielControleurInterne r = repository.findById(id);
|
||||
if (r == null || r.getPdfBytes() == null) {
|
||||
throw new NotFoundException("Rapport ou PDF introuvable : " + id);
|
||||
}
|
||||
String filename = String.format("rapport-trim-%d-T%d.pdf", r.getAnnee(), r.getTrimestre());
|
||||
return Response.ok(r.getPdfBytes())
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user