Add function to retrieve company address as JSON and update invoice procedures

- Created a new function `fds__getCompanyAddressJson` to return a company's postal address as a structured JSON object.
- Modified stored procedures `fds__createInvoice`, `fds__setInvoice`, and `fds__prepInvoice` to include a new parameter `@SendToAddressJson` for handling the address data.
- Updated the invoice table and user-defined types to accommodate the new `SendToAddressJson` field.
- Ensured that the address data is properly retrieved and stored in the invoice records.
This commit is contained in:
2026-07-18 18:01:24 +02:00
parent 5ccd85c38f
commit 628802db19
45 changed files with 1892 additions and 26 deletions
+29
View File
@@ -83,6 +83,34 @@ public class FdsInvoiceData
}
}
/// <summary>
/// Structured recipient (buyer) address for eRechnung (EN 16931) mapping, parsed from the
/// <c>CustomValues</c> JSON (interim persistence — see ADR 0012). Null for invoices that
/// predate structured capture; callers fall back to the free-text <c>SendToAddress</c> block.
/// </summary>
public Fuchs.Services.InvoiceRecipientAddress? RecipientAddress
{
get
{
// Prefer the dedicated SendToAddressJson column; fall back to the CustomValues blob
// (interim persistence and rows written before the column existed).
string col = InvoiceRegistration?.getString("SendToAddressJson") ?? "";
if (col.Length > 0)
{
try { return Fuchs.Services.InvoiceRecipientAddress.FromJson(JObject.Parse(col)); }
catch { /* fall through to CustomValues */ }
}
return Fuchs.Services.InvoiceRecipientAddress.FromCustomValues(
InvoiceRegistration?.getString("CustomValues") is { Length: > 0 } cv ? cv : RawCustomValues);
}
}
/// <summary>The structured recipient address serialised for the <c>SendToAddressJson</c> column
/// (from the posted CustomValues blob), or empty when the invoice has no structured address.</summary>
private string SendToAddressJsonForPersist() =>
Fuchs.Services.InvoiceRecipientAddress.FromCustomValues(RawCustomValues)?.ToJson()
.ToString(Newtonsoft.Json.Formatting.None) ?? "";
/// <summary>VAT rows keyed by percentage string (e.g. "19"), from InvoiceRegistration.</summary>
public Dictionary<string, Dictionary<string, object?>> VatRows
{
@@ -153,6 +181,7 @@ public class FdsInvoiceData
SQL_NVarChar("@SendToEmail", RawInvoiceEmail),
SQL_NVarChar("@ProvisionPeriod", RawProvisionPeriod, dbNull_IfEmpty: true),
SQL_NVarChar("@CustomValues", RawCustomValues, dbNull_IfEmpty: true),
SQL_NVarChar("@SendToAddressJson", SendToAddressJsonForPersist(), dbNull_IfEmpty: true),
SQL_Float("@InvoiceService_net", stringvalue: Sms?.nz("tscn") ?? "0"),
SQL_Float("@InvoiceService_VAT", stringvalue: Sms?.nz("tscvat") ?? "0"),
SQL_VarChar("@InvoiceOptions", BuildInvoiceOptions(), dbNull_IfEmpty: true)
+12 -4
View File
@@ -979,18 +979,26 @@ public static class FuchsPdf
}
}
/// <summary>Renders a MigraDoc Document to a PDF/A byte array.</summary>
public static byte[] DocToPdfBytes(Document doc)
/// <summary>
/// Renders a MigraDoc Document to a plain (non-PDF/A) PDF byte array. Fonts are embedded via
/// the <c>OCOREFontResolver</c>, but no PDF/A post-processing is applied. This is the visual
/// PDF fed into <c>eRechnungLib.ToZugferd(...)</c>, which owns the single PDF/A-3 conversion
/// for eRechnung output (avoiding a conflicting second PDF/A layer from Spire). See ADR 0005.
/// </summary>
public static byte[] DocToPdfBytesRaw(Document doc)
{
EnsureFontResolver();
var renderer = new PdfDocumentRenderer() { Document = doc };
renderer.RenderDocument();
using var ms = new MemoryStream();
renderer.PdfDocument.Save(ms, closeStream: false);
ms.Position = 0;
return OCORE.pdf._pdf.pdfAFileContent(ms.ToArray());
return ms.ToArray();
}
/// <summary>Renders a MigraDoc Document to a PDF/A byte array (Spire post-processing).</summary>
public static byte[] DocToPdfBytes(Document doc)
=> OCORE.pdf._pdf.pdfAFileContent(DocToPdfBytesRaw(doc));
/// <summary>Renders a MigraDoc Document to an ImageCollection for preview.</summary>
public static async Task<OCORE.pdf._pdf.ImageCollection> DocToImageCollection(Document doc)
{