Add support for invoice attachments

Introduced the `InvoiceAttachment` entity and its relationship with `ZugferdInvoice` to manage extracted invoice attachments. Updated `AppDbContext` and added a migration to create the `InvoiceAttachments` table with cascading delete behavior and an index for optimized queries.

Enhanced the UI to display attachments in `Details.cshtml`, including file type icons, file size, and extraction date. Added a new `ViewAttachment` page to render or download attachments based on their type, with support for XML, plain text, images, and downloads.

Implemented `AttachmentViewerService` to determine viewer types and MIME types for attachments. Registered the service in the DI container. Updated `Upload.cshtml.cs` to save extracted attachments to the database.

Improved user experience with syntax highlighting for XML files and appropriate messages for unsupported file types.
This commit is contained in:
OlgunR
2026-06-02 15:03:37 +02:00
parent 43d63e975d
commit 920dce13d5
13 changed files with 536 additions and 2 deletions

View File

@@ -2,6 +2,13 @@
@model DXApp.TemplateKitProject.Pages.Invoices.DetailsModel
@{
ViewData["Title"] = "Rechnungsdetails";
string FormatFileSize(long bytes)
{
if (bytes < 1024) return $"{bytes} Bytes";
if (bytes < 1024 * 1024) return $"{bytes / 1024.0:N2} KB";
return $"{bytes / (1024.0 * 1024.0):N2} MB";
}
}
<h2>📄 Rechnungsdetails</h2>
@@ -11,7 +18,7 @@
{
<button class="btn btn-primary mb-3 ms-2"
onclick="openPdfViewer(@Model.Invoice.Id)">
📄 PDF anzeigen
📄 Ergebnis anzeigen
</button>
}
@@ -50,6 +57,51 @@ else
</tr>
</table>
@* Anhänge-Sektion *@
@if (Model.Invoice.Attachments.Any())
{
<h4 class="mt-4">📎 Anhänge (@Model.Invoice.Attachments.Count)</h4>
<div class="list-group">
@foreach (var attachment in Model.Invoice.Attachments)
{
var icon = attachment.IsZugferdXml ? "📋" : "📄";
var extension = System.IO.Path.GetExtension(attachment.OriginalFileName).ToLowerInvariant();
icon = extension switch
{
".xml" => "📋",
".pdf" => "📄",
".jpg" or ".jpeg" or ".png" or ".gif" => "🖼️",
".txt" => "📝",
_ => "📎"
};
<a href="/Invoices/ViewAttachment?filePath=@Uri.EscapeDataString(attachment.SavedFilePath)"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"
target="_blank">
<div>
<span class="me-2">@icon</span>
<strong>@attachment.OriginalFileName</strong>
@if (attachment.IsZugferdXml)
{
<span class="badge bg-success ms-2">ZUGFeRD-XML</span>
}
<br />
<small class="text-muted">
Größe: @FormatFileSize(attachment.FileSizeBytes) ·
Extrahiert: @attachment.ExtractedAt.ToString("dd.MM.yyyy HH:mm")
</small>
</div>
<span class="badge bg-primary">Öffnen</span>
</a>
}
</div>
}
else
{
<h4 class="mt-4">📎 Anhänge</h4>
<div class="alert alert-info">Keine Anhänge extrahiert.</div>
}
@(Html.DevExtreme().Popup()
.ID("pdf-viewer-popup")
.Title("PDF Viewer")