- List removal: GetOngoingEstimatesAsProvider filters on ProviderValidationDate so a provider's signed estimate leaves their list; PostIt refreshes the list on navigation pop-back. - Title renames: "Devis en attente de signature fournisseur/client" on both the provider and client sides (HomePage, VM, tests). - Move the estimate/bill TeX+PDF generation flow from Yavsc.Org into Yavsc.Api (TeXHelpers, PdfGenerationViewModel, BillViewComponent, Bill/Estimate templates, _ViewImports). Program.cs wires AddControllersWithViews + SiteSettings; FrontOfficeApiController injects IRazorViewEngine (cast to IViewEngine) with optional deps and exposes estimate.tex/estimate.pdf endpoints keyed by query id. - PDF generation drives through the Estimate_pdf view (side-effect render) and compiles via the system lualatex, fed TeX on stdin (no intermediate .tex), PDF to the bills dir, aux artifacts cleaned so only the .pdf persists. Resolve the bills dir to an absolute path so lualatex's -output-directory and working directory agree regardless of the host cwd (fixes "I can't write on file estimate-1.log"). - PostIt: DownloadAsync on the client + GetEstimateTex/PdfAsync; Télécharger PDF/TeX buttons on the validation and command-detail pages via a StorageProvider save helper. Co-Authored-By: Claude Code <noreply@anthropic.com>
108 lines
4.4 KiB
C#
108 lines
4.4 KiB
C#
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
}
|