Premier commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
namespace SIFinance.Modules.Achats.Dtos;
|
||||
|
||||
public record BudgetRevisionDto(int Id, decimal MontantRevise, string? Commentaire, string RevisePar, DateTime DateRevision);
|
||||
|
||||
public record BudgetServiceDto(
|
||||
int Id,
|
||||
int ServiceId,
|
||||
string Service,
|
||||
int ExerciceAnnee,
|
||||
decimal MontantInitial,
|
||||
decimal? MontantRevise,
|
||||
List<BudgetRevisionDto> Revisions);
|
||||
|
||||
// L'exercice n'est pas calendaire : ExerciceAnnee=2026 désigne la période du 01/09/2026
|
||||
// au 31/08/2027 (cf. BudgetService.cs). Un budget initial est unique par service et par
|
||||
// exercice — la création échoue s'il en existe déjà un (contrainte d'unicité en base).
|
||||
public record CreateBudgetServiceDto(int ServiceId, int ExerciceAnnee, decimal MontantInitial);
|
||||
|
||||
// Chaque révision est ajoutée à l'historique, jamais écrasée — MontantRevise doit
|
||||
// représenter le nouveau plafond total, pas un delta à ajouter.
|
||||
public record CreateBudgetRevisionDto(decimal MontantRevise, string? Commentaire);
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace SIFinance.Modules.Achats.Dtos;
|
||||
|
||||
public record ContratEcheanceDto(int Id, DateOnly Date, decimal Montant, string Statut, string? JustificatifNom);
|
||||
|
||||
public record ContratDocumentDto(int Id, DateOnly Date, string Tag, string Nom);
|
||||
|
||||
// Rapprochement facture ↔ contrat : permet de voir, sur la fiche contrat, quelles factures
|
||||
// ont déjà été saisies contre lui (sans lien automatique aux échéances — le rapprochement
|
||||
// avec une échéance précise reste une vérification humaine).
|
||||
public record ContratFactureLieeDto(int Id, string Numero, decimal MontantTtc, DateOnly DateFacture, string Statut);
|
||||
|
||||
public record ContratDto(
|
||||
int Id,
|
||||
int FournisseurId,
|
||||
string Fournisseur,
|
||||
int? ServiceId,
|
||||
string? Service,
|
||||
int? CampusId,
|
||||
string? Campus,
|
||||
string? Reference,
|
||||
string Objet,
|
||||
DateOnly? DateSignature,
|
||||
DateOnly DateDebut,
|
||||
DateOnly DateFin,
|
||||
DateOnly? DateResiliation,
|
||||
string? MotifResiliation,
|
||||
decimal MontantTotal,
|
||||
string Periodicite,
|
||||
string Statut,
|
||||
List<ContratEcheanceDto> Echeances,
|
||||
List<ContratDocumentDto> Documents,
|
||||
List<ContratFactureLieeDto> FacturesLiees);
|
||||
|
||||
public record CreateContratEcheanceDto(DateOnly Date, decimal Montant);
|
||||
|
||||
public record CreateContratDto(
|
||||
int FournisseurId,
|
||||
int? ServiceId,
|
||||
int? CampusId,
|
||||
string Objet,
|
||||
string? Reference,
|
||||
DateOnly? DateSignature,
|
||||
DateOnly DateDebut,
|
||||
DateOnly DateFin,
|
||||
decimal MontantTotal,
|
||||
string Periodicite,
|
||||
List<CreateContratEcheanceDto> Echeances);
|
||||
|
||||
public record ChangerStatutContratDto(string Statut, DateOnly? DateResiliation, string? MotifResiliation, string? Commentaire);
|
||||
|
||||
public record ContratHistoriqueDto(int Id, string? AncienStatut, string NouveauStatut, string? Commentaire, string Utilisateur, DateTime DateAction);
|
||||
|
||||
public record UpdateEcheanceDto(DateOnly Date, decimal Montant, string Statut, string? JustificatifNom);
|
||||
|
||||
public record AddDocumentDto(string Tag, string Nom, DateOnly? Date);
|
||||
|
||||
public record UpdateDocumentDto(string Tag, DateOnly Date);
|
||||
@@ -0,0 +1,127 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SIFinance.Shared.Entities;
|
||||
|
||||
namespace SIFinance.Modules.Achats.Endpoints;
|
||||
|
||||
// Endpoint agrégé du tableau de bord : tous les compteurs et sommes sont calculés
|
||||
// par SQL Server (COUNT/SUM), et seules les quelques lignes réellement affichées
|
||||
// sont transférées. Le temps de réponse ne dépend donc plus du volume en base.
|
||||
public static class DashboardEndpoints
|
||||
{
|
||||
private static readonly string[] StatutsEnAttente = ["SOUMISE", "EN_VALIDATION", "COMPLEMENT_REQUIS"];
|
||||
private static readonly string[] StatutsEngages = ["VALIDEE", "FACTUREE"];
|
||||
|
||||
public static IEndpointRouteBuilder MapDashboardEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/api/dashboard").WithTags("Dashboard").RequireAuthorization();
|
||||
|
||||
group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
var today = DateTime.UtcNow.Date;
|
||||
// DateEcheance est un DateOnly côté entité : les comparaisons doivent se
|
||||
// faire avec un DateOnly pour être traduites en SQL.
|
||||
var todayDate = DateOnly.FromDateTime(today);
|
||||
|
||||
// Bornes du trimestre courant et du précédent, calculées côté C# pour que
|
||||
// la comparaison reste une simple comparaison de dates traduisible en SQL.
|
||||
var trimCourant = (today.Month - 1) / 3;
|
||||
var debutTrimestre = new DateTime(today.Year, trimCourant * 3 + 1, 1);
|
||||
var debutTrimestrePrecedent = debutTrimestre.AddMonths(-3);
|
||||
|
||||
var demandes = db.DemandesAchats.AsNoTracking();
|
||||
var factures = db.Factures.AsNoTracking();
|
||||
|
||||
// --- KPI demandes ---
|
||||
var enAttente = demandes.Where(d => StatutsEnAttente.Contains(d.Statut));
|
||||
var nbAValider = await enAttente.CountAsync(ct);
|
||||
var montantAValider = await enAttente.SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m;
|
||||
|
||||
var engagees = demandes.Where(d => StatutsEngages.Contains(d.Statut));
|
||||
var engageCourant = await engagees
|
||||
.Where(d => d.DateCreation >= debutTrimestre)
|
||||
.SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m;
|
||||
var engagePrecedent = await engagees
|
||||
.Where(d => d.DateCreation >= debutTrimestrePrecedent && d.DateCreation < debutTrimestre)
|
||||
.SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m;
|
||||
|
||||
// --- KPI factures ---
|
||||
var aPayer = factures.Where(f => f.Statut == "validee");
|
||||
var nbAPayer = await aPayer.CountAsync(ct);
|
||||
var montantAPayer = await aPayer.SumAsync(f => (decimal?)f.MontantTtc, ct) ?? 0m;
|
||||
|
||||
var enCours = factures.Where(f => f.Statut != "payee");
|
||||
var nbEchues = await enCours.CountAsync(f => f.DateEcheance < todayDate, ct);
|
||||
var nbProches = await enCours.CountAsync(f => f.DateEcheance >= todayDate && f.DateEcheance <= todayDate.AddDays(7), ct);
|
||||
|
||||
// --- Listes affichées (volumes bornés par Take) ---
|
||||
var demandesRecentes = await demandes
|
||||
.OrderByDescending(d => d.DateCreation)
|
||||
.Take(4)
|
||||
.Select(d => new DashboardDemandeDto(d.Id, d.Reference, d.Objet, d.MontantTtc, d.Statut, d.TypeDepenseId))
|
||||
.ToListAsync(ct);
|
||||
|
||||
var demandesAValiderApercu = await enAttente
|
||||
.OrderByDescending(d => d.DateCreation)
|
||||
.Take(1)
|
||||
.Select(d => new DashboardDemandeDto(d.Id, d.Reference, d.Objet, d.MontantTtc, d.Statut, d.TypeDepenseId))
|
||||
.ToListAsync(ct);
|
||||
|
||||
var echeances = await enCours
|
||||
.OrderBy(f => f.DateEcheance)
|
||||
.Take(5)
|
||||
.Select(f => new DashboardEcheanceDto(f.Id, f.NumeroFacture, f.Fournisseur.RaisonSociale, f.MontantTtc, f.DateEcheance))
|
||||
.ToListAsync(ct);
|
||||
|
||||
// Alertes : uniquement les premières lignes réellement affichées.
|
||||
var facturesEchues = await enCours
|
||||
.Where(f => f.DateEcheance < todayDate)
|
||||
.OrderBy(f => f.DateEcheance)
|
||||
.Take(5)
|
||||
.Select(f => new DashboardEcheanceDto(f.Id, f.NumeroFacture, f.Fournisseur.RaisonSociale, f.MontantTtc, f.DateEcheance))
|
||||
.ToListAsync(ct);
|
||||
|
||||
var avoirsDisponibles = await db.Avoirs
|
||||
.AsNoTracking()
|
||||
.Where(a => a.SoldeDisponible > 0)
|
||||
.OrderByDescending(a => a.DateCreation)
|
||||
.Take(5)
|
||||
.Select(a => new DashboardAvoirDto(a.Id, a.NumeroAvoir, a.Fournisseur.RaisonSociale, a.SoldeDisponible))
|
||||
.ToListAsync(ct);
|
||||
|
||||
return Results.Ok(new DashboardDto(
|
||||
engageCourant, engagePrecedent,
|
||||
nbAValider, montantAValider,
|
||||
nbAPayer, montantAPayer,
|
||||
nbProches, nbEchues,
|
||||
demandesRecentes,
|
||||
demandesAValiderApercu.FirstOrDefault(),
|
||||
facturesEchues,
|
||||
avoirsDisponibles,
|
||||
echeances));
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
public record DashboardDemandeDto(int Id, string Reference, string Objet, decimal MontantTtc, string Statut, int TypeDepenseId);
|
||||
public record DashboardEcheanceDto(int Id, string Numero, string Fournisseur, decimal MontantTtc, DateOnly DateEcheance);
|
||||
public record DashboardAvoirDto(int Id, string Numero, string Fournisseur, decimal SoldeDisponible);
|
||||
|
||||
public record DashboardDto(
|
||||
decimal EngageCourant,
|
||||
decimal EngagePrecedent,
|
||||
int NbAValider,
|
||||
decimal MontantAValider,
|
||||
int NbAPayer,
|
||||
decimal MontantAPayer,
|
||||
int NbEcheancesProches,
|
||||
int NbFacturesEchues,
|
||||
IReadOnlyList<DashboardDemandeDto> DemandesRecentes,
|
||||
DashboardDemandeDto? PremiereAValider,
|
||||
IReadOnlyList<DashboardEcheanceDto> FacturesEchues,
|
||||
IReadOnlyList<DashboardAvoirDto> AvoirsDisponibles,
|
||||
IReadOnlyList<DashboardEcheanceDto> Echeances);
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace SIFinance.Modules.Achats.Dtos;
|
||||
|
||||
public record CreateJustificatifDto(
|
||||
string Libelle, string Nom,
|
||||
string? SharepointUrl, string? SharepointItemId, long? TailleOctets);
|
||||
|
||||
public record DemandeAchatCreateDto(
|
||||
int ServiceId,
|
||||
string Objet,
|
||||
string? Description,
|
||||
int? FournisseurId,
|
||||
string? FournisseurLibre,
|
||||
string? CategorieFournisseurLibre,
|
||||
int TypeDepenseId,
|
||||
decimal MontantHt,
|
||||
decimal TauxTva,
|
||||
DateOnly DatePrevisionnelle,
|
||||
int ModePaiementId,
|
||||
List<CreateJustificatifDto>? Justificatifs);
|
||||
|
||||
public record UpdateDemandeStatutDto(string Statut);
|
||||
|
||||
// Le workflow matériel utilise des actions, plutôt qu'un statut libre envoyé par le
|
||||
// navigateur : l'API conserve ainsi le contrôle des transitions et des rôles autorisés.
|
||||
public record WorkflowDemandeDto(
|
||||
string Action,
|
||||
string? Commentaire,
|
||||
string? DocumentNom = null,
|
||||
string? DocumentSharepointUrl = null,
|
||||
string? DocumentSharepointItemId = null,
|
||||
long? DocumentTailleOctets = null);
|
||||
|
||||
public record AjouterFactureDemandeDto(
|
||||
string Numero,
|
||||
decimal Montant,
|
||||
DateOnly DateFacture,
|
||||
DateOnly DateEcheance,
|
||||
string DocumentNom,
|
||||
string DocumentSharepointUrl,
|
||||
string DocumentSharepointItemId,
|
||||
long DocumentTailleOctets);
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace SIFinance.Modules.Achats.Dtos;
|
||||
|
||||
public record DemandeJustificatifDto(int Id, string Libelle, string Nom, DateTime? DateUpload, string? SharepointUrl);
|
||||
public record DemandeWorkflowHistoriqueDto(string Action, string? Commentaire, string? AncienStatut, string? NouveauStatut, string Auteur, DateTime? DateAction);
|
||||
|
||||
public record DemandeAchatDto(
|
||||
int Id,
|
||||
string Reference,
|
||||
int ServiceId,
|
||||
string Objet,
|
||||
string? Description,
|
||||
int? FournisseurId,
|
||||
string? FournisseurLibre,
|
||||
string? CategorieFournisseurLibre,
|
||||
int TypeDepenseId,
|
||||
decimal MontantHt,
|
||||
decimal TauxTva,
|
||||
decimal MontantTva,
|
||||
decimal MontantTtc,
|
||||
DateOnly DatePrevisionnelle,
|
||||
int ModePaiementId,
|
||||
string Statut,
|
||||
int DemandeurId,
|
||||
string DemandeurNom,
|
||||
int? ValideurId,
|
||||
string? ValideurNom,
|
||||
decimal SeuilApplicable,
|
||||
DateTime? DateCreation,
|
||||
DateTime? DateSoumission,
|
||||
DateTime? DateDerniereMaj,
|
||||
List<DemandeJustificatifDto> Justificatifs,
|
||||
bool PeutValider,
|
||||
bool FactureAjoutee = false,
|
||||
string? FactureUrl = null,
|
||||
string? FactureNom = null);
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace SIFinance.Modules.Achats.Dtos;
|
||||
|
||||
public record ServiceDto(int Id, string Libelle);
|
||||
|
||||
public record TypeDepenseDto(int Id, string Libelle);
|
||||
|
||||
public record ModePaiementDto(int Id, string Libelle, bool NecessiteXml);
|
||||
|
||||
public record FournisseurDto(int Id, string RaisonSociale, string? Siret, string Statut);
|
||||
|
||||
public record FournisseurDetailDto(
|
||||
int Id, string RaisonSociale, string? Siret, string? Email, string? Telephone,
|
||||
string? Adresse, string Statut, DateTime? DateCreation);
|
||||
|
||||
public record FournisseurSaveDto(
|
||||
string RaisonSociale, string? Siret, string? Email, string? Telephone,
|
||||
string? Adresse, string Statut);
|
||||
Reference in New Issue
Block a user