All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 10m12s
97 lines
3.5 KiB
C#
97 lines
3.5 KiB
C#
using System.Collections.Concurrent;
|
|
using Documents.Interfaces;
|
|
using Documents.Models;
|
|
using Domain.Dtos; // QuoteDto
|
|
using Domain.Dtos.Stock; // ExpeditionDto
|
|
using Transversal.Interfaces;
|
|
|
|
public class DocumentTemplateService : IDocumentTemplateService
|
|
{
|
|
private readonly ITemplateRenderer _templateRenderer;
|
|
private readonly IPdfGeneratorService _pdfGeneratorService;
|
|
|
|
// Cache simple para no leer el logo del disco en cada render
|
|
private static readonly ConcurrentDictionary<string, string> _imageCacheBase64 = new();
|
|
|
|
public DocumentTemplateService(ITemplateRenderer templateRenderer, IPdfGeneratorService pdfGeneratorService)
|
|
{
|
|
_templateRenderer = templateRenderer;
|
|
_pdfGeneratorService = pdfGeneratorService;
|
|
}
|
|
|
|
public async Task<byte[]> GenerateDocumentAsync(DocumentGenerationRequest request)
|
|
{
|
|
if (request is null) throw new ArgumentNullException(nameof(request));
|
|
if (request.Model is null) throw new ArgumentNullException(nameof(request.Model));
|
|
|
|
string? templatePath = null;
|
|
|
|
try
|
|
{
|
|
// 1) Elegir plantilla por tipo de documento
|
|
templatePath = ResolveTemplate(request.DocumentType);
|
|
|
|
// 2) Inyectar logo (si el DTO lo soporta)
|
|
var logoBase64 = GetImageBase64Cached(
|
|
Path.Combine(Directory.GetCurrentDirectory(), "Resources", "logo.png"));
|
|
InjectLogoIfSupported(request.Model, logoBase64);
|
|
|
|
// 3) Render + PDF
|
|
var html = await _templateRenderer.RenderAsync(templatePath, request.Model);
|
|
return await _pdfGeneratorService.GeneratePdfFromHtmlAsync(html);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Envolvemos con contexto para facilitar el diagnóstico
|
|
var wrapped = new Exception(
|
|
$"Document generation failed (DocumentType={request.DocumentType}, Template='{templatePath ?? "?"}', ModelType={request.Model.GetType().FullName}). See inner exception.",
|
|
ex
|
|
);
|
|
wrapped.Data["DocumentType"] = request.DocumentType.ToString();
|
|
if (!string.IsNullOrEmpty(templatePath)) wrapped.Data["TemplatePath"] = templatePath;
|
|
wrapped.Data["ModelType"] = request.Model.GetType().FullName;
|
|
throw wrapped;
|
|
}
|
|
}
|
|
|
|
private static string ResolveTemplate(DocumentType type) => type switch
|
|
{
|
|
DocumentType.Quote => "Quotes/Template_v1.cshtml",
|
|
DocumentType.Expedition => "Expeditions/Template_v1.cshtml",
|
|
_ => "Shared/Template_Generic.cshtml"
|
|
};
|
|
|
|
private static void InjectLogoIfSupported(object model, string base64)
|
|
{
|
|
// Inyección “segura”: si el modelo expone LogoBase64, lo seteamos.
|
|
switch (model)
|
|
{
|
|
case QuoteDto q:
|
|
q.LogoBase64 = base64;
|
|
break;
|
|
case ExpeditionDto e:
|
|
e.LogoBase64 = base64;
|
|
break;
|
|
default:
|
|
// Si no tiene LogoBase64, no hacemos nada.
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static string GetImageBase64Cached(string imagePath)
|
|
{
|
|
if (_imageCacheBase64.TryGetValue(imagePath, out var cached))
|
|
return cached;
|
|
|
|
if (!File.Exists(imagePath))
|
|
{
|
|
_imageCacheBase64[imagePath] = "";
|
|
return "";
|
|
}
|
|
|
|
var base64 = Convert.ToBase64String(File.ReadAllBytes(imagePath));
|
|
_imageCacheBase64[imagePath] = base64;
|
|
return base64;
|
|
}
|
|
}
|