Add duplicate invoice detection and warning message
Added a warning message in `Upload.cshtml` to notify users when a duplicate invoice is detected. Introduced the `IsDuplicate` property in `UploadModel` to track duplicates and updated the `OnPostAsync` method to set this property based on the `ImportedAt` timestamp. Enhanced the `ImportAsync` method in `ZugferdImportService` to include duplicate detection by checking the database for invoices with the same `InvoiceNumber` and `SellerTaxId`. If a duplicate is found, it logs a warning and returns the existing invoice. Updated `ImportAsync` to accept an optional `guidelineId` parameter and added logging for duplicate detection and successful imports.
This commit is contained in:
@@ -104,5 +104,12 @@
|
|||||||
<tr><th>Importiert am</th><td>@Model.ImportedInvoice.ImportedAt.ToString("dd.MM.yyyy HH:mm")</td></tr>
|
<tr><th>Importiert am</th><td>@Model.ImportedInvoice.ImportedAt.ToString("dd.MM.yyyy HH:mm")</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<div class="alert alert-success mt-2">✔ Rechnung wurde in der Datenbank gespeichert (ID: @Model.ImportedInvoice.Id)</div>
|
<div class="alert alert-success mt-2">✔ Rechnung wurde in der Datenbank gespeichert (ID: @Model.ImportedInvoice.Id)</div>
|
||||||
|
@if (Model.IsDuplicate)
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning mt-2">
|
||||||
|
⚠️ <strong>Duplikat:</strong> Diese Rechnung wurde bereits importiert (ID: @Model.ImportedInvoice!.Id).
|
||||||
|
Es wurde kein neuer Eintrag angelegt.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ public class UploadModel(
|
|||||||
public string? ErrorMessage { get; private set; }
|
public string? ErrorMessage { get; private set; }
|
||||||
public ZugferdInvoice? ImportedInvoice { get; private set; }
|
public ZugferdInvoice? ImportedInvoice { get; private set; }
|
||||||
public string? ResultFilePath { get; private set; }
|
public string? ResultFilePath { get; private set; }
|
||||||
|
public bool IsDuplicate { get; private set; }
|
||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{ }
|
{ }
|
||||||
@@ -56,10 +57,14 @@ public class UploadModel(
|
|||||||
if (Result.HasZugferdXml)
|
if (Result.HasZugferdXml)
|
||||||
{
|
{
|
||||||
memStream.Position = 0;
|
memStream.Position = 0;
|
||||||
ImportedInvoice = await zugferdImportService.ImportAsync(memStream, "Upload");
|
ImportedInvoice = await zugferdImportService.ImportAsync(memStream, "Upload", Result.ZugferdGuidelineId);
|
||||||
|
|
||||||
// 3. Result-Package erstellen (nur wenn Import erfolgreich)
|
// Duplikat erkennen: vorhandener Eintrag hat ImportedAt von früher
|
||||||
if (ImportedInvoice is not null)
|
if (ImportedInvoice is not null && ImportedInvoice.ImportedAt < DateTime.UtcNow.AddSeconds(-5))
|
||||||
|
IsDuplicate = true;
|
||||||
|
|
||||||
|
// 3. Result-Package erstellen (nur wenn Import erfolgreich UND kein Duplikat)
|
||||||
|
if (ImportedInvoice is not null && !IsDuplicate)
|
||||||
{
|
{
|
||||||
ResultFilePath = await resultPackageService.CreateResultPackageAsync(
|
ResultFilePath = await resultPackageService.CreateResultPackageAsync(
|
||||||
originalBytes, PdfFile.FileName, ImportedInvoice);
|
originalBytes, PdfFile.FileName, ImportedInvoice);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using DXApp.TemplateKitProject.Data;
|
using DXApp.TemplateKitProject.Data;
|
||||||
using DXApp.TemplateKitProject.Models;
|
using DXApp.TemplateKitProject.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DXApp.TemplateKitProject.Services;
|
namespace DXApp.TemplateKitProject.Services;
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ public class ZugferdImportService(
|
|||||||
AppDbContext db,
|
AppDbContext db,
|
||||||
ILogger<ZugferdImportService> logger)
|
ILogger<ZugferdImportService> logger)
|
||||||
{
|
{
|
||||||
public async Task<ZugferdInvoice?> ImportAsync(Stream pdfStream, string sourceType)
|
public async Task<ZugferdInvoice?> ImportAsync(Stream pdfStream, string sourceType, string guidelineId = "")
|
||||||
{
|
{
|
||||||
var xml = extractor.ExtractXml(pdfStream);
|
var xml = extractor.ExtractXml(pdfStream);
|
||||||
|
|
||||||
@@ -20,11 +21,31 @@ public class ZugferdImportService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invoice = parser.Parse(xml);
|
var invoice = parser.Parse(xml);
|
||||||
|
|
||||||
|
// Duplikatprüfung
|
||||||
|
var duplicate = await db.ZugferdInvoices.FirstOrDefaultAsync(i =>
|
||||||
|
i.InvoiceNumber == invoice.InvoiceNumber &&
|
||||||
|
i.SellerTaxId == invoice.SellerTaxId);
|
||||||
|
|
||||||
|
if (duplicate is not null)
|
||||||
|
{
|
||||||
|
logger.LogWarning(
|
||||||
|
"Duplikat erkannt: Rechnung '{Number}' von '{Seller}' existiert bereits (ID: {Id}).",
|
||||||
|
invoice.InvoiceNumber, invoice.SellerName, duplicate.Id);
|
||||||
|
return duplicate;
|
||||||
|
}
|
||||||
|
|
||||||
invoice.SourceType = sourceType;
|
invoice.SourceType = sourceType;
|
||||||
|
invoice.GuidelineId = guidelineId;
|
||||||
|
invoice.ImportedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
db.ZugferdInvoices.Add(invoice);
|
db.ZugferdInvoices.Add(invoice);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
logger.LogInformation(
|
||||||
|
"Rechnung '{Number}' von '{Seller}' importiert (ID: {Id}).",
|
||||||
|
invoice.InvoiceNumber, invoice.SellerName, invoice.Id);
|
||||||
|
|
||||||
return invoice;
|
return invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user