feat(versement): nouveau module Versement (paiements rattachés à des objets)
- Entités : Versement, VersementObjet (lien polymorphique vers cotisation/adhesion/etc.) - VersementRepository : requêtes par membre, org, période - VersementResource : endpoints REST /api/versements - VersementService : logique métier (validation, rattachement objets) - Migration V27 : ajout numeroTelephone sur versements
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.versement.request.DeclarerVersementManuelRequest;
|
||||
import dev.lions.unionflow.server.api.dto.versement.request.InitierDepotEpargneRequest;
|
||||
import dev.lions.unionflow.server.api.dto.versement.request.InitierVersementWaveRequest;
|
||||
import dev.lions.unionflow.server.api.dto.versement.response.VersementGatewayResponse;
|
||||
import dev.lions.unionflow.server.api.dto.versement.response.VersementResponse;
|
||||
import dev.lions.unionflow.server.api.dto.versement.response.VersementStatutResponse;
|
||||
import dev.lions.unionflow.server.api.dto.versement.response.VersementSummaryResponse;
|
||||
import dev.lions.unionflow.server.service.VersementService;
|
||||
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 java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des versements.
|
||||
*
|
||||
* <p>Endpoints principaux :
|
||||
* <ul>
|
||||
* <li>{@code POST /api/versements/initier-wave} — démarre le flux Wave deep link</li>
|
||||
* <li>{@code GET /api/versements/statut/{intentionId}} — retour deep link / polling</li>
|
||||
* <li>{@code POST /api/versements/declarer-manuel} — déclaration espèces/virement/chèque</li>
|
||||
* <li>{@code GET /api/versements/mes-versements} — historique du membre connecté</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 4.0
|
||||
* @since 2026-04-13
|
||||
*/
|
||||
@Path("/api/versements")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER"})
|
||||
@Tag(name = "Versements", description = "Versements de cotisations — Wave et manuel")
|
||||
public class VersementResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(VersementResource.class);
|
||||
|
||||
@Inject
|
||||
VersementService versementService;
|
||||
|
||||
// ── Lecture ───────────────────────────────────────────────────────────────
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
public Response trouverParId(@PathParam("id") UUID id) {
|
||||
LOG.infof("GET /api/versements/%s", id);
|
||||
VersementResponse result = versementService.trouverParId(id);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/reference/{numeroReference}")
|
||||
public Response trouverParNumeroReference(
|
||||
@PathParam("numeroReference") String numeroReference) {
|
||||
LOG.infof("GET /api/versements/reference/%s", numeroReference);
|
||||
VersementResponse result = versementService.trouverParNumeroReference(numeroReference);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
public Response listerParMembre(@PathParam("membreId") UUID membreId) {
|
||||
LOG.infof("GET /api/versements/membre/%s", membreId);
|
||||
List<VersementSummaryResponse> result = versementService.listerParMembre(membreId);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/mes-versements")
|
||||
@RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response getMesVersements(
|
||||
@QueryParam("limit") @DefaultValue("20") int limit) {
|
||||
LOG.infof("GET /api/versements/mes-versements?limit=%d", limit);
|
||||
List<VersementSummaryResponse> result = versementService.getMesVersements(limit);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
// ── Validation / Annulation ───────────────────────────────────────────────
|
||||
|
||||
@POST
|
||||
@Path("/{id}/valider")
|
||||
@RolesAllowed({"ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response validerVersement(@PathParam("id") UUID id) {
|
||||
LOG.infof("POST /api/versements/%s/valider", id);
|
||||
VersementResponse result = versementService.validerVersement(id);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{id}/annuler")
|
||||
@RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
public Response annulerVersement(@PathParam("id") UUID id) {
|
||||
LOG.infof("POST /api/versements/%s/annuler", id);
|
||||
VersementResponse result = versementService.annulerVersement(id);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
// ── Flux Wave ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Initie un versement Wave.
|
||||
*
|
||||
* <p>Le mobile appelle cet endpoint puis ouvre le {@code waveLaunchUrl} retourné
|
||||
* avec {@code url_launcher}. Wave s'ouvre avec le montant et le numéro pré-remplis.
|
||||
* Après confirmation, Wave redirige vers
|
||||
* {@code unionflow://payment?result=success&ref={clientReference}}.
|
||||
*/
|
||||
@POST
|
||||
@Path("/initier-wave")
|
||||
@RolesAllowed({"MEMBRE", "MEMBRE_ACTIF", "ADMIN", "ADMIN_ORGANISATION", "USER"})
|
||||
public Response initierVersementWave(@Valid InitierVersementWaveRequest request) {
|
||||
LOG.infof("POST /api/versements/initier-wave — cotisation: %s", request.cotisationId());
|
||||
VersementGatewayResponse result = versementService.initierVersementWave(request);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retour deep link / polling du statut d'un versement Wave.
|
||||
*
|
||||
* <p>Appelé par le mobile au retour du deep link
|
||||
* {@code unionflow://payment?result=success&ref={intentionId}}
|
||||
* pour confirmer que le paiement est bien enregistré côté UnionFlow.
|
||||
* Également utilisé par le web en polling toutes les 3 secondes.
|
||||
*/
|
||||
@GET
|
||||
@Path("/statut/{intentionId}")
|
||||
@RolesAllowed({"MEMBRE", "MEMBRE_ACTIF", "ADMIN", "ADMIN_ORGANISATION", "USER"})
|
||||
public Response getStatutVersement(@PathParam("intentionId") UUID intentionId) {
|
||||
LOG.infof("GET /api/versements/statut/%s", intentionId);
|
||||
VersementStatutResponse result = versementService.verifierStatutVersement(intentionId);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
// ── Flux manuel ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Déclare un versement manuel (espèces, virement, chèque).
|
||||
* Le versement est créé avec le statut EN_ATTENTE_VALIDATION.
|
||||
* Le trésorier devra le valider via la page admin.
|
||||
*/
|
||||
@POST
|
||||
@Path("/declarer-manuel")
|
||||
@RolesAllowed({"MEMBRE", "ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response declarerVersementManuel(@Valid DeclarerVersementManuelRequest request) {
|
||||
LOG.infof("POST /api/versements/declarer-manuel — cotisation: %s, méthode: %s",
|
||||
request.cotisationId(), request.methodePaiement());
|
||||
VersementResponse result = versementService.declarerVersementManuel(request);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
}
|
||||
|
||||
// ── Dépôt épargne ─────────────────────────────────────────────────────────
|
||||
|
||||
@POST
|
||||
@Path("/initier-depot-epargne")
|
||||
@RolesAllowed({"MEMBRE", "MEMBRE_ACTIF", "ADMIN", "ADMIN_ORGANISATION", "USER"})
|
||||
public Response initierDepotEpargne(@Valid InitierDepotEpargneRequest request) {
|
||||
LOG.infof("POST /api/versements/initier-depot-epargne — compte: %s, montant: %s",
|
||||
request.compteId(), request.montant());
|
||||
VersementGatewayResponse result = versementService.initierDepotEpargneEnLigne(request);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user