111 lines
5.0 KiB
C#
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);
|
|
}
|
|
}
|