diff --git a/DXApp.TemplateKitProject/Models/PdfExtractionResult.cs b/DXApp.TemplateKitProject/Models/PdfExtractionResult.cs index 9c774ce..93efd3b 100644 --- a/DXApp.TemplateKitProject/Models/PdfExtractionResult.cs +++ b/DXApp.TemplateKitProject/Models/PdfExtractionResult.cs @@ -17,6 +17,11 @@ public class PdfExtractionResult // Soll eine Warnung angezeigt werden? // true wenn kein PDF/A → ZUGFeRD-Rechnungen müssen PDF/A-3b sein public bool PdfAWarning { get; set; } + + // ZUGFeRD Guideline-ID aus XMP-Metadaten + // Beispiel: "urn:ferd:invoice:rc:comfort" + // Leer wenn keine ZUGFeRD-Metadaten gefunden + public string ZugferdGuidelineId { get; set; } = string.Empty; } public class ExtractedAttachment diff --git a/DXApp.TemplateKitProject/Pages/Invoices/Upload.cshtml b/DXApp.TemplateKitProject/Pages/Invoices/Upload.cshtml index feb389b..7552eef 100644 --- a/DXApp.TemplateKitProject/Pages/Invoices/Upload.cshtml +++ b/DXApp.TemplateKitProject/Pages/Invoices/Upload.cshtml @@ -45,6 +45,13 @@ @* PDF/A-Konformitätsstufe anzeigen *@
PDF/A-Konformität: @Model.Result.PdfALevel + @if (!string.IsNullOrEmpty(Model.Result.ZugferdGuidelineId)) + { +
+ + Guideline-ID: + @Model.Result.ZugferdGuidelineId + } @if (Model.Result.PdfAWarning) { – ⚠️ ZUGFeRD-Rechnungen müssen PDF/A-3b sein. diff --git a/DXApp.TemplateKitProject/Services/PdfAttachmentExtractorService.cs b/DXApp.TemplateKitProject/Services/PdfAttachmentExtractorService.cs index 6a0066e..5377c96 100644 --- a/DXApp.TemplateKitProject/Services/PdfAttachmentExtractorService.cs +++ b/DXApp.TemplateKitProject/Services/PdfAttachmentExtractorService.cs @@ -42,6 +42,24 @@ public class PdfAttachmentExtractorService( "PDF '{FileName}': Konformität = {Level}", sourceFileName, result.PdfALevel); + // ZUGFeRD Guideline-ID aus XMP-Metadaten lesen + try + { + var xmpData = processor.Document.Metadata?.Data; + if (!string.IsNullOrWhiteSpace(xmpData)) + { + result.ZugferdGuidelineId = ExtractGuidelineId(xmpData); + if (!string.IsNullOrEmpty(result.ZugferdGuidelineId)) + logger.LogInformation( + "PDF '{FileName}': Guideline-ID = {GuidelineId}", + sourceFileName, result.ZugferdGuidelineId); + } + } + catch (Exception ex) + { + logger.LogWarning(ex, "XMP-Metadaten konnten nicht gelesen werden."); + } + // Fix: .ToList() → IEnumerable → List mit Count-Property var attachments = processor.Document.FileAttachments.ToList(); @@ -155,4 +173,34 @@ public class PdfAttachmentExtractorService( ? $"Ja → {result.ZugferdXmlAttachment!.OriginalFileName}" : "Nein"); } + + private static string ExtractGuidelineId(string xmpData) + { + // Bekannte ZUGFeRD/Factur-X Guideline-Präfixe + string[] knownPrefixes = + [ + "urn:ferd:", + "urn:cen.eu:", + "urn:factur-x.", + "urn:zugferd:", + "urn:xoev-de:" + ]; + + // XMP ist XML → wir suchen nach dem Wert zwischen Tags + // Typisch: urn:ferd:invoice:rc:comfort + // oder: EN 16931 + foreach (var prefix in knownPrefixes) + { + var idx = xmpData.IndexOf(prefix, StringComparison.OrdinalIgnoreCase); + if (idx < 0) continue; + + // Ende des Wertes finden (nächstes < Zeichen) + var end = xmpData.IndexOf('<', idx); + if (end < 0) end = xmpData.Length; + + return xmpData[idx..end].Trim(); + } + + return string.Empty; + } } \ No newline at end of file