package dev.lions.unionflow.server.resource; import dev.lions.unionflow.server.service.BudgetService; import dev.lions.unionflow.server.common.ErrorResponse; import dev.lions.unionflow.server.api.dto.finance_workflow.request.CreateBudgetRequest; import dev.lions.unionflow.server.api.dto.finance_workflow.response.BudgetResponse; 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.util.List; import java.util.Map; import java.util.UUID; /** * Resource REST pour la gestion des budgets * * @author UnionFlow Team * @version 1.0 * @since 2026-03-13 */ @Path("/api/finance/budgets") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Tag(name = "Finance - Budgets", description = "Gestion des budgets organisationnels") public class BudgetResource { private static final Logger LOG = Logger.getLogger(BudgetResource.class); @Inject BudgetService budgetService; @GET @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Récupère les budgets", description = "Liste tous les budgets d'une organisation avec filtres optionnels") public Response getBudgets( @QueryParam("organizationId") UUID organizationId, @QueryParam("status") String status, @QueryParam("year") Integer year) { LOG.infof("GET /api/finance/budgets?organizationId=%s&status=%s&year=%s", organizationId, status, year); try { List budgets = budgetService.getBudgets(organizationId, status, year); return Response.ok(budgets).build(); } catch (BadRequestException 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 des budgets", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } @GET @Path("/{budgetId}") @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Récupère un budget par ID", description = "Retourne les détails complets d'un budget") public Response getBudgetById(@PathParam("budgetId") UUID budgetId) { LOG.infof("GET /api/finance/budgets/%s", budgetId); try { BudgetResponse budget = budgetService.getBudgetById(budgetId); return Response.ok(budget).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 du budget", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } @POST @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Crée un nouveau budget", description = "Crée un budget avec ses lignes budgétaires") public Response createBudget(@Valid CreateBudgetRequest request) { LOG.infof("POST /api/finance/budgets - Creating budget: %s", request.getName()); try { BudgetResponse budget = budgetService.createBudget(request); return Response.status(Response.Status.CREATED) .entity(budget) .build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND) .entity(ErrorResponse.of(e.getMessage())) .build(); } catch (BadRequestException 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 du budget", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } @GET @Path("/{budgetId}/tracking") @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Récupère le suivi budgétaire", description = "Retourne les statistiques de suivi et réalisation du budget") public Response getBudgetTracking(@PathParam("budgetId") UUID budgetId) { LOG.infof("GET /api/finance/budgets/%s/tracking", budgetId); try { Map tracking = budgetService.getBudgetTracking(budgetId); return Response.ok(tracking).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 du suivi budgétaire", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } @PUT @Path("/{budgetId}") @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Met à jour un budget", description = "Modifie un budget existant (nom, description, lignes, statut)") public Response updateBudget( @PathParam("budgetId") UUID budgetId, Map updates) { LOG.infof("PUT /api/finance/budgets/%s", budgetId); try { BudgetResponse budget = budgetService.updateBudget(budgetId, updates); return Response.ok(budget).build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND) .entity(ErrorResponse.of(e.getMessage())) .build(); } catch (BadRequestException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(ErrorResponse.of(e.getMessage())) .build(); } catch (Exception e) { LOG.error("Erreur lors de la mise à jour du budget", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } @DELETE @Path("/{budgetId}") @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"}) @Operation(summary = "Supprime un budget", description = "Supprime logiquement un budget (soft delete)") public Response deleteBudget(@PathParam("budgetId") UUID budgetId) { LOG.infof("DELETE /api/finance/budgets/%s", budgetId); try { budgetService.deleteBudget(budgetId); return Response.noContent().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 suppression du budget", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ErrorResponse.of(e.getMessage())) .build(); } } }