corrige la génération du Pdf
This commit is contained in:
parent
7caae103fe
commit
bbc37d3095
9 changed files with 34 additions and 211 deletions
|
|
@ -23,7 +23,7 @@ namespace Yavsc.ApiControllers
|
||||||
private readonly IBillingService billing;
|
private readonly IBillingService billing;
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
private readonly SiteSettings siteSettings;
|
private readonly SiteSettings siteSettings;
|
||||||
private readonly IViewEngine viewEngine;
|
private readonly IRazorViewEngine viewEngine;
|
||||||
|
|
||||||
// The view-rendering dependencies (siteSettings, viewEngine) are
|
// The view-rendering dependencies (siteSettings, viewEngine) are
|
||||||
// optional: only EstimateTex/EstimatePdf use them, and those run in
|
// optional: only EstimateTex/EstimatePdf use them, and those run in
|
||||||
|
|
@ -51,7 +51,7 @@ namespace Yavsc.ApiControllers
|
||||||
this.billing = billing ?? new BillingService(context);
|
this.billing = billing ?? new BillingService(context);
|
||||||
logger = loggerFactory.CreateLogger<FrontOfficeApiController>();
|
logger = loggerFactory.CreateLogger<FrontOfficeApiController>();
|
||||||
this.siteSettings = siteSettings?.Value;
|
this.siteSettings = siteSettings?.Value;
|
||||||
this.viewEngine = viewEngine as IViewEngine;
|
this.viewEngine = viewEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("profiles/{actCode}")]
|
[HttpGet("profiles/{actCode}")]
|
||||||
|
|
@ -97,9 +97,14 @@ namespace Yavsc.ApiControllers
|
||||||
public async Task<IActionResult> EstimatePdf(long queryId, CancellationToken token)
|
public async Task<IActionResult> EstimatePdf(long queryId, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (viewEngine is null || siteSettings is null)
|
if (viewEngine is null || siteSettings is null)
|
||||||
return Problem("PDF rendering is not configured on this host.",
|
{
|
||||||
|
string msg = "PDF rendering is not configured on this host.";
|
||||||
|
logger.LogError(msg);
|
||||||
|
return Problem(msg,
|
||||||
statusCode: StatusCodes.Status503ServiceUnavailable);
|
statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var estimate = await LoadEstimateForRenderAsync(queryId, token);
|
var estimate = await LoadEstimateForRenderAsync(queryId, token);
|
||||||
if (estimate is null) return NotFound(new { Error = "no estimate linked to this query" });
|
if (estimate is null) return NotFound(new { Error = "no estimate linked to this query" });
|
||||||
|
|
||||||
|
|
@ -135,21 +140,20 @@ namespace Yavsc.ApiControllers
|
||||||
// we stream the generated PDF bytes — but driving the
|
// we stream the generated PDF bytes — but driving the
|
||||||
// generation through the view keeps the template as the
|
// generation through the view keeps the template as the
|
||||||
// output producer, per the original design.
|
// output producer, per the original design.
|
||||||
this.RenderViewToString(viewEngine, "Estimate_pdf", model);
|
if (model.GenerateEstimatePdf() is FileInfo fo)
|
||||||
|
{
|
||||||
|
return File(fo.OpenRead(), "application/pdf");
|
||||||
|
}
|
||||||
|
string msg = "TeX render failed: " + model.GenerationErrorMessage;
|
||||||
|
logger.LogError(msg);
|
||||||
|
return Problem(msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex, "estimate {QueryId}: TeX/PDF render failed", queryId);
|
logger.LogError(ex, "estimate {QueryId}: TeX/PDF render failed", queryId);
|
||||||
return Problem("TeX render failed: " + ex.Message);
|
return Problem("TeX render failed: " + ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!model.Generated)
|
|
||||||
return Problem("PDF generation failed: "
|
|
||||||
+ (model.GenerationErrorMessage?.Value ?? "unknown error"));
|
|
||||||
|
|
||||||
var pdfPath = System.IO.Path.Combine(billsDir, baseFileName + ".pdf");
|
|
||||||
var bytes = await System.IO.File.ReadAllBytesAsync(pdfPath, token);
|
|
||||||
return File(bytes, "application/pdf", baseFileName + ".pdf");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads the estimate linked to a query with the navigation the
|
// Loads the estimate linked to a query with the navigation the
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
|
||||||
namespace Yavsc.Helpers
|
namespace Yavsc.Helpers
|
||||||
{
|
{
|
||||||
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
using ViewModels.Gen;
|
using ViewModels.Gen;
|
||||||
public class TeXString : HtmlString
|
public class TeXString : HtmlString
|
||||||
{
|
{
|
||||||
|
|
@ -115,7 +116,7 @@ namespace Yavsc.Helpers
|
||||||
return new TeXString(string.Join(lineSeparator,texlines));
|
return new TeXString(string.Join(lineSeparator,texlines));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool GenerateEstimatePdf(this SiteSettings settings, PdfGenerationViewModel Model)
|
public static FileInfo GenerateEstimatePdf(this PdfGenerationViewModel Model)
|
||||||
{
|
{
|
||||||
string errorMsg = null;
|
string errorMsg = null;
|
||||||
// Resolve to an absolute path: SiteSettings.Bills may be a
|
// Resolve to an absolute path: SiteSettings.Bills may be a
|
||||||
|
|
@ -195,17 +196,18 @@ namespace Yavsc.Helpers
|
||||||
".fdb_latexmk", ".synctex.gz", ".toc" })
|
".fdb_latexmk", ".synctex.gz", ".toc" })
|
||||||
{
|
{
|
||||||
if (ext == ".log" && !fo.Exists) continue;
|
if (ext == ".log" && !fo.Exists) continue;
|
||||||
var f = new FileInfo(System.IO.Path.Combine(billdir, name + ext));
|
var fileName = name + ext;
|
||||||
|
var f = new FileInfo(System.IO.Path.Combine(billdir, fileName));
|
||||||
if (f.Exists) { try { f.Delete(); } catch { } }
|
if (f.Exists) { try { f.Delete(); } catch { } }
|
||||||
}
|
}
|
||||||
|
|
||||||
Model.Generated = fo.Exists;
|
Model.Generated = fo.Exists;
|
||||||
Model.GenerationErrorMessage = new HtmlString(errorMsg);
|
Model.GenerationErrorMessage = new HtmlString(errorMsg);
|
||||||
return fo.Exists;
|
return fo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RenderViewToString(
|
public static string RenderViewToString(
|
||||||
this Controller controller, IViewEngine engine,
|
this Controller controller, IRazorViewEngine engine,
|
||||||
string viewName, object model, bool isMainPage = true)
|
string viewName, object model, bool isMainPage = true)
|
||||||
{
|
{
|
||||||
if (engine == null)
|
if (engine == null)
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Yavsc.Billing;
|
|
||||||
using Yavsc.Helpers;
|
|
||||||
using Yavsc.Models;
|
|
||||||
using Yavsc.ViewModels;
|
|
||||||
using Yavsc.ViewModels.Gen;
|
|
||||||
using Yavsc.Services;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Yavsc.ViewComponents
|
|
||||||
{
|
|
||||||
public class BillViewComponent : ViewComponent
|
|
||||||
{
|
|
||||||
readonly ApplicationDbContext dbContext;
|
|
||||||
readonly IBillingService billing;
|
|
||||||
readonly IStringLocalizer<BillViewComponent> localizer;
|
|
||||||
private readonly SiteSettings siteSettings;
|
|
||||||
|
|
||||||
public BillViewComponent(ApplicationDbContext dbContext,
|
|
||||||
IStringLocalizer<BillViewComponent> localizer,
|
|
||||||
IBillingService billing,
|
|
||||||
IOptions<SiteSettings> siteSettings)
|
|
||||||
{
|
|
||||||
this.billing = billing;
|
|
||||||
this.dbContext = dbContext;
|
|
||||||
this.localizer = localizer;
|
|
||||||
this.siteSettings = siteSettings.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(
|
|
||||||
string code,
|
|
||||||
IBillable billable,
|
|
||||||
OutputFormat format,
|
|
||||||
bool asBill,
|
|
||||||
SiteSettings settings
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var di = new DirectoryInfo(settings.Bills);
|
|
||||||
var dia = new DirectoryInfo(settings.Avatars);
|
|
||||||
ViewBag.BillsDir = di.FullName;
|
|
||||||
ViewBag.AvatarsDir = dia.FullName;
|
|
||||||
ViewBag.AsBill = asBill; // vrai pour une facture, sinon, c'est un devis
|
|
||||||
ViewBag.Acquitted = billable.GetIsAcquitted();
|
|
||||||
|
|
||||||
ViewBag.BillingCode = code;
|
|
||||||
var client = await dbContext.Users
|
|
||||||
.Include(u=>u.PostalAddress)
|
|
||||||
.SingleAsync(u=>u.Id == billable.ClientId);
|
|
||||||
ViewBag.Client = client;
|
|
||||||
var performer = await dbContext.Users
|
|
||||||
.Include(u=>u.BankInfo)
|
|
||||||
.Include(u=>u.PostalAddress)
|
|
||||||
.SingleAsync(u=>u.Id == billable.PerformerId);
|
|
||||||
ViewBag.Performer = performer;
|
|
||||||
string clientAddress = client.PostalAddress?.Address ?? null;
|
|
||||||
ViewBag.ClientAddress = clientAddress.SplitAddressToTeX();
|
|
||||||
|
|
||||||
|
|
||||||
var profile = await dbContext.Performers
|
|
||||||
.Include(p=>p.OrganizationAddress)
|
|
||||||
.SingleAsync(p=>p.PerformerId == billable.PerformerId);
|
|
||||||
ViewBag.PerformerProfile = profile;
|
|
||||||
ViewBag.ActivityLabel = (await dbContext.Activities.SingleAsync(a => a.Code == billable.ActivityCode)).Name;
|
|
||||||
|
|
||||||
var proAddr = profile.OrganizationAddress.Address;
|
|
||||||
ViewBag.PerformerOrganizationAddress = proAddr.SplitAddressToTeX() ;
|
|
||||||
ViewBag.FooterPerformerOrganizationAddress = proAddr.SplitAddressToTeX(", ");
|
|
||||||
|
|
||||||
ViewBag.PerformerAddress = performer.PostalAddress?.Address.SplitAddressToTeX() ;
|
|
||||||
switch (format) {
|
|
||||||
case OutputFormat.LaTeX :
|
|
||||||
return this.View("Bill_tex", billable);
|
|
||||||
case OutputFormat.Pdf :
|
|
||||||
string tex = null;
|
|
||||||
var oldWriter = ViewComponentContext.ViewContext.Writer;
|
|
||||||
using (var writer = new StringWriter())
|
|
||||||
{
|
|
||||||
this.ViewComponentContext.ViewContext.Writer = writer;
|
|
||||||
var resultTex = View("Bill_tex", billable);
|
|
||||||
await resultTex.ExecuteAsync(this.ViewComponentContext);
|
|
||||||
tex = writer.ToString();
|
|
||||||
}
|
|
||||||
ViewComponentContext.ViewContext.Writer = oldWriter;
|
|
||||||
|
|
||||||
var pdfGenerationViewModel = new PdfGenerationViewModel
|
|
||||||
{
|
|
||||||
Temp = siteSettings.TempDir,
|
|
||||||
TeXSource = tex,
|
|
||||||
DestDir = siteSettings.Bills,
|
|
||||||
BaseFileName = billable.GetFileBaseName(billing)
|
|
||||||
};
|
|
||||||
if (settings.GenerateEstimatePdf(pdfGenerationViewModel)) {
|
|
||||||
return this.View(new { Generated = pdfGenerationViewModel.BaseFileName+".pdf" });
|
|
||||||
} else {
|
|
||||||
return View(new { Error = pdfGenerationViewModel.GenerationErrorMessage } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ViewBag.BillFileInfo = billable.GetBillInfo(billing, siteSettings);
|
|
||||||
ViewBag.Settings = settings;
|
|
||||||
return View("Default",billable);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
@using System.Reflection
|
|
||||||
@using System.IO
|
|
||||||
@using Microsoft.Extensions.WebEncoders
|
|
||||||
@using System.Diagnostics
|
|
||||||
@using System.Text
|
|
||||||
@using Yavsc.Helpers
|
|
||||||
@model Yavsc.ViewModels.Gen.PdfGenerationViewModel
|
|
||||||
|
|
||||||
@{
|
|
||||||
ViewBag.Settings.GenerateEstimatePdf(Model);
|
|
||||||
}
|
|
||||||
@if (Model.Generated) {
|
|
||||||
<div>
|
|
||||||
@(Model.BaseFileName).pdf was generated
|
|
||||||
</div>
|
|
||||||
} else {
|
|
||||||
<div class="error">
|
|
||||||
@Model.GenerationErrorMessage
|
|
||||||
<p>Something went wrong ...</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
|
|
||||||
@model IBillable
|
|
||||||
<div class="bill">
|
|
||||||
@if (ViewBag.BillFileInfo.Exists) {
|
|
||||||
<a class="btn btn-link" href="~/api/bill/facture-@(ViewBag.BillingCode)-@(Model.Id).pdf" >La facture au format Pdf</a>
|
|
||||||
} else {
|
|
||||||
<script>
|
|
||||||
var onGenerate = function () {
|
|
||||||
var _btn = "this";
|
|
||||||
$.post('/api/bill/genpdf/@ViewBag.BillingCode/@Model.Id')
|
|
||||||
.done(function(data){
|
|
||||||
var res = JSON.parse(data);
|
|
||||||
window.location.href = '/api/bill/' + res.Generated;
|
|
||||||
_btn.disabled = "true";
|
|
||||||
})
|
|
||||||
.fail(function( jqXHR, textStatus, errorThrown ) {
|
|
||||||
$('#billgenerr').html('<em>La génération a échoué</em><p>'+textStatus+'</p>')
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function(){$('#btnGen').on('click',onGenerate)})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<div id="billgen">
|
|
||||||
<form action= method="POST">
|
|
||||||
<input id="btnGen" class="btn btn-primary" type="button" onclick="onGenerate" value="Générer la facture au format Pdf" />
|
|
||||||
|
|
||||||
<div id="billgenerr" class="error">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
@using System.Reflection
|
|
||||||
@using System.IO
|
|
||||||
@using Microsoft.Extensions.WebEncoders
|
|
||||||
@using System.Diagnostics
|
|
||||||
@using System.Text
|
|
||||||
@using Yavsc.Helpers
|
|
||||||
@model Yavsc.ViewModels.Gen.PdfGenerationViewModel
|
|
||||||
|
|
||||||
@{
|
|
||||||
SiteSettings.Value.GenerateEstimatePdf(Model);
|
|
||||||
}
|
|
||||||
@if (Model.Generated) {
|
|
||||||
<div>
|
|
||||||
@(Model.BaseFileName).pdf was generated
|
|
||||||
</div>
|
|
||||||
} else {
|
|
||||||
<div class="error">
|
|
||||||
@Model.GenerationErrorMessage
|
|
||||||
<p>Something went wrong ...</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
@ -113,10 +113,10 @@
|
||||||
|
|
||||||
% Logo de la société
|
% Logo de la société
|
||||||
@if (from.Avatar != null) {
|
@if (from.Avatar != null) {
|
||||||
<text>\includegraphics[height=60pt]{@(ViewBag.AvatarsDir)/@(from.UserName).png}
|
<text>\includegraphics[height=60pt, viewport=0 0 128 128]{@(ViewBag.AvatarsDir)/@(from.UserName).png}
|
||||||
</text>
|
</text>
|
||||||
} else {
|
} else {
|
||||||
<text>%\includegraphics{logo.png}
|
<text>%\includegraphics[height=60pt, viewport=0 0 128 128]{logo.png}
|
||||||
</text>
|
</text>
|
||||||
}
|
}
|
||||||
% Nom et adresse de la société
|
% Nom et adresse de la société
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue