Files
2026-09-11 14:55:41 +02:00

111 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using SIFinance.Modules.Achats.Dtos;
using SIFinance.Shared;
using SIFinance.Shared.Entities;
namespace SIFinance.Modules.Achats.Endpoints;
public static class BudgetsEndpoints
{
public static IEndpointRouteBuilder MapBudgetsEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/budgets").WithTags("Budgets").RequireAuthorization();
// exercice optionnel : sans filtre, remonte tous les exercices connus (utile pour
// un historique) — Pilotage passera l'exercice courant pour ne charger que l'utile.
group.MapGet("", async (int? exercice, SIFinanceDbContext db, CancellationToken ct) =>
{
var query = db.BudgetsServices
.AsNoTracking()
.Include(b => b.Service)
.Include(b => b.Revisions).ThenInclude(r => r.ReviseParUtilisateur)
.AsQueryable();
if (exercice.HasValue)
query = query.Where(b => b.ExerciceAnnee == exercice.Value);
var budgets = await query.OrderBy(b => b.Service.Libelle).ToListAsync(ct);
return budgets.Select(ToDto);
});
group.MapPost("", async (CreateBudgetServiceDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) =>
{
if (!await currentUser.HasRoleAsync("FINANCE", db, ct))
return Results.Forbid();
if (input.MontantInitial <= 0)
return Results.BadRequest(new { error = "Le montant initial doit être supérieur à 0." });
var existeDeja = await db.BudgetsServices
.AnyAsync(b => b.ServiceId == input.ServiceId && b.ExerciceAnnee == input.ExerciceAnnee, ct);
if (existeDeja)
return Results.BadRequest(new { error = "Un budget existe déjà pour ce service sur cet exercice." });
var budget = new BudgetService
{
ServiceId = input.ServiceId,
ExerciceAnnee = input.ExerciceAnnee,
MontantInitial = input.MontantInitial,
SaisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct),
DateCreation = DateTime.UtcNow,
};
db.BudgetsServices.Add(budget);
await db.SaveChangesAsync(ct);
await db.Entry(budget).Reference(b => b.Service).LoadAsync(ct);
return Results.Created($"/api/budgets/{budget.Id}", ToDto(budget));
});
// Réservé à FINANCE : seul le responsable Finance qui gère le budget peut
// l'augmenter en cours d'exercice. Chaque appel ajoute une ligne à l'historique
// (budgets_service_revisions) plutôt que d'écraser une valeur unique.
group.MapPost("/{id:int}/revisions", async (int id, CreateBudgetRevisionDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) =>
{
if (!await currentUser.HasRoleAsync("FINANCE", db, ct))
return Results.Forbid();
var budget = await db.BudgetsServices
.Include(b => b.Service)
.Include(b => b.Revisions).ThenInclude(r => r.ReviseParUtilisateur)
.FirstOrDefaultAsync(b => b.Id == id, ct);
if (budget is null) return Results.NotFound();
if (input.MontantRevise <= 0)
return Results.BadRequest(new { error = "Le montant révisé doit être supérieur à 0." });
var revision = new BudgetServiceRevision
{
BudgetServiceId = id,
MontantRevise = input.MontantRevise,
Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(),
RevisePar = await currentUser.GetOrCreateCurrentUserIdAsync(ct),
DateRevision = DateTime.UtcNow,
};
db.BudgetServiceRevisions.Add(revision);
budget.DateDerniereMaj = DateTime.UtcNow;
await db.SaveChangesAsync(ct);
await db.Entry(revision).Reference(r => r.ReviseParUtilisateur).LoadAsync(ct);
budget.Revisions.Add(revision);
return Results.Created($"/api/budgets/{id}/revisions/{revision.Id}", ToDto(budget));
});
return app;
}
private static BudgetServiceDto ToDto(BudgetService b)
{
var revisions = b.Revisions
.OrderByDescending(r => r.DateRevision)
.Select(r => new BudgetRevisionDto(
r.Id, r.MontantRevise, r.Commentaire,
$"{r.ReviseParUtilisateur.Prenom} {r.ReviseParUtilisateur.Nom}".Trim(),
r.DateRevision))
.ToList();
return new BudgetServiceDto(
b.Id, b.ServiceId, b.Service.Libelle, b.ExerciceAnnee,
b.MontantInitial, revisions.Count > 0 ? revisions[0].MontantRevise : null,
revisions);
}
}