Renommage : TrajetKmTracker -> SchotzIadKmTracker (backend, specs, frontend)
- Backend : projets, solution, namespaces et références TrajetKmTracker.Api/.Tests -> SchotzIadKmTracker.Api/.Tests (dossiers, .csproj, .sln, .http, namespaces C#). GUID UserSecretsId préservé. - Specs : dossier .kiro/specs/trajet-km-tracker -> schotziad-km-tracker et mise à jour des références internes (chemins, tags de propriétés). - Frontend : projet Angular trajet-km-tracker -> schotziad-km-tracker (package.json, angular.json, chemin dist, Dockerfile), titre applicatif 'SchotzIAD KM Tracker'. - Docker et scripts (backend/Dockerfile, frontend/Dockerfile, run-dev.sh) mis à jour. - Suppression du fichier résiduel package.json.bak. Backend (solution) et frontend (build production) compilent sans erreur.
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System.Net.Mime;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SchotzIadKmTracker.Api.Services;
|
||||
using SchotzIadKmTracker.Api.Services.Exceptions;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoints d'Export_PDF : rapport mensuel et synthèse annuelle (Exigences 7.3, 7.4, 7.5).
|
||||
/// Chaque réponse renvoie un unique fichier <c>application/pdf</c> avec l'en-tête
|
||||
/// <c>Content-Disposition: attachment</c> et le nom de fichier suggéré par le service
|
||||
/// (<c>trajets_MM_AAAA.pdf</c> pour le mensuel, <c>trajets_AAAA.pdf</c> pour l'annuel).
|
||||
///
|
||||
/// Le filtre par véhicule est optionnel : si <c>vehiculeId</c> est omis, le Véhicule_Courant
|
||||
/// est utilisé par défaut.
|
||||
///
|
||||
/// <para>
|
||||
/// Note (Exigence 7.9) : une réponse HTTP unique ne peut transporter qu'un seul fichier.
|
||||
/// Ces endpoints produisent donc le rapport du véhicule sélectionné (ou courant). La génération
|
||||
/// d'un rapport distinct par véhicule lorsqu'une période comporte plusieurs véhicules est assurée
|
||||
/// par les variantes de service <c>GenererRapports…ParVehiculeAsync</c>, réservées à un usage
|
||||
/// multi-fichiers ultérieur (p. ex. archive). Ici, un appel = un fichier filtré par véhicule.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Erreurs : un dépassement du délai de génération ou un échec de rendu lève une
|
||||
/// <see cref="PdfGenerationException"/> mappée vers <c>503 Service Unavailable</c> ; un véhicule
|
||||
/// introuvable lève une <see cref="VehiculeNotFoundException"/> mappée vers <c>404 Not Found</c>
|
||||
/// (réponses ProblemDetails).
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/exports")]
|
||||
public class ExportsPdfController : ControllerBase
|
||||
{
|
||||
private readonly IPdfService _pdfService;
|
||||
private readonly IVehiculeService _vehiculeService;
|
||||
|
||||
public ExportsPdfController(IPdfService pdfService, IVehiculeService vehiculeService)
|
||||
{
|
||||
_pdfService = pdfService;
|
||||
_vehiculeService = vehiculeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère et télécharge le rapport PDF mensuel d'un (année, mois) pour le véhicule indiqué
|
||||
/// (Véhicule_Courant si <paramref name="vehiculeId"/> est omis). Retourne les octets du PDF
|
||||
/// avec <c>Content-Type: application/pdf</c> et <c>Content-Disposition: attachment;
|
||||
/// filename="trajets_MM_AAAA.pdf"</c> (Exigences 7.3, 7.4).
|
||||
/// </summary>
|
||||
[HttpGet("mensuel/{annee:int}/{mois:int}")]
|
||||
[Produces(MediaTypeNames.Application.Pdf)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
|
||||
public async Task<IActionResult> ExporterMensuel(
|
||||
int annee, int mois, [FromQuery] int? vehiculeId, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var vehicule = await ResoudreVehiculeIdAsync(vehiculeId, cancellationToken);
|
||||
var pdf = await _pdfService.GenererRapportMensuelAsync(annee, mois, vehicule, cancellationToken);
|
||||
return FichierPdf(pdf);
|
||||
}
|
||||
catch (VehiculeNotFoundException ex)
|
||||
{
|
||||
return Problem(detail: ex.Message, statusCode: StatusCodes.Status404NotFound);
|
||||
}
|
||||
catch (PdfGenerationException ex)
|
||||
{
|
||||
return Problem(detail: ex.Message, statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère et télécharge la synthèse PDF annuelle d'une année pour le véhicule indiqué
|
||||
/// (Véhicule_Courant si <paramref name="vehiculeId"/> est omis). Retourne les octets du PDF
|
||||
/// avec <c>Content-Type: application/pdf</c> et <c>Content-Disposition: attachment;
|
||||
/// filename="trajets_AAAA.pdf"</c> (Exigences 7.3, 7.4).
|
||||
/// </summary>
|
||||
[HttpGet("annuel/{annee:int}")]
|
||||
[Produces(MediaTypeNames.Application.Pdf)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
|
||||
public async Task<IActionResult> ExporterAnnuel(
|
||||
int annee, [FromQuery] int? vehiculeId, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var vehicule = await ResoudreVehiculeIdAsync(vehiculeId, cancellationToken);
|
||||
var pdf = await _pdfService.GenererRapportAnnuelAsync(annee, vehicule, cancellationToken);
|
||||
return FichierPdf(pdf);
|
||||
}
|
||||
catch (VehiculeNotFoundException ex)
|
||||
{
|
||||
return Problem(detail: ex.Message, statusCode: StatusCodes.Status404NotFound);
|
||||
}
|
||||
catch (PdfGenerationException ex)
|
||||
{
|
||||
return Problem(detail: ex.Message, statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Résout l'identifiant du véhicule à utiliser : celui fourni s'il est présent, sinon
|
||||
/// le Véhicule_Courant (Date_Effet la plus récente <= aujourd'hui).
|
||||
/// </summary>
|
||||
private async Task<int> ResoudreVehiculeIdAsync(int? vehiculeId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (vehiculeId.HasValue)
|
||||
{
|
||||
return vehiculeId.Value;
|
||||
}
|
||||
|
||||
var courant = await _vehiculeService.GetVehiculeCourantAsync(cancellationToken);
|
||||
return courant.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renvoie le PDF en pièce jointe téléchargeable, en s'appuyant sur le nom de fichier
|
||||
/// suggéré par le <see cref="PdfResult"/> (<c>Content-Disposition: attachment</c>).
|
||||
/// </summary>
|
||||
private FileContentResult FichierPdf(PdfResult pdf)
|
||||
=> File(pdf.Contenu, MediaTypeNames.Application.Pdf, pdf.NomFichier);
|
||||
}
|
||||
Reference in New Issue
Block a user