using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using SIFinance.Modules.Finance.Dtos; using SIFinance.Shared; using SIFinance.Shared.Entities; namespace SIFinance.Modules.Finance.Endpoints; public static class PaiementsEndpoints { public static IEndpointRouteBuilder MapPaiementsEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup("/api/paiements").WithTags("Paiements").RequireAuthorization(); group.MapGet("/factures-disponibles", async (SIFinanceDbContext db, CancellationToken ct) => { var facturesPayeesIds = db.Paiements.Select(p => p.FactureId); return await db.Factures .AsNoTracking() .Include(f => f.Fournisseur) .Where(f => f.Statut == "validee" && !facturesPayeesIds.Contains(f.Id)) .OrderBy(f => f.DateFacture) .Select(f => new FactureDisponibleDto(f.Id, f.FournisseurId, f.Fournisseur.RaisonSociale, f.NumeroFacture, f.MontantTtc, f.DateFacture)) .ToListAsync(ct); }); group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => { var paiements = await db.Paiements .AsNoTracking() .Include(p => p.Facture).ThenInclude(f => f.Fournisseur) .Include(p => p.ModeReglement) .ToListAsync(ct); return paiements .GroupBy(p => p.ReferencePaiement ?? $"PAI-{p.Id}") .Select(g => { var first = g.First(); return new PaiementGroupeDto( g.Key, first.Facture.FournisseurId, first.Facture.Fournisseur.RaisonSociale, g.Select(p => p.FactureId).ToList(), g.Select(p => p.Facture.NumeroFacture).ToList(), g.Sum(p => p.MontantPaye), first.ModeReglementId, first.ModeReglement.Libelle, first.DatePaiement, first.Statut, first.Rapproche); }) .OrderByDescending(p => p.DatePaiement); }); group.MapPost("", async (CreatePaiementDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => { if (input.FactureIds.Count == 0) return Results.BadRequest(new { error = "Sélectionner au moins une facture." }); if (string.IsNullOrWhiteSpace(input.Reference)) return Results.BadRequest(new { error = "La référence de paiement est requise." }); var factures = await db.Factures.Where(f => input.FactureIds.Contains(f.Id)).ToListAsync(ct); if (factures.Count != input.FactureIds.Count) return Results.BadRequest(new { error = "Une ou plusieurs factures sont introuvables." }); if (factures.Select(f => f.FournisseurId).Distinct().Count() > 1) return Results.BadRequest(new { error = "Un paiement regroupe uniquement des factures d'un même fournisseur." }); var dejaCouvertes = await db.Paiements.AnyAsync(p => input.FactureIds.Contains(p.FactureId), ct); if (dejaCouvertes) return Results.BadRequest(new { error = "Une ou plusieurs factures sont déjà couvertes par un paiement." }); var referenceExistante = await db.Paiements.AnyAsync(p => p.ReferencePaiement == input.Reference, ct); if (referenceExistante) return Results.BadRequest(new { error = "Cette référence de paiement existe déjà." }); var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); var lignes = factures.Select(f => new Paiement { FactureId = f.Id, MontantPaye = f.MontantTtc, DatePaiement = input.DatePaiement, ModeReglementId = input.ModePaiementId, ReferencePaiement = input.Reference, EnregistreParId = currentUserId, DateSaisie = DateTime.UtcNow, Statut = input.Statut, Rapproche = input.Rapproche, }).ToList(); db.Paiements.AddRange(lignes); try { await db.SaveChangesAsync(ct); } catch (DbUpdateException ex) { return Results.BadRequest(new { error = "Impossible d'enregistrer le paiement.", detail = ex.InnerException?.Message ?? ex.Message }); } return Results.Created($"/api/paiements/{Uri.EscapeDataString(input.Reference)}", (object?)null); }); group.MapPatch("/{reference}", async (string reference, UpdatePaiementDto input, SIFinanceDbContext db, CancellationToken ct) => { var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); if (lignes.Count == 0) return Results.NotFound(); foreach (var p in lignes) { p.ModeReglementId = input.ModePaiementId; p.DatePaiement = input.DatePaiement; p.ReferencePaiement = input.Reference; p.Statut = input.Statut; p.Rapproche = input.Rapproche; } await db.SaveChangesAsync(ct); return Results.NoContent(); }); group.MapPatch("/{reference}/rapproche", async (string reference, SIFinanceDbContext db, CancellationToken ct) => { var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); if (lignes.Count == 0) return Results.NotFound(); var nouvelEtat = !lignes[0].Rapproche; foreach (var p in lignes) p.Rapproche = nouvelEtat; await db.SaveChangesAsync(ct); return Results.Ok(new { rapproche = nouvelEtat }); }); group.MapDelete("/{reference}", async (string reference, SIFinanceDbContext db, CancellationToken ct) => { var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); if (lignes.Count == 0) return Results.NotFound(); db.Paiements.RemoveRange(lignes); await db.SaveChangesAsync(ct); return Results.NoContent(); }); return app; } }