Compare commits
2 Commits
2ff2a0377b
...
684036875c
| Author | SHA1 | Date | |
|---|---|---|---|
| 684036875c | |||
| e7069b3848 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -423,3 +423,4 @@ FodyWeavers.xsd
|
||||
/Domain/obj/Domain.csproj.nuget.g.props
|
||||
/Models/obj/Models.csproj.nuget.g.props
|
||||
/phronCare.API/obj/Debug/net8.0/ApiEndpoints.json
|
||||
/phronCare.API/.local-chromium/Win64-884014/chrome-win
|
||||
|
||||
@ -16,4 +16,7 @@
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore\8.0.10\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore\8.0.10\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\maski\.nuget\packages\newtonsoft.json\10.0.3</PkgNewtonsoft_Json>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.1</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.1</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
|
||||
26
Transversal/Interfaces/IPdfGeneratorService.cs
Normal file
26
Transversal/Interfaces/IPdfGeneratorService.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Transversal.Models;
|
||||
|
||||
namespace Transversal.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Define el contrato para un servicio generador de PDFs a partir de contenido HTML.
|
||||
/// </summary>
|
||||
public interface IPdfGeneratorService
|
||||
{
|
||||
/// <summary>
|
||||
/// Genera un documento PDF a partir de una cadena HTML.
|
||||
/// </summary>
|
||||
/// <param name="htmlContent">
|
||||
/// Contenido HTML completo (incluyendo etiquetas <html>, <head>, <body>, etc.) que se desea convertir a PDF.
|
||||
/// </param>
|
||||
/// <param name="options">
|
||||
/// Opcional: configuración personalizada para el documento PDF (tamaño de papel, orientación, márgenes, encabezados, pies de página, etc.).
|
||||
/// Si se deja en null, se aplica la configuración por defecto (A4, vertical, sin márgenes personalizados).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Un array de bytes que representa el documento PDF generado.
|
||||
/// Puede ser utilizado para guardar en disco, devolver en una API como FileContentResult, etc.
|
||||
/// </returns>
|
||||
Task<byte[]> GeneratePdfFromHtmlAsync(string htmlContent, PdfGenerationOptions options = null);
|
||||
}
|
||||
}
|
||||
15
Transversal/Models/PdfGenerationOptions.cs
Normal file
15
Transversal/Models/PdfGenerationOptions.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using PuppeteerSharp.Media;
|
||||
|
||||
namespace Transversal.Models
|
||||
{
|
||||
public class PdfGenerationOptions
|
||||
{
|
||||
public PaperFormat Format { get; set; } = PaperFormat.A4;
|
||||
public bool Landscape { get; set; } = false;
|
||||
public bool PrintBackground { get; set; } = true;
|
||||
public decimal Scale { get; set; } = 1.0m;
|
||||
public MarginOptions Margins { get; set; } = null;
|
||||
public string HeaderTemplate { get; set; } = null;
|
||||
public string FooterTemplate { get; set; } = null;
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Transversal.Services
|
||||
{
|
||||
internal class Class1
|
||||
{
|
||||
}
|
||||
}
|
||||
57
Transversal/Services/PuppeteerPdfGeneratorService.cs
Normal file
57
Transversal/Services/PuppeteerPdfGeneratorService.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Transversal.Interfaces;
|
||||
using Transversal.Models;
|
||||
using PuppeteerSharp;
|
||||
using PuppeteerSharp.Media;
|
||||
|
||||
namespace Transversal.Services
|
||||
{
|
||||
public class PuppeteerPdfGeneratorService : IPdfGeneratorService, IAsyncDisposable
|
||||
{
|
||||
private readonly Browser _browser;
|
||||
|
||||
public PuppeteerPdfGeneratorService()
|
||||
{
|
||||
// Descargar Chromium si no está
|
||||
new BrowserFetcher().DownloadAsync().GetAwaiter().GetResult();
|
||||
|
||||
// Lanzar Chromium persistente
|
||||
_browser = Puppeteer.LaunchAsync(new LaunchOptions
|
||||
{
|
||||
Headless = true,
|
||||
Args = new[]
|
||||
{
|
||||
"--no-sandbox",
|
||||
"--disable-setuid-sandbox"
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
public async Task<byte[]> GeneratePdfFromHtmlAsync(string htmlContent, PdfGenerationOptions options = null)
|
||||
{
|
||||
options ??= new PdfGenerationOptions();
|
||||
|
||||
using var page = await _browser.NewPageAsync();
|
||||
await page.SetContentAsync(htmlContent);
|
||||
|
||||
var pdfOptions = new PdfOptions
|
||||
{
|
||||
Format = options.Format,
|
||||
Landscape = options.Landscape,
|
||||
PrintBackground = options.PrintBackground,
|
||||
Scale = options.Scale,
|
||||
MarginOptions = options.Margins ?? new MarginOptions(),
|
||||
DisplayHeaderFooter = !string.IsNullOrEmpty(options.HeaderTemplate)
|
||||
|| !string.IsNullOrEmpty(options.FooterTemplate),
|
||||
HeaderTemplate = options.HeaderTemplate,
|
||||
FooterTemplate = options.FooterTemplate
|
||||
};
|
||||
|
||||
return await page.PdfDataAsync(pdfOptions);
|
||||
}
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_browser != null)
|
||||
await _browser.CloseAsync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EPPlus" Version="7.5.2" />
|
||||
<PackageReference Include="EPPlus" Version="8.0.4" />
|
||||
<PackageReference Include="PuppeteerSharp" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -7,10 +7,13 @@
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\maski\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\maski\.nuget\packages\" />
|
||||
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\maski\.nuget\packages\newtonsoft.json\10.0.3</PkgNewtonsoft_Json>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
103
phronCare.API/Controllers/PdfTestController.cs
Normal file
103
phronCare.API/Controllers/PdfTestController.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Transversal.Interfaces;
|
||||
|
||||
namespace phronCare.API.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class PdfTestController : ControllerBase
|
||||
{
|
||||
private readonly IPdfGeneratorService _pdfGeneratorService;
|
||||
|
||||
public PdfTestController(IPdfGeneratorService pdfGeneratorService)
|
||||
{
|
||||
_pdfGeneratorService = pdfGeneratorService;
|
||||
}
|
||||
|
||||
[HttpGet("test")]
|
||||
public async Task<IActionResult> GetTestPdf()
|
||||
{
|
||||
// HTML de ejemplo
|
||||
string html = @"
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; font-size: 12px; margin: 20px; }
|
||||
.header { text-align: center; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; margin-bottom: 30px; }
|
||||
.header h1 { color: #4CAF50; margin: 0; }
|
||||
.info-table, .details-table, .totals-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
|
||||
.info-table td { padding: 5px; }
|
||||
.details-table th, .details-table td { border: 1px solid #ddd; padding: 8px; text-align: center; }
|
||||
.details-table th { background-color: #4CAF50; color: white; }
|
||||
.totals-table th, .totals-table td { border: 1px solid #ddd; padding: 8px; text-align: right; }
|
||||
.totals-table th { background-color: #4CAF50; color: white; }
|
||||
.footer { position: fixed; bottom: 15px; left: 0; right: 0; text-align: center; font-size: 10px; color: gray; border-top: 1px solid #ddd; padding-top: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='header'>
|
||||
<h1>PhronCare Ortopedia</h1>
|
||||
<p>Presupuesto médico de prueba</p>
|
||||
</div>
|
||||
|
||||
<table class='info-table'>
|
||||
<tr>
|
||||
<td><strong>Cliente:</strong> Juan Pérez</td>
|
||||
<td><strong>Presupuesto N°:</strong> 000123</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Fecha:</strong> 13/05/2025</td>
|
||||
<td><strong>Profesional:</strong> Dr. Carlos López</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class='details-table'>
|
||||
<tr>
|
||||
<th>Cantidad</th>
|
||||
<th>Producto</th>
|
||||
<th>Precio Unitario</th>
|
||||
<th>Subtotal</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Rodillera ortopédica</td>
|
||||
<td>$5.000</td>
|
||||
<td>$10.000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Férula de inmovilización</td>
|
||||
<td>$8.000</td>
|
||||
<td>$8.000</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class='totals-table'>
|
||||
<tr>
|
||||
<th>Subtotal</th>
|
||||
<td>$18.000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IVA (21%)</th>
|
||||
<td>$3.780</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<td><strong>$21.780</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class='footer'>
|
||||
Documento generado automáticamente por PhronCare - Solo para fines demostrativos. No válido como factura.
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
|
||||
byte[] pdfBytes = await _pdfGeneratorService.GeneratePdfFromHtmlAsync(html);
|
||||
|
||||
return File(pdfBytes, "application/pdf", "Presupuesto.pdf");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,8 @@ using Core.Services;
|
||||
using phronCare.API.Models.Security;
|
||||
using Models.Repositories;
|
||||
using Models.Models;
|
||||
using Transversal.Interfaces;
|
||||
using Transversal.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -174,6 +176,9 @@ app.Run();
|
||||
|
||||
static void RepositorysAndServices(WebApplicationBuilder builder)
|
||||
{
|
||||
// Registro servicio PDF transversal
|
||||
builder.Services.AddScoped<IPdfGeneratorService, PuppeteerPdfGeneratorService>();
|
||||
|
||||
builder.Services.AddScoped<ITicketDom, TicketService>();
|
||||
builder.Services.AddScoped<ITicketRepository, TicketRepository>();
|
||||
|
||||
|
||||
@ -1056,6 +1056,16 @@
|
||||
],
|
||||
"ReturnTypes": []
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.PdfTestController",
|
||||
"Method": "GetTestPdf",
|
||||
"RelativePath": "api/PdfTest/test",
|
||||
"HttpMethod": "GET",
|
||||
"IsController": true,
|
||||
"Order": 0,
|
||||
"Parameters": [],
|
||||
"ReturnTypes": []
|
||||
},
|
||||
{
|
||||
"ContainingType": "phronCare.API.Controllers.Sales.PeopleController",
|
||||
"Method": "GetById",
|
||||
|
||||
@ -501,7 +501,11 @@
|
||||
"dependencies": {
|
||||
"EPPlus": {
|
||||
"target": "Package",
|
||||
"version": "[7.5.2, )"
|
||||
"version": "[8.0.4, )"
|
||||
},
|
||||
"PuppeteerSharp": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
<Import Project="$(NuGetPackageRoot)entityframework\6.5.1\buildTransitive\net6.0\EntityFramework.props" Condition="Exists('$(NuGetPackageRoot)entityframework\6.5.1\buildTransitive\net6.0\EntityFramework.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\maski\.nuget\packages\newtonsoft.json\10.0.3</PkgNewtonsoft_Json>
|
||||
<PkgMicrosoft_Extensions_ApiDescription_Server Condition=" '$(PkgMicrosoft_Extensions_ApiDescription_Server)' == '' ">C:\Users\maski\.nuget\packages\microsoft.extensions.apidescription.server\6.0.5</PkgMicrosoft_Extensions_ApiDescription_Server>
|
||||
<PkgMicrosoft_CodeAnalysis_Analyzers Condition=" '$(PkgMicrosoft_CodeAnalysis_Analyzers)' == '' ">C:\Users\maski\.nuget\packages\microsoft.codeanalysis.analyzers\3.3.3</PkgMicrosoft_CodeAnalysis_Analyzers>
|
||||
<PkgMicrosoft_VisualStudio_Azure_Containers_Tools_Targets Condition=" '$(PkgMicrosoft_VisualStudio_Azure_Containers_Tools_Targets)' == '' ">C:\Users\maski\.nuget\packages\microsoft.visualstudio.azure.containers.tools.targets\1.21.0</PkgMicrosoft_VisualStudio_Azure_Containers_Tools_Targets>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)system.text.json\8.0.5\buildTransitive\net6.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\8.0.5\buildTransitive\net6.0\System.Text.Json.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)system.text.json\8.0.5\buildTransitive\net6.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\8.0.5\buildTransitive\net6.0\System.Text.Json.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.0\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.0\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.visualstudio.azure.containers.tools.targets\1.21.0\build\Microsoft.VisualStudio.Azure.Containers.Tools.Targets.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.visualstudio.azure.containers.tools.targets\1.21.0\build\Microsoft.VisualStudio.Azure.Containers.Tools.Targets.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)entityframework\6.5.1\buildTransitive\net6.0\EntityFramework.targets" Condition="Exists('$(NuGetPackageRoot)entityframework\6.5.1\buildTransitive\net6.0\EntityFramework.targets')" />
|
||||
</ImportGroup>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,73 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Configuration;
|
||||
using System.Reflection;
|
||||
using Models.Models;
|
||||
using Models.Repositories;
|
||||
|
||||
namespace phronCare.Test
|
||||
{
|
||||
[TestFixture]
|
||||
public class TicketRepositoryTests
|
||||
{
|
||||
private TicketRepository _ticketRepository;
|
||||
private PhronCareOperationsHubContext _context;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
#if NETCOREAPP
|
||||
// Copiar el archivo config para asegurarse de que ConfigurationManager lo encuentre
|
||||
string configFile = $"{Assembly.GetExecutingAssembly().Location}.config";
|
||||
string outputConfigFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
|
||||
File.Copy(configFile, outputConfigFile, true);
|
||||
#endif
|
||||
|
||||
// Obtener la cadena de conexión desde app.config usando ConfigurationManager
|
||||
var connectionString = ConfigurationManager.ConnectionStrings["OperationsHub"]?.ConnectionString;
|
||||
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("No se encontró la cadena de conexión 'OperationsHub'.");
|
||||
}
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<PhronCareOperationsHubContext>();
|
||||
optionsBuilder.UseSqlServer(connectionString);
|
||||
|
||||
_context = new PhronCareOperationsHubContext(optionsBuilder.Options);
|
||||
_ticketRepository = new TicketRepository(_context);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Verifica que _context no sea null antes de llamarlo
|
||||
_context?.Dispose();
|
||||
}
|
||||
[Test]
|
||||
public async Task GetSummaryAsync_ShouldReturnValidSummaryData()
|
||||
{
|
||||
// Act
|
||||
var summaryData = await _ticketRepository.GetSummaryAsync();
|
||||
|
||||
// Assert
|
||||
Assert.That(summaryData, Is.Not.Null, "El resumen no debe ser nulo");
|
||||
Assert.That(summaryData.Any(), Is.True, "El resumen debe contener al menos un elemento");
|
||||
|
||||
foreach (var item in summaryData)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(item.Estado, Is.Not.Null, "El campo Estado no debe ser nulo");
|
||||
Assert.That(item.Cantidad, Is.GreaterThanOrEqualTo(0), "La cantidad debe ser mayor o igual a cero");
|
||||
});
|
||||
}
|
||||
}
|
||||
[Test]
|
||||
public async Task GetAllAsync_ReturnsAllTickets()
|
||||
{
|
||||
var tickets = await _ticketRepository.GetAllAsync();
|
||||
Assert.That(tickets, Is.Not.Null);
|
||||
Assert.That(tickets.Any(), Is.True);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
phronCare.Test/PdfGeneratorServiceTests.cs
Normal file
142
phronCare.Test/PdfGeneratorServiceTests.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using Transversal.Services;
|
||||
using Transversal.Models;
|
||||
|
||||
namespace phronCare.Test
|
||||
{
|
||||
[TestFixture]
|
||||
public class PdfGeneratorServiceTests
|
||||
{
|
||||
private PuppeteerPdfGeneratorService _pdfService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// Instancia real del servicio (recomendado para test manual/local)
|
||||
_pdfService = new PuppeteerPdfGeneratorService();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
// Liberar Chromium al finalizar los tests
|
||||
if (_pdfService != null)
|
||||
await _pdfService.DisposeAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GeneratePdfFromHtml_ShouldCreatePdfFile()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
string html = @"
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; font-size: 12px; margin: 20px; }
|
||||
.header { text-align: center; border-bottom: 2px solid #4CAF50; padding-bottom: 10px; margin-bottom: 20px; }
|
||||
.header h1 { color: #4CAF50; }
|
||||
.info { margin-bottom: 20px; }
|
||||
.info table { width: 100%; }
|
||||
.info td { padding: 5px; }
|
||||
.details table { width: 100%; border-collapse: collapse; }
|
||||
.details th, .details td { border: 1px solid #dddddd; text-align: center; padding: 8px; }
|
||||
.details th { background-color: #4CAF50; color: white; }
|
||||
.totals { margin-top: 20px; float: right; width: 300px; }
|
||||
.totals table { width: 100%; border-collapse: collapse; }
|
||||
.totals th, .totals td { border: 1px solid #dddddd; text-align: right; padding: 8px; }
|
||||
.totals th { background-color: #4CAF50; color: white; }
|
||||
.footer { position: fixed; bottom: 20px; left: 0; right: 0; text-align: center; font-size: 10px; color: gray; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='header'>
|
||||
<h1>PhronCare Ortopedia</h1>
|
||||
<p>Presupuesto Médico</p>
|
||||
</div>
|
||||
|
||||
<div class='info'>
|
||||
<table>
|
||||
<tr>
|
||||
<td><strong>Cliente:</strong> Juan Pérez</td>
|
||||
<td><strong>Presupuesto N°:</strong> 000123</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Fecha:</strong> 13/05/2025</td>
|
||||
<td><strong>Profesional:</strong> Dr. Carlos López</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class='details'>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Cantidad</th>
|
||||
<th>Producto</th>
|
||||
<th>Precio Unitario</th>
|
||||
<th>Subtotal</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Rodillera ortopédica</td>
|
||||
<td>$5.000</td>
|
||||
<td>$10.000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Férula de inmovilización</td>
|
||||
<td>$8.000</td>
|
||||
<td>$8.000</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class='totals'>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Subtotal</th>
|
||||
<td>$18.000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IVA (21%)</th>
|
||||
<td>$3.780</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<td><strong>$21.780</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class='footer'>
|
||||
Presupuesto generado automáticamente por PhronCare - No válido como factura.
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
string outputFolder = @"C:\temp";
|
||||
if (!Directory.Exists(outputFolder))
|
||||
Directory.CreateDirectory(outputFolder);
|
||||
|
||||
string outputPath = Path.Combine(outputFolder, "DemoTest_Puppeteer.pdf");
|
||||
|
||||
// Opcional: podés probar pasando o no opciones
|
||||
var options = new PdfGenerationOptions
|
||||
{
|
||||
Format = PuppeteerSharp.Media.PaperFormat.A4,
|
||||
Landscape = false,
|
||||
PrintBackground = true,
|
||||
Scale = 1.0m,
|
||||
HeaderTemplate = "<div style='font-size:10px; text-align:center;'>Presupuesto</div>",
|
||||
FooterTemplate = "<div style='font-size:10px; text-align:center;'>Página <span class='pageNumber'></span> de <span class='totalPages'></span></div>"
|
||||
};
|
||||
|
||||
// Act
|
||||
byte[] pdfBytes = await _pdfService.GeneratePdfFromHtmlAsync(html, options);
|
||||
await File.WriteAllBytesAsync(outputPath, pdfBytes);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(File.Exists(outputPath));
|
||||
Assert.IsTrue(new FileInfo(outputPath).Length > 0);
|
||||
TestContext.WriteLine($"PDF generado correctamente en: {outputPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,11 @@
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"projectReferences": {}
|
||||
"projectReferences": {
|
||||
"C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\Transversal.csproj": {
|
||||
"projectPath": "C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\Transversal.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
@ -98,6 +102,81 @@
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.201/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\Transversal.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\Transversal.csproj",
|
||||
"projectName": "Transversal",
|
||||
"projectPath": "C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\Transversal.csproj",
|
||||
"packagesPath": "C:\\Users\\maski\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\Users\\maski\\source\\repos\\SaludLAB\\phronCare\\Transversal\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\maski\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net8.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.200"
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"dependencies": {
|
||||
"EPPlus": {
|
||||
"target": "Package",
|
||||
"version": "[8.0.4, )"
|
||||
},
|
||||
"PuppeteerSharp": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.201/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--<ProjectReference Include="..\Models\Models.csproj" />-->
|
||||
<ProjectReference Include="..\Transversal\Transversal.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
46
phronCare.UIBlazor/Pages/Testpdf.razor
Normal file
46
phronCare.UIBlazor/Pages/Testpdf.razor
Normal file
@ -0,0 +1,46 @@
|
||||
@page "/testpdf"
|
||||
@inject HttpClient Http
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<h3>Visualizador PDF de prueba (servidor + local + descarga)</h3>
|
||||
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary me-2" @onclick="SavePdf">
|
||||
<i class="fas fa-download me-1"></i> Guardar
|
||||
</button>
|
||||
<button class="btn btn-success me-2" @onclick="ViewPdf">
|
||||
<i class="fas fa-eye me-1"></i> Ver
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(LocalPdfPath))
|
||||
{
|
||||
<iframe src="@LocalPdfPath" width="100%" height="800px" style="border: 1px solid #ddd;"></iframe>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string LocalPdfPath;
|
||||
|
||||
private async Task SavePdf()
|
||||
{
|
||||
string path = @"C:\temp\PresupuestoTest.pdf";
|
||||
|
||||
if (!Directory.Exists(@"C:\temp"))
|
||||
Directory.CreateDirectory(@"C:\temp");
|
||||
|
||||
// ✅ 1. Llamar al endpoint de API para generar PDF
|
||||
var pdfBytes = await Http.GetByteArrayAsync("http://localhost:5243/api/PdfTest/test"); // Cambia a tu URL real
|
||||
|
||||
// ✅ 2. Guardar en disco
|
||||
System.IO.File.WriteAllBytes(path, pdfBytes);
|
||||
|
||||
// ✅ 3. Descargar al usuario
|
||||
await JS.InvokeVoidAsync("saveAsFile", "PresupuestoTest.pdf", Convert.ToBase64String(pdfBytes));
|
||||
}
|
||||
|
||||
private void ViewPdf()
|
||||
{
|
||||
// 👉 Cargar el archivo guardado
|
||||
LocalPdfPath = "/pdf/DemoTest.pdf";
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
|
||||
using Blazored.Modal;
|
||||
using Blazored.Toast;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
@ -48,7 +47,6 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
||||
builder.Services.AddScoped<ISalesLookupService, SalesLookupService>();
|
||||
builder.Services.AddScoped<IExchangeRateService, ExchangeRateService>();
|
||||
|
||||
|
||||
builder.Services.AddScoped<ExchangeRateService>();
|
||||
builder.Services.AddScoped<QuoteService>();
|
||||
builder.Services.AddScoped<TicketsService>();
|
||||
@ -64,4 +62,5 @@ static void InjectDependencies(WebAssemblyHostBuilder builder)
|
||||
builder.Services.AddScoped<ProfessionalSpecialtyService>();
|
||||
builder.Services.AddScoped<ProductCategoryService>();
|
||||
builder.Services.AddScoped<PatientService>();
|
||||
|
||||
}
|
||||
@ -67,7 +67,7 @@
|
||||
{
|
||||
<ul class="nav-flex-column">
|
||||
<div class="nav-item px-1">
|
||||
<NavLink class="nav-link" href="sales/customers/">
|
||||
<NavLink class="nav-link" href="Testpdf/">
|
||||
<li aria-hidden="true"></li> Clientes
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
@ -239,7 +239,11 @@
|
||||
"dependencies": {
|
||||
"EPPlus": {
|
||||
"target": "Package",
|
||||
"version": "[7.5.2, )"
|
||||
"version": "[8.0.4, )"
|
||||
},
|
||||
"PuppeteerSharp": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
<Import Project="$(NuGetPackageRoot)blazored.modal\7.3.1\buildTransitive\Blazored.Modal.props" Condition="Exists('$(NuGetPackageRoot)blazored.modal\7.3.1\buildTransitive\Blazored.Modal.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\maski\.nuget\packages\newtonsoft.json\10.0.3</PkgNewtonsoft_Json>
|
||||
<PkgMicrosoft_NET_Sdk_WebAssembly_Pack Condition=" '$(PkgMicrosoft_NET_Sdk_WebAssembly_Pack)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.sdk.webassembly.pack\9.0.3</PkgMicrosoft_NET_Sdk_WebAssembly_Pack>
|
||||
<PkgMicrosoft_NET_ILLink_Tasks Condition=" '$(PkgMicrosoft_NET_ILLink_Tasks)' == '' ">C:\Users\maski\.nuget\packages\microsoft.net.illink.tasks\8.0.14</PkgMicrosoft_NET_ILLink_Tasks>
|
||||
<PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer Condition=" '$(PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer)' == '' ">C:\Users\maski\.nuget\packages\microsoft.aspnetcore.components.webassembly.devserver\8.0.6</PkgMicrosoft_AspNetCore_Components_WebAssembly_DevServer>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.3\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.3\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.3\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.sdk.webassembly.pack\9.0.3\build\Microsoft.NET.Sdk.WebAssembly.Pack.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.1\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\8.0.1\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly.devserver\8.0.6\build\Microsoft.AspNetCore.Components.WebAssembly.DevServer.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.components.webassembly.devserver\8.0.6\build\Microsoft.AspNetCore.Components.WebAssembly.DevServer.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.components.analyzers\8.0.6\buildTransitive\netstandard2.0\Microsoft.AspNetCore.Components.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.components.analyzers\8.0.6\buildTransitive\netstandard2.0\Microsoft.AspNetCore.Components.Analyzers.targets')" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
phronCare.UIBlazor/wwwroot/pdf/DemoTest.pdf
Normal file
BIN
phronCare.UIBlazor/wwwroot/pdf/DemoTest.pdf
Normal file
Binary file not shown.
@ -1,27 +0,0 @@
|
||||
[
|
||||
{
|
||||
"date": "2022-01-06",
|
||||
"temperatureC": 1,
|
||||
"summary": "Freezing"
|
||||
},
|
||||
{
|
||||
"date": "2022-01-07",
|
||||
"temperatureC": 14,
|
||||
"summary": "Bracing"
|
||||
},
|
||||
{
|
||||
"date": "2022-01-08",
|
||||
"temperatureC": -13,
|
||||
"summary": "Freezing"
|
||||
},
|
||||
{
|
||||
"date": "2022-01-09",
|
||||
"temperatureC": -16,
|
||||
"summary": "Balmy"
|
||||
},
|
||||
{
|
||||
"date": "2022-01-10",
|
||||
"temperatureC": -2,
|
||||
"summary": "Chilly"
|
||||
}
|
||||
]
|
||||
Loading…
x
Reference in New Issue
Block a user