Compare commits
19 Commits
2ae2bbdaf6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de4e9421af | ||
|
|
b622df4187 | ||
|
|
746465d8fe | ||
|
|
559829882a | ||
|
|
bec9ea2356 | ||
|
|
1cc617de42 | ||
|
|
b515b4f523 | ||
|
|
f19251ac1a | ||
|
|
6dd1fd71da | ||
|
|
762f76c920 | ||
|
|
ba570687b2 | ||
|
|
84a4c182e2 | ||
|
|
a55e53521f | ||
|
|
f7cac8c0a7 | ||
|
|
8065c589bc | ||
|
|
c9ba7912fa | ||
|
|
920dce13d5 | ||
|
|
43d63e975d | ||
|
|
03599addb8 |
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -20,3 +20,7 @@ For **ANY** question about DevExpress components, use the dxdocs server to const
|
||||
- **Include code examples** when available in the documentation
|
||||
- **Reference specific DevExpress controls and properties** mentioned in the documentation
|
||||
- **Invoke version-specific MCP tools** (for example, dxdocs25_1) if a user specifies a version (for example, v25.1)
|
||||
|
||||
- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools.
|
||||
- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
|
||||
- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using DevExtreme.AspNet.Data;
|
||||
using DevExtreme.AspNet.Mvc;
|
||||
using DXApp.TemplateKitProject.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class SampleDataController : Controller
|
||||
{
|
||||
|
||||
[HttpGet]
|
||||
public object Get(DataSourceLoadOptions loadOptions)
|
||||
{
|
||||
return DataSourceLoader.Load(SampleData.Orders, loadOptions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -27,4 +27,8 @@
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="3.0.71" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -6,4 +6,41 @@ namespace DXApp.TemplateKitProject.Data;
|
||||
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<ZugferdInvoice> ZugferdInvoices { get; set; }
|
||||
public DbSet<InvoiceAttachment> InvoiceAttachments { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<ZugferdInvoice>(entity =>
|
||||
{
|
||||
// Index für Performance-Optimierung der Rechnungsliste
|
||||
entity.HasIndex(e => e.ImportedAt)
|
||||
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||
|
||||
// Index für Duplikatprüfung
|
||||
entity.HasIndex(e => new { e.InvoiceNumber, e.SellerTaxId })
|
||||
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||
|
||||
// Decimal-Präzision explizit festlegen (behebt EF Core Warnung)
|
||||
entity.Property(e => e.TotalAmount)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
entity.Property(e => e.TaxAmount)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
// Relationship: One-to-Many mit InvoiceAttachments
|
||||
entity.HasMany(e => e.Attachments)
|
||||
.WithOne(a => a.ZugferdInvoice)
|
||||
.HasForeignKey(a => a.ZugferdInvoiceId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<InvoiceAttachment>(entity =>
|
||||
{
|
||||
// Index für schnelleres Laden der Attachments einer Rechnung
|
||||
entity.HasIndex(e => e.ZugferdInvoiceId)
|
||||
.HasDatabaseName("IX_InvoiceAttachments_ZugferdInvoiceId");
|
||||
});
|
||||
}
|
||||
}
|
||||
91
DXApp.TemplateKitProject/Migrations/20260602114123_AddPerformanceIndexes.Designer.cs
generated
Normal file
91
DXApp.TemplateKitProject/Migrations/20260602114123_AddPerformanceIndexes.Designer.cs
generated
Normal file
@@ -0,0 +1,91 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DXApp.TemplateKitProject.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DXApp.TemplateKitProject.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260602114123_AddPerformanceIndexes")]
|
||||
partial class AddPerformanceIndexes
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.8")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BuyerName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CurrencyCode")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("GuidelineId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Iban")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("ImportedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime>("InvoiceDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("InvoiceNumber")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RawXml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ResultFilePath")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SellerName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SellerTaxId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("TaxAmount")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("TotalAmount")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImportedAt")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||
|
||||
b.HasIndex("InvoiceNumber", "SellerTaxId")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||
|
||||
b.ToTable("ZugferdInvoices");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DXApp.TemplateKitProject.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPerformanceIndexes : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SellerTaxId",
|
||||
table: "ZugferdInvoices",
|
||||
type: "nvarchar(450)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "InvoiceNumber",
|
||||
table: "ZugferdInvoices",
|
||||
type: "nvarchar(450)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ZugferdInvoices_ImportedAt",
|
||||
table: "ZugferdInvoices",
|
||||
column: "ImportedAt");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ZugferdInvoices_InvoiceNumber_SellerTaxId",
|
||||
table: "ZugferdInvoices",
|
||||
columns: new[] { "InvoiceNumber", "SellerTaxId" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ZugferdInvoices_ImportedAt",
|
||||
table: "ZugferdInvoices");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ZugferdInvoices_InvoiceNumber_SellerTaxId",
|
||||
table: "ZugferdInvoices");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SellerTaxId",
|
||||
table: "ZugferdInvoices",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "InvoiceNumber",
|
||||
table: "ZugferdInvoices",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
141
DXApp.TemplateKitProject/Migrations/20260602123528_AddInvoiceAttachments.Designer.cs
generated
Normal file
141
DXApp.TemplateKitProject/Migrations/20260602123528_AddInvoiceAttachments.Designer.cs
generated
Normal file
@@ -0,0 +1,141 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DXApp.TemplateKitProject.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DXApp.TemplateKitProject.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260602123528_AddInvoiceAttachments")]
|
||||
partial class AddInvoiceAttachments
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.8")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.InvoiceAttachment", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("ExtractedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<long>("FileSizeBytes")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("IsZugferdXml")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("OriginalFileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SavedFilePath")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ZugferdInvoiceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ZugferdInvoiceId")
|
||||
.HasDatabaseName("IX_InvoiceAttachments_ZugferdInvoiceId");
|
||||
|
||||
b.ToTable("InvoiceAttachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BuyerName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CurrencyCode")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("GuidelineId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Iban")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("ImportedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime>("InvoiceDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("InvoiceNumber")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RawXml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ResultFilePath")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SellerName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SellerTaxId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("TaxAmount")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("TotalAmount")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImportedAt")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||
|
||||
b.HasIndex("InvoiceNumber", "SellerTaxId")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||
|
||||
b.ToTable("ZugferdInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.InvoiceAttachment", b =>
|
||||
{
|
||||
b.HasOne("DXApp.TemplateKitProject.Models.ZugferdInvoice", "ZugferdInvoice")
|
||||
.WithMany("Attachments")
|
||||
.HasForeignKey("ZugferdInvoiceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ZugferdInvoice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||
{
|
||||
b.Navigation("Attachments");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DXApp.TemplateKitProject.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddInvoiceAttachments : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "InvoiceAttachments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ZugferdInvoiceId = table.Column<int>(type: "int", nullable: false),
|
||||
OriginalFileName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SavedFilePath = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
FileSizeBytes = table.Column<long>(type: "bigint", nullable: false),
|
||||
IsZugferdXml = table.Column<bool>(type: "bit", nullable: false),
|
||||
ExtractedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_InvoiceAttachments", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_InvoiceAttachments_ZugferdInvoices_ZugferdInvoiceId",
|
||||
column: x => x.ZugferdInvoiceId,
|
||||
principalTable: "ZugferdInvoices",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InvoiceAttachments_ZugferdInvoiceId",
|
||||
table: "InvoiceAttachments",
|
||||
column: "ZugferdInvoiceId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "InvoiceAttachments");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,40 @@ namespace DXApp.TemplateKitProject.Migrations
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.InvoiceAttachment", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("ExtractedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<long>("FileSizeBytes")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("IsZugferdXml")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("OriginalFileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SavedFilePath")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ZugferdInvoiceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ZugferdInvoiceId")
|
||||
.HasDatabaseName("IX_InvoiceAttachments_ZugferdInvoiceId");
|
||||
|
||||
b.ToTable("InvoiceAttachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -49,7 +83,7 @@ namespace DXApp.TemplateKitProject.Migrations
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("InvoiceNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RawXml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
@@ -61,7 +95,7 @@ namespace DXApp.TemplateKitProject.Migrations
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SellerTaxId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
@@ -74,8 +108,30 @@ namespace DXApp.TemplateKitProject.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImportedAt")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||
|
||||
b.HasIndex("InvoiceNumber", "SellerTaxId")
|
||||
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||
|
||||
b.ToTable("ZugferdInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.InvoiceAttachment", b =>
|
||||
{
|
||||
b.HasOne("DXApp.TemplateKitProject.Models.ZugferdInvoice", "ZugferdInvoice")
|
||||
.WithMany("Attachments")
|
||||
.HasForeignKey("ZugferdInvoiceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ZugferdInvoice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||
{
|
||||
b.Navigation("Attachments");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
18
DXApp.TemplateKitProject/Models/InvoiceAttachment.cs
Normal file
18
DXApp.TemplateKitProject/Models/InvoiceAttachment.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace DXApp.TemplateKitProject.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Extrahierte Anhänge einer ZUGFeRD-Rechnung (gespeichert in DB)
|
||||
/// </summary>
|
||||
public class InvoiceAttachment
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ZugferdInvoiceId { get; set; }
|
||||
public string OriginalFileName { get; set; } = string.Empty;
|
||||
public string SavedFilePath { get; set; } = string.Empty;
|
||||
public long FileSizeBytes { get; set; }
|
||||
public bool IsZugferdXml { get; set; }
|
||||
public DateTime ExtractedAt { get; set; }
|
||||
|
||||
// Navigation Property
|
||||
public ZugferdInvoice? ZugferdInvoice { get; set; }
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Models
|
||||
{
|
||||
internal static class SampleData
|
||||
{
|
||||
public static List<SampleOrder> Orders = new List<SampleOrder>() {
|
||||
new SampleOrder {
|
||||
OrderID = 10248,
|
||||
OrderDate = new DateTime(1996, 7, 4),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Reims",
|
||||
CustomerName = "Paul Henriot"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10249,
|
||||
OrderDate = new DateTime(1996, 7, 5),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Münster",
|
||||
CustomerName = "Karin Josephs"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10250,
|
||||
OrderDate = new DateTime(1996, 7, 8),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Rio de Janeiro",
|
||||
CustomerName = "Mario Pontes"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10251,
|
||||
OrderDate = new DateTime(1996, 7, 8),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Lyon",
|
||||
CustomerName = "Mary Saveley"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10252,
|
||||
OrderDate = new DateTime(1996, 7, 9),
|
||||
ShipCountry = "Belgium",
|
||||
ShipCity = "Charleroi",
|
||||
CustomerName = "Pascale Cartrain"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10253,
|
||||
OrderDate = new DateTime(1996, 7, 10),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Rio de Janeiro",
|
||||
CustomerName = "Mario Pontes"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10254,
|
||||
OrderDate = new DateTime(1996, 7, 11),
|
||||
ShipCountry = "Switzerland",
|
||||
ShipCity = "Bern",
|
||||
CustomerName = "Yang Wang"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10255,
|
||||
OrderDate = new DateTime(1996, 7, 12),
|
||||
ShipCountry = "Switzerland",
|
||||
ShipCity = "Genève",
|
||||
CustomerName = "Michael Holz"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10256,
|
||||
OrderDate = new DateTime(1996, 7, 15),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Resende",
|
||||
CustomerName = "Paula Parente"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10257,
|
||||
OrderDate = new DateTime(1996, 7, 16),
|
||||
ShipCountry = "Venezuela",
|
||||
ShipCity = "San Cristóbal",
|
||||
CustomerName = "Carlos Hernández"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10258,
|
||||
OrderDate = new DateTime(1996, 7, 17),
|
||||
ShipCountry = "Austria",
|
||||
ShipCity = "Graz",
|
||||
CustomerName = "Roland Mendel"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10259,
|
||||
OrderDate = new DateTime(1996, 7, 18),
|
||||
ShipCountry = "Mexico",
|
||||
ShipCity = "México D.F.",
|
||||
CustomerName = "Francisco Chang"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10260,
|
||||
OrderDate = new DateTime(1996, 7, 19),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Köln",
|
||||
CustomerName = "Henriette Pfalzheim"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10261,
|
||||
OrderDate = new DateTime(1996, 7, 19),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Rio de Janeiro",
|
||||
CustomerName = "Bernardo Batista"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10262,
|
||||
OrderDate = new DateTime(1996, 7, 22),
|
||||
ShipCountry = "USA",
|
||||
ShipCity = "Albuquerque",
|
||||
CustomerName = "Paula Wilson"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10263,
|
||||
OrderDate = new DateTime(1996, 7, 23),
|
||||
ShipCountry = "Austria",
|
||||
ShipCity = "Graz",
|
||||
CustomerName = "Roland Mendel"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10264,
|
||||
OrderDate = new DateTime(1996, 7, 24),
|
||||
ShipCountry = "Sweden",
|
||||
ShipCity = "Bräcke",
|
||||
CustomerName = "Maria Larsson"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10265,
|
||||
OrderDate = new DateTime(1996, 7, 25),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Strasbourg",
|
||||
CustomerName = "Frédérique Citeaux"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10266,
|
||||
OrderDate = new DateTime(1996, 7, 26),
|
||||
ShipCountry = "Finland",
|
||||
ShipCity = "Oulu",
|
||||
CustomerName = "Pirkko Koskitalo"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10267,
|
||||
OrderDate = new DateTime(1996, 7, 29),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "München",
|
||||
CustomerName = "Peter Franken"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10268,
|
||||
OrderDate = new DateTime(1996, 7, 30),
|
||||
ShipCountry = "Venezuela",
|
||||
ShipCity = "Caracas",
|
||||
CustomerName = "Manuel Pereira"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10269,
|
||||
OrderDate = new DateTime(1996, 7, 31),
|
||||
ShipCountry = "USA",
|
||||
ShipCity = "Seattle",
|
||||
CustomerName = "Karl Jablonski"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10270,
|
||||
OrderDate = new DateTime(1996, 8, 1),
|
||||
ShipCountry = "Finland",
|
||||
ShipCity = "Oulu",
|
||||
CustomerName = "Pirkko Koskitalo"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10271,
|
||||
OrderDate = new DateTime(1996, 8, 1),
|
||||
ShipCountry = "USA",
|
||||
ShipCity = "Lander",
|
||||
CustomerName = "Art Braunschweiger"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10272,
|
||||
OrderDate = new DateTime(1996, 8, 2),
|
||||
ShipCountry = "USA",
|
||||
ShipCity = "Albuquerque",
|
||||
CustomerName = "Paula Wilson"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10273,
|
||||
OrderDate = new DateTime(1996, 8, 5),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Cunewalde",
|
||||
CustomerName = "Horst Kloss"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10274,
|
||||
OrderDate = new DateTime(1996, 8, 6),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Reims",
|
||||
CustomerName = "Paul Henriot"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10275,
|
||||
OrderDate = new DateTime(1996, 8, 7),
|
||||
ShipCountry = "Italy",
|
||||
ShipCity = "Bergamo",
|
||||
CustomerName = "Giovanni Rovelli"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10276,
|
||||
OrderDate = new DateTime(1996, 8, 8),
|
||||
ShipCountry = "Mexico",
|
||||
ShipCity = "México D.F.",
|
||||
CustomerName = "Miguel Angel Paolino"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10277,
|
||||
OrderDate = new DateTime(1996, 8, 9),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Leipzig",
|
||||
CustomerName = "Alexander Feuer"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10278,
|
||||
OrderDate = new DateTime(1996, 8, 12),
|
||||
ShipCountry = "Sweden",
|
||||
ShipCity = "Luleå",
|
||||
CustomerName = "Christina Berglund"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10279,
|
||||
OrderDate = new DateTime(1996, 8, 13),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Frankfurt a.M.",
|
||||
CustomerName = "Renate Messner"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10280,
|
||||
OrderDate = new DateTime(1996, 8, 14),
|
||||
ShipCountry = "Sweden",
|
||||
ShipCity = "Luleå",
|
||||
CustomerName = "Christina Berglund"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10281,
|
||||
OrderDate = new DateTime(1996, 8, 14),
|
||||
ShipCountry = "Spain",
|
||||
ShipCity = "Madrid",
|
||||
CustomerName = "Alejandra Camino"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10282,
|
||||
OrderDate = new DateTime(1996, 8, 15),
|
||||
ShipCountry = "Spain",
|
||||
ShipCity = "Madrid",
|
||||
CustomerName = "Alejandra Camino"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10283,
|
||||
OrderDate = new DateTime(1996, 8, 16),
|
||||
ShipCountry = "Venezuela",
|
||||
ShipCity = "Barquisimeto",
|
||||
CustomerName = "Carlos González"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10284,
|
||||
OrderDate = new DateTime(1996, 8, 19),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Frankfurt a.M.",
|
||||
CustomerName = "Renate Messner"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10285,
|
||||
OrderDate = new DateTime(1996, 8, 20),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Cunewalde",
|
||||
CustomerName = "Horst Kloss"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10286,
|
||||
OrderDate = new DateTime(1996, 8, 21),
|
||||
ShipCountry = "Germany",
|
||||
ShipCity = "Cunewalde",
|
||||
CustomerName = "Horst Kloss"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10287,
|
||||
OrderDate = new DateTime(1996, 8, 22),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Rio de Janeiro",
|
||||
CustomerName = "Janete Limeira"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10288,
|
||||
OrderDate = new DateTime(1996, 8, 23),
|
||||
ShipCountry = "Italy",
|
||||
ShipCity = "Reggio Emilia",
|
||||
CustomerName = "Maurizio Moroni"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10289,
|
||||
OrderDate = new DateTime(1996, 8, 26),
|
||||
ShipCountry = "UK",
|
||||
ShipCity = "London",
|
||||
CustomerName = "Victoria Ashworth"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10290,
|
||||
OrderDate = new DateTime(1996, 8, 27),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Sao Paulo",
|
||||
CustomerName = "Pedro Afonso"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10291,
|
||||
OrderDate = new DateTime(1996, 8, 27),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Rio de Janeiro",
|
||||
CustomerName = "Bernardo Batista"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10292,
|
||||
OrderDate = new DateTime(1996, 8, 28),
|
||||
ShipCountry = "Brazil",
|
||||
ShipCity = "Sao Paulo",
|
||||
CustomerName = "Anabela Domingues"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10293,
|
||||
OrderDate = new DateTime(1996, 8, 29),
|
||||
ShipCountry = "Mexico",
|
||||
ShipCity = "México D.F.",
|
||||
CustomerName = "Miguel Angel Paolino"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10294,
|
||||
OrderDate = new DateTime(1996, 8, 30),
|
||||
ShipCountry = "USA",
|
||||
ShipCity = "Albuquerque",
|
||||
CustomerName = "Paula Wilson"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10295,
|
||||
OrderDate = new DateTime(1996, 9, 2),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Reims",
|
||||
CustomerName = "Paul Henriot"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10296,
|
||||
OrderDate = new DateTime(1996, 9, 3),
|
||||
ShipCountry = "Venezuela",
|
||||
ShipCity = "Barquisimeto",
|
||||
CustomerName = "Carlos González"
|
||||
},
|
||||
new SampleOrder {
|
||||
OrderID = 10297,
|
||||
OrderDate = new DateTime(1996, 9, 4),
|
||||
ShipCountry = "France",
|
||||
ShipCity = "Strasbourg",
|
||||
CustomerName = "Frédérique Citeaux"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Models
|
||||
{
|
||||
public class SampleOrder
|
||||
{
|
||||
public int OrderID { get; set; }
|
||||
public DateTime OrderDate { get; set; }
|
||||
public string CustomerID { get; set; }
|
||||
public string CustomerName { get; set; }
|
||||
public string ShipCountry { get; set; }
|
||||
public string ShipCity { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,5 +17,8 @@
|
||||
public string SourceType { get; set; } = string.Empty; // "Upload" oder "Email"
|
||||
public string ResultFilePath { get; set; } = string.Empty; // Pfad der Result-PDF
|
||||
public string GuidelineId { get; set; } = string.Empty; // ZUGFeRD Guideline-ID aus XMP
|
||||
|
||||
// Navigation Property
|
||||
public List<InvoiceAttachment> Attachments { get; set; } = [];
|
||||
}
|
||||
}
|
||||
22
DXApp.TemplateKitProject/Models/ZugferdInvoiceListDto.cs
Normal file
22
DXApp.TemplateKitProject/Models/ZugferdInvoiceListDto.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace DXApp.TemplateKitProject.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight DTO für die Rechnungsliste (ohne RawXml für bessere Performance)
|
||||
/// </summary>
|
||||
public class ZugferdInvoiceListDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string InvoiceNumber { get; set; } = string.Empty;
|
||||
public DateTime InvoiceDate { get; set; }
|
||||
public string SellerName { get; set; } = string.Empty;
|
||||
public string SellerTaxId { get; set; } = string.Empty;
|
||||
public string BuyerName { get; set; } = string.Empty;
|
||||
public decimal TotalAmount { get; set; }
|
||||
public decimal TaxAmount { get; set; }
|
||||
public string CurrencyCode { get; set; } = string.Empty;
|
||||
public string Iban { get; set; } = string.Empty;
|
||||
public DateTime ImportedAt { get; set; }
|
||||
public string SourceType { get; set; } = string.Empty;
|
||||
public string ResultFilePath { get; set; } = string.Empty;
|
||||
public string GuidelineId { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,28 +1,85 @@
|
||||
@page
|
||||
@using DXApp.TemplateKitProject.Models
|
||||
@model DXApp.TemplateKitProject.Pages.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Home";
|
||||
}
|
||||
|
||||
<h2 class="content-block">Home</h2>
|
||||
<div class="content-block">
|
||||
<h2><i class="dx-icon-product"></i> DevExpress TemplateKit – Evaluierungsprojekt</h2>
|
||||
<p class="lead text-muted">Validierung von DevExpress als Ablösung für GdPicture im Rahmen der E-Rechnungsverarbeitung</p>
|
||||
|
||||
<div class="alert alert-primary mt-4">
|
||||
<h4><i class="dx-icon-todo"></i> Projektziel: ZUGFeRD/Factur-X Rechnungsverarbeitung</h4>
|
||||
<p>Diese Anwendung demonstriert die vollständige Verarbeitungskette für elektronische Rechnungen:</p>
|
||||
|
||||
<ol class="mt-3">
|
||||
<li class="mb-2">
|
||||
<strong>Upload & Validierung</strong>
|
||||
<br/>
|
||||
<small class="text-muted">E-Rechnungen im PDF/A-Format hochladen und auf Konformität prüfen</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<strong>Extraktion</strong>
|
||||
<br/>
|
||||
<small class="text-muted">Automatische Erkennung und Extraktion eingebetteter Anhänge (ZUGFeRD-XML, Bilder, Dokumente)</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<strong>Datenverarbeitung</strong>
|
||||
<br/>
|
||||
<small class="text-muted">Parsing der Rechnungsdaten aus dem ZUGFeRD-XML und persistente Speicherung in der Datenbank</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<strong>Workflow-Integration</strong>
|
||||
<br/>
|
||||
<small class="text-muted">Hier werden Workflow-Schritte durchlaufen und der Ergebnisbericht erstellt.</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<strong>Ausgabe-Generierung</strong>
|
||||
<br/>
|
||||
<small class="text-muted">Erstellung einer Result-PDF mit Verarbeitungsstempel und angehängtem Ergebnisbericht</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<strong>Visualisierung</strong>
|
||||
<br/>
|
||||
<small class="text-muted">Interaktive Anzeige aller Anhänge (XML mit Syntax-Highlighting, PDF-Viewer, Bilder)</small>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@(Html.DevExtreme().DataGrid<SampleOrder>()
|
||||
.ElementAttr(new { @class = "dx-card wide-card" })
|
||||
.DataSource(d => d.Mvc().Controller("SampleData").LoadAction("Get").Key("OrderID"))
|
||||
.ShowBorders(false)
|
||||
.FilterRow(f => f.Visible(true))
|
||||
.FocusedRowEnabled(true)
|
||||
.FocusedRowIndex(0)
|
||||
.ColumnAutoWidth(true)
|
||||
.ColumnHidingEnabled(true)
|
||||
.Columns(columns => {
|
||||
columns.AddFor(m => m.OrderID);
|
||||
columns.AddFor(m => m.OrderDate);
|
||||
columns.AddFor(m => m.CustomerName);
|
||||
columns.AddFor(m => m.ShipCountry);
|
||||
columns.AddFor(m => m.ShipCity);
|
||||
})
|
||||
.Paging(p => p.PageSize(10))
|
||||
.Pager(p => p
|
||||
.ShowPageSizeSelector(true)
|
||||
.AllowedPageSizes(new[] { 5, 10, 20 })
|
||||
.ShowInfo(true)
|
||||
)
|
||||
)
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="dx-icon-upload"></i> Rechnungen hochladen</h5>
|
||||
<p class="card-text">Laden Sie ZUGFeRD-konforme E-Rechnungen hoch und starten Sie die automatische Verarbeitung.</p>
|
||||
<a href="/Invoices/Upload" class="btn btn-primary">
|
||||
<i class="dx-icon-upload"></i> Zum Upload
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="dx-icon-chart"></i> Rechnungsübersicht</h5>
|
||||
<p class="card-text">Zeigen Sie alle importierten Rechnungen an und greifen Sie auf Details und Anhänge zu.</p>
|
||||
<a href="/Invoices" class="btn btn-secondary">
|
||||
<i class="dx-icon-doc"></i> Zur Übersicht
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-light mt-4">
|
||||
<h6 class="mb-2"><i class="dx-icon-preferences"></i> Technologie-Stack</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="badge bg-secondary">ASP.NET Core 8.0</span>
|
||||
<span class="badge bg-secondary">DevExpress v25.2</span>
|
||||
<span class="badge bg-secondary">Entity Framework Core</span>
|
||||
<span class="badge bg-secondary">PDF.js</span>
|
||||
<span class="badge bg-secondary">CodeMirror</span>
|
||||
<span class="badge bg-secondary">SQL Server</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,16 +2,23 @@
|
||||
@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>
|
||||
<a href="/Invoices" class="btn btn-secondary mb-3">← Zurück zur Liste</a>
|
||||
<h2><i class="dx-icon-doc"></i> Rechnungsdetails</h2>
|
||||
<a href="/Invoices" class="btn btn-secondary mb-3"><i class="dx-icon-back"></i> Zurück zur Liste</a>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Invoice?.ResultFilePath))
|
||||
{
|
||||
<button class="btn btn-primary mb-3 ms-2"
|
||||
onclick="openPdfViewer(@Model.Invoice.Id)">
|
||||
📄 PDF anzeigen
|
||||
<i class="dx-icon-pdffile"></i> Ergebnis anzeigen
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -50,6 +57,51 @@ else
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@* Anhänge-Sektion *@
|
||||
@if (Model.Invoice.Attachments.Any())
|
||||
{
|
||||
<h4 class="mt-4"><i class="dx-icon-attach"></i> Anhänge (@Model.Invoice.Attachments.Count)</h4>
|
||||
<div class="list-group">
|
||||
@foreach (var attachment in Model.Invoice.Attachments)
|
||||
{
|
||||
var icon = "dx-icon-file";
|
||||
var extension = System.IO.Path.GetExtension(attachment.OriginalFileName).ToLowerInvariant();
|
||||
icon = extension switch
|
||||
{
|
||||
".xml" => "dx-icon-exportxlsx",
|
||||
".pdf" => "dx-icon-pdffile",
|
||||
".jpg" or ".jpeg" or ".png" or ".gif" => "dx-icon-image",
|
||||
".txt" => "dx-icon-txtfile",
|
||||
_ => "dx-icon-file"
|
||||
};
|
||||
|
||||
<a href="javascript:void(0);"
|
||||
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"
|
||||
onclick="openAttachmentViewer('@attachment.OriginalFileName', '@Uri.EscapeDataString(attachment.SavedFilePath)', '@extension')">
|
||||
<div>
|
||||
<i class="@icon me-2"></i>
|
||||
<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"><i class="dx-icon-attach"></i> Anhänge</h4>
|
||||
<div class="alert alert-info">Keine Anhänge extrahiert.</div>
|
||||
}
|
||||
|
||||
@(Html.DevExtreme().Popup()
|
||||
.ID("pdf-viewer-popup")
|
||||
.Title("PDF Viewer")
|
||||
@@ -57,12 +109,54 @@ else
|
||||
.Height("90%")
|
||||
.ShowCloseButton(true)
|
||||
.OnHiding("onPdfPopupHiding")
|
||||
.Shading(true)
|
||||
.ShadingColor("rgba(0, 0, 0, 0.5)")
|
||||
.ContentTemplate(new JS(@"function() {
|
||||
return '<iframe id=""pdf-iframe"" style=""width:100%;height:100%;border:none;""></iframe>';
|
||||
}"))
|
||||
)
|
||||
|
||||
@(Html.DevExtreme().Popup()
|
||||
.ID("attachment-viewer-popup")
|
||||
.Title("Anhang")
|
||||
.Width("90%")
|
||||
.Height("90%")
|
||||
.ShowCloseButton(true)
|
||||
.OnHiding("onAttachmentPopupHiding")
|
||||
.Shading(true)
|
||||
.ShadingColor("rgba(0, 0, 0, 0.5)")
|
||||
.ContentTemplate(new JS(@"function() {
|
||||
return '<div id=""attachment-content"" style=""width:100%;height:100%;overflow:hidden;display:flex;flex-direction:column;""></div>';
|
||||
}"))
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
/* Z-Index für PDF-Viewer Popup erhöhen - muss höher sein als layout-header (1505) */
|
||||
.dx-popup-wrapper.dx-overlay-wrapper {
|
||||
z-index: 10500 !important;
|
||||
}
|
||||
|
||||
.dx-overlay-shader {
|
||||
z-index: 10499 !important;
|
||||
}
|
||||
|
||||
/* Spezifisch für unser PDF-Popup */
|
||||
#pdf-viewer-popup .dx-overlay-content {
|
||||
z-index: 10500 !important;
|
||||
}
|
||||
|
||||
/* CodeMirror soll volle Höhe des Popups nutzen */
|
||||
#attachment-content {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
#attachment-content .CodeMirror {
|
||||
height: 100% !important;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function openPdfViewer(invoiceId) {
|
||||
var pdfUrl = window.location.origin + '/Invoices/ViewPdf?id=' + invoiceId;
|
||||
@@ -70,9 +164,17 @@ else
|
||||
|
||||
var popup = $('#pdf-viewer-popup').dxPopup('instance');
|
||||
|
||||
// Z-Index explizit setzen (höher als layout-header mit 1505)
|
||||
popup.option('container', undefined); // Default container verwenden
|
||||
popup.option('position', { my: 'center', at: 'center', of: window });
|
||||
|
||||
// onShown sicherstellen dass iframe im DOM ist
|
||||
popup.option('onShown', function () {
|
||||
$('#pdf-iframe').attr('src', viewerUrl);
|
||||
|
||||
// Z-Index nach dem Öffnen nochmal sicherstellen
|
||||
$('.dx-popup-wrapper').css('z-index', '10500');
|
||||
$('.dx-overlay-shader').css('z-index', '10499');
|
||||
});
|
||||
|
||||
popup.show();
|
||||
@@ -82,4 +184,111 @@ else
|
||||
// iframe src leeren beim Schließen → verhindert dass PDF im Hintergrund weiter läuft
|
||||
$('#pdf-iframe').attr('src', '');
|
||||
}
|
||||
|
||||
function openAttachmentViewer(fileName, encodedFilePath, extension) {
|
||||
var filePath = decodeURIComponent(encodedFilePath);
|
||||
var popup = $('#attachment-viewer-popup').dxPopup('instance');
|
||||
var $content = $('#attachment-content');
|
||||
|
||||
// Popup-Titel setzen
|
||||
popup.option('title', fileName);
|
||||
|
||||
// Z-Index setzen
|
||||
popup.option('container', undefined);
|
||||
popup.option('position', { my: 'center', at: 'center', of: window });
|
||||
|
||||
// Content basierend auf Dateityp laden
|
||||
popup.option('onShown', function () {
|
||||
$content.html('<div class="text-center p-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Laden...</span></div></div>');
|
||||
|
||||
// Z-Index sicherstellen
|
||||
$('.dx-popup-wrapper').css('z-index', '10500');
|
||||
$('.dx-overlay-shader').css('z-index', '10499');
|
||||
|
||||
if (extension === '.pdf') {
|
||||
// PDF mit PDF.js anzeigen
|
||||
var pdfUrl = window.location.origin + '/Invoices/ViewAttachment?handler=Download&filePath=' + encodedFilePath;
|
||||
var viewerUrl = '/js/pdfjs/web/viewer.html?file=' + encodeURIComponent(pdfUrl);
|
||||
$content.html('<iframe style="width:100%;height:100%;border:none;" src="' + viewerUrl + '"></iframe>');
|
||||
}
|
||||
else if (extension === '.xml' || extension === '.txt') {
|
||||
// Text/XML laden und mit Syntax-Highlighting anzeigen
|
||||
$.get('/Invoices/ViewAttachment?filePath=' + encodedFilePath, function(data) {
|
||||
if (extension === '.xml') {
|
||||
// CodeMirror für XML - Container leeren
|
||||
$content.html('');
|
||||
|
||||
// CodeMirror laden (falls noch nicht geladen)
|
||||
if (typeof CodeMirror === 'undefined') {
|
||||
$('<link>')
|
||||
.attr('rel', 'stylesheet')
|
||||
.attr('href', 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css')
|
||||
.appendTo('head');
|
||||
$('<link>')
|
||||
.attr('rel', 'stylesheet')
|
||||
.attr('href', 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css')
|
||||
.appendTo('head');
|
||||
|
||||
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js', function() {
|
||||
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/xml/xml.min.js', function() {
|
||||
initCodeMirror(data);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
initCodeMirror(data);
|
||||
}
|
||||
|
||||
function initCodeMirror(content) {
|
||||
// CodeMirror direkt in den Container einfügen
|
||||
var editor = CodeMirror($content[0], {
|
||||
value: content,
|
||||
mode: 'xml',
|
||||
lineNumbers: true,
|
||||
readOnly: true,
|
||||
theme: 'monokai',
|
||||
lineWrapping: true,
|
||||
viewportMargin: Infinity
|
||||
});
|
||||
|
||||
// Höhe explizit setzen nach kurzer Verzögerung
|
||||
setTimeout(function() {
|
||||
editor.setSize('100%', '100%');
|
||||
editor.refresh();
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
// Plain Text
|
||||
$content.html('<pre style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; height: 100%; overflow: auto; margin: 0;">' +
|
||||
$('<div>').text(data).html() + '</pre>');
|
||||
}
|
||||
}).fail(function() {
|
||||
$content.html('<div class="alert alert-danger m-3">Fehler beim Laden der Datei.</div>');
|
||||
});
|
||||
}
|
||||
else if (extension === '.jpg' || extension === '.jpeg' || extension === '.png' || extension === '.gif') {
|
||||
// Bild anzeigen
|
||||
var imageUrl = window.location.origin + '/Invoices/ViewAttachment?handler=Download&filePath=' + encodedFilePath;
|
||||
$content.html('<div class="text-center p-3" style="height: 100%; display: flex; align-items: center; justify-content: center;">' +
|
||||
'<img src="' + imageUrl + '" alt="' + fileName + '" class="img-fluid" style="max-height: 100%; max-width: 100%; object-fit: contain;">' +
|
||||
'</div>');
|
||||
}
|
||||
else {
|
||||
// Nicht unterstützter Typ → Download anbieten
|
||||
var downloadUrl = window.location.origin + '/Invoices/ViewAttachment?handler=Download&filePath=' + encodedFilePath;
|
||||
$content.html('<div class="alert alert-info m-3">' +
|
||||
'<h5>Dieser Dateityp kann nicht angezeigt werden.</h5>' +
|
||||
'<p>Bitte laden Sie die Datei herunter:</p>' +
|
||||
'<a href="' + downloadUrl + '" class="btn btn-primary" download="' + fileName + '">' +
|
||||
'<i class="dx-icon-download"></i> Datei herunterladen' +
|
||||
'</a></div>');
|
||||
}
|
||||
});
|
||||
|
||||
popup.show();
|
||||
}
|
||||
|
||||
function onAttachmentPopupHiding() {
|
||||
// Content leeren
|
||||
$('#attachment-content').html('');
|
||||
}
|
||||
</script>
|
||||
@@ -12,7 +12,9 @@ public class DetailsModel(AppDbContext db) : PageModel
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(int id)
|
||||
{
|
||||
Invoice = await db.ZugferdInvoices.FirstOrDefaultAsync(i => i.Id == id);
|
||||
Invoice = await db.ZugferdInvoices
|
||||
.Include(i => i.Attachments)
|
||||
.FirstOrDefaultAsync(i => i.Id == id);
|
||||
|
||||
if (Invoice is null)
|
||||
return NotFound();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@(Html.DevExtreme().DataGrid<DXApp.TemplateKitProject.Models.ZugferdInvoice>()
|
||||
@(Html.DevExtreme().DataGrid<DXApp.TemplateKitProject.Models.ZugferdInvoiceListDto>()
|
||||
.DataSource(Model.Invoices)
|
||||
.KeyExpr("Id")
|
||||
.ShowBorders(true)
|
||||
|
||||
@@ -7,12 +7,30 @@ namespace DXApp.TemplateKitProject.Pages.Invoices;
|
||||
|
||||
public class IndexModel(AppDbContext db) : PageModel
|
||||
{
|
||||
public List<ZugferdInvoice> Invoices { get; private set; } = [];
|
||||
public List<ZugferdInvoiceListDto> Invoices { get; private set; } = [];
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
Invoices = await db.ZugferdInvoices
|
||||
.OrderByDescending(i => i.ImportedAt)
|
||||
.Select(i => new ZugferdInvoiceListDto
|
||||
{
|
||||
Id = i.Id,
|
||||
InvoiceNumber = i.InvoiceNumber,
|
||||
InvoiceDate = i.InvoiceDate,
|
||||
SellerName = i.SellerName,
|
||||
SellerTaxId = i.SellerTaxId,
|
||||
BuyerName = i.BuyerName,
|
||||
TotalAmount = i.TotalAmount,
|
||||
TaxAmount = i.TaxAmount,
|
||||
CurrencyCode = i.CurrencyCode,
|
||||
Iban = i.Iban,
|
||||
ImportedAt = i.ImportedAt,
|
||||
SourceType = i.SourceType,
|
||||
ResultFilePath = i.ResultFilePath,
|
||||
GuidelineId = i.GuidelineId
|
||||
// RawXml wird NICHT geladen ? Performance-Optimierung!
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,29 @@ public class UploadModel(
|
||||
ImportedInvoice.ResultFilePath = ResultFilePath;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// 4. Attachments in DB speichern
|
||||
if (Result.HasAttachments)
|
||||
{
|
||||
foreach (var attachment in Result.Attachments)
|
||||
{
|
||||
var invoiceAttachment = new InvoiceAttachment
|
||||
{
|
||||
ZugferdInvoiceId = ImportedInvoice.Id,
|
||||
OriginalFileName = attachment.OriginalFileName,
|
||||
SavedFilePath = attachment.SavedFilePath,
|
||||
FileSizeBytes = attachment.FileSizeBytes,
|
||||
IsZugferdXml = attachment.IsZugferdXml,
|
||||
ExtractedAt = DateTime.UtcNow
|
||||
};
|
||||
db.InvoiceAttachments.Add(invoiceAttachment);
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
logger.LogInformation(
|
||||
"Rechnung '{InvoiceNumber}': {Count} Anhang/Anhänge in DB gespeichert.",
|
||||
ImportedInvoice.InvoiceNumber, Result.Attachments.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
@page
|
||||
@using DXApp.TemplateKitProject.Services
|
||||
@model DXApp.TemplateKitProject.Pages.Invoices.ViewAttachmentModel
|
||||
@{
|
||||
ViewData["Title"] = $"Attachment: {Model.FileName}";
|
||||
}
|
||||
|
||||
<h2>?? Attachment: @Model.FileName</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<a href="/Invoices/Details" class="btn btn-secondary">? Zurück</a>
|
||||
<a href="?handler=Download&filePath=@Request.Query["filePath"]"
|
||||
class="btn btn-primary">
|
||||
?? Download
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@switch (Model.ViewerType)
|
||||
{
|
||||
case AttachmentViewerType.Pdf:
|
||||
<div class="alert alert-info">
|
||||
PDF-Dateien können Sie über die Result-PDF-Funktion anzeigen.
|
||||
</div>
|
||||
break;
|
||||
|
||||
case AttachmentViewerType.Xml:
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<strong>XML-Inhalt</strong> (ZUGFeRD/Factur-X)
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<textarea id="xml-viewer" style="display:none;">@Model.TextContent</textarea>
|
||||
<div id="xml-rendered"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/xml/xml.min.js"></script>
|
||||
<script>
|
||||
var editor = CodeMirror(document.getElementById('xml-rendered'), {
|
||||
value: document.getElementById('xml-viewer').value,
|
||||
mode: 'xml',
|
||||
lineNumbers: true,
|
||||
readOnly: true,
|
||||
theme: 'monokai',
|
||||
lineWrapping: true
|
||||
});
|
||||
</script>
|
||||
break;
|
||||
|
||||
case AttachmentViewerType.Text:
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
<strong>Text-Inhalt</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; max-height: 600px; overflow: auto;">@Model.TextContent</pre>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case AttachmentViewerType.Image:
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<img src="?handler=Download&filePath=@Request.Query["filePath"]"
|
||||
alt="@Model.FileName"
|
||||
class="img-fluid"
|
||||
style="max-height: 800px;">
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case AttachmentViewerType.Word:
|
||||
<div class="alert alert-warning">
|
||||
<strong>Word-Dokumente können nicht direkt angezeigt werden.</strong>
|
||||
<p>Bitte laden Sie die Datei herunter oder implementieren Sie eine Konvertierung zu PDF.</p>
|
||||
</div>
|
||||
break;
|
||||
|
||||
default:
|
||||
<div class="alert alert-warning">
|
||||
<strong>Dieser Dateityp kann nicht angezeigt werden.</strong>
|
||||
<p>Bitte laden Sie die Datei herunter.</p>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using DXApp.TemplateKitProject.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using System.Text;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Pages.Invoices;
|
||||
|
||||
public class ViewAttachmentModel(AttachmentViewerService viewerService) : PageModel
|
||||
{
|
||||
public string FileName { get; private set; } = string.Empty;
|
||||
public AttachmentViewerType ViewerType { get; private set; }
|
||||
public string? TextContent { get; private set; }
|
||||
|
||||
public IActionResult OnGet(string filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath))
|
||||
return NotFound();
|
||||
|
||||
FileName = Path.GetFileName(filePath);
|
||||
ViewerType = viewerService.DetermineViewerType(FileName);
|
||||
|
||||
// Für Text/XML: Inhalt direkt als Content zurückgeben (für AJAX)
|
||||
if (ViewerType == AttachmentViewerType.Xml || ViewerType == AttachmentViewerType.Text)
|
||||
{
|
||||
TextContent = System.IO.File.ReadAllText(filePath, Encoding.UTF8);
|
||||
|
||||
// Wenn Request von AJAX kommt (Accept: */* oder text/plain)
|
||||
if (Request.Headers.Accept.ToString().Contains("*/*") ||
|
||||
Request.Headers["X-Requested-With"] == "XMLHttpRequest")
|
||||
{
|
||||
return Content(TextContent, "text/plain", Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public IActionResult OnGetDownload(string filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath))
|
||||
return NotFound();
|
||||
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
var mimeType = viewerService.GetMimeType(fileName);
|
||||
var bytes = System.IO.File.ReadAllBytes(filePath);
|
||||
|
||||
return File(bytes, mimeType, fileName);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ builder.Services.AddScoped<PdfResultPackageService>();
|
||||
builder.Services.AddScoped<ZugferdExtractorService>();
|
||||
builder.Services.AddScoped<ZugferdParserService>();
|
||||
builder.Services.AddScoped<ZugferdImportService>();
|
||||
builder.Services.AddScoped<AttachmentViewerService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
49
DXApp.TemplateKitProject/Services/AttachmentViewerService.cs
Normal file
49
DXApp.TemplateKitProject/Services/AttachmentViewerService.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using DXApp.TemplateKitProject.Models;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Services;
|
||||
|
||||
public class AttachmentViewerService
|
||||
{
|
||||
public AttachmentViewerType DetermineViewerType(string fileName)
|
||||
{
|
||||
var extension = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
|
||||
return extension switch
|
||||
{
|
||||
".pdf" => AttachmentViewerType.Pdf,
|
||||
".xml" => AttachmentViewerType.Xml,
|
||||
".txt" => AttachmentViewerType.Text,
|
||||
".jpg" or ".jpeg" or ".png" or ".gif" => AttachmentViewerType.Image,
|
||||
".docx" or ".doc" => AttachmentViewerType.Word,
|
||||
_ => AttachmentViewerType.Download
|
||||
};
|
||||
}
|
||||
|
||||
public string GetMimeType(string fileName)
|
||||
{
|
||||
var extension = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
|
||||
return extension switch
|
||||
{
|
||||
".pdf" => "application/pdf",
|
||||
".xml" => "application/xml",
|
||||
".txt" => "text/plain",
|
||||
".jpg" or ".jpeg" => "image/jpeg",
|
||||
".png" => "image/png",
|
||||
".gif" => "image/gif",
|
||||
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".doc" => "application/msword",
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum AttachmentViewerType
|
||||
{
|
||||
Pdf, // PDF.js Viewer
|
||||
Xml, // Syntax-highlighted XML
|
||||
Text, // Plain Text
|
||||
Image, // <img> Tag
|
||||
Word, // Konvertierung zu PDF (optional)
|
||||
Download // Nur Download-Button
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class PdfResultPackageService(
|
||||
converter.SaveDocument(convertedStream);
|
||||
convertedStream.Position = 0;
|
||||
|
||||
// Schritt 3: Anhang einbetten
|
||||
// Schritt 3: Hauptdokument laden
|
||||
using var processor = new PdfDocumentProcessor();
|
||||
processor.LoadDocument(convertedStream);
|
||||
|
||||
@@ -68,15 +68,23 @@ public class PdfResultPackageService(
|
||||
processor.Document.Subject = string.Empty;
|
||||
processor.Document.Keywords = string.Empty;
|
||||
|
||||
processor.AttachFile(new PdfFileAttachment
|
||||
// Schritt 3a: Berichts-Seiten ans Ende anhängen
|
||||
int reportPageCount;
|
||||
using (var reportStream = new FileStream(reportPath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
FileName = Path.GetFileName(reportPath),
|
||||
Description = "Ergebnisbericht",
|
||||
MimeType = "application/pdf",
|
||||
Relationship = PdfAssociatedFileRelationship.Supplement,
|
||||
CreationDate = DateTime.Now,
|
||||
Data = File.ReadAllBytes(reportPath)
|
||||
});
|
||||
// Temporäres Dokument öffnen um Seitenanzahl zu ermitteln
|
||||
using var tempProcessor = new PdfDocumentProcessor();
|
||||
tempProcessor.LoadDocument(reportStream);
|
||||
reportPageCount = tempProcessor.Document.Pages.Count;
|
||||
|
||||
logger.LogDebug(
|
||||
"Hänge {PageCount} Seite(n) aus Bericht an das Dokument an.",
|
||||
reportPageCount);
|
||||
|
||||
// Stream zurücksetzen und ans Hauptdokument anhängen
|
||||
reportStream.Position = 0;
|
||||
processor.AppendDocument(reportStream);
|
||||
}
|
||||
|
||||
// Schritt 4: Stempel auf Seite 1 zeichnen
|
||||
var firstPage = processor.Document.Pages[0];
|
||||
@@ -126,10 +134,15 @@ public class PdfResultPackageService(
|
||||
processor.Document.Pages[0], 96, 96);
|
||||
}
|
||||
|
||||
// Schritt 4: Speichern
|
||||
// Schritt 5: Speichern
|
||||
try
|
||||
{
|
||||
processor.SaveDocument(outputPath);
|
||||
|
||||
logger.LogInformation(
|
||||
"Result-PDF erstellt mit {TotalPages} Seiten (Original + {ReportPages} Berichtseiten).",
|
||||
processor.Document.Pages.Count,
|
||||
reportPageCount);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
89
DXApp.TemplateKitProject/package-lock.json
generated
89
DXApp.TemplateKitProject/package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -28,6 +29,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@devexpress/utils/-/utils-2.1.1.tgz",
|
||||
"integrity": "sha512-hlemXR3L0yDPsMdhTQl9EtjiEYxIHklH7I4RKPkOhfrF5+jb4f3kkfvbKT7nA9aoFEHPPF0/hll43gOBpUpY0g==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "2.3.1"
|
||||
}
|
||||
@@ -37,6 +39,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
@@ -47,6 +50,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
@@ -57,6 +61,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@@ -65,13 +70,15 @@
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
@@ -82,6 +89,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.2.tgz",
|
||||
"integrity": "sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
@@ -93,6 +101,7 @@
|
||||
"integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
@@ -102,19 +111,22 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/devexpress-diagram": {
|
||||
"version": "2.2.29",
|
||||
"resolved": "https://registry.npmjs.org/devexpress-diagram/-/devexpress-diagram-2.2.29.tgz",
|
||||
"integrity": "sha512-6QS1zKP736QlIU6lMeZdq52Fe85BSa6vh1TwDIxSAKNwT6gHnGKm5paI71epRJ9H6GC4SLtv/zhlH8QCjwNnXQ==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@devexpress/utils": "^2.1.1",
|
||||
"es6-object-assign": "^1.1.0"
|
||||
@@ -125,6 +137,7 @@
|
||||
"resolved": "https://registry.npmjs.org/devexpress-gantt/-/devexpress-gantt-4.1.68.tgz",
|
||||
"integrity": "sha512-pilTDWwCv1EthcCV9uFj5krbSkA3MdjiKCgJdCmGaTA+lDNiWP5Xc2JzNK1sOOTMUmzN+3h7kAtcLxzTfjWTRg==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@devexpress/utils": "^2.1.1",
|
||||
"tslib": "2.3.1"
|
||||
@@ -175,6 +188,7 @@
|
||||
"resolved": "https://registry.npmjs.org/devextreme-quill/-/devextreme-quill-1.7.9.tgz",
|
||||
"integrity": "sha512-UEkR16+I/7P/4+7dUmc65lv+VRdRGk0kFvZXgHnM2UFcEBKqm1kVDdVPBolGRFwuOFuDttJxp2SXUAyiuSC8wA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-js": "^3.34.0",
|
||||
"eventemitter3": "^4.0.7",
|
||||
@@ -188,25 +202,29 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
||||
"integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
||||
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/inferno": {
|
||||
"version": "8.2.3",
|
||||
@@ -214,6 +232,7 @@
|
||||
"integrity": "sha512-LMeRlCe+RlXw8kHCLyOWRk2PsZ3Fo4jkESyAR1g4FfPT48N78i11YhTVXW2ukCx5MFjv+qrfa73JzJWU9sg4CQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.2",
|
||||
"inferno-vnode-flags": "8.2.3",
|
||||
@@ -229,6 +248,7 @@
|
||||
"resolved": "https://registry.npmjs.org/inferno-create-element/-/inferno-create-element-8.2.3.tgz",
|
||||
"integrity": "sha512-YEwX4OiFlgeTutvE16uCGxkaSVwZ1DklpAPX8okjVsGaLIWQrM8QIQFxn3mTLWSu70Uea+afQfKL5wE4hxn39Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"inferno": "8.2.3"
|
||||
}
|
||||
@@ -238,6 +258,7 @@
|
||||
"resolved": "https://registry.npmjs.org/inferno-hydrate/-/inferno-hydrate-8.2.3.tgz",
|
||||
"integrity": "sha512-AyCiswnjYg7D9veJdjiQg06Npp0/iXKhwOm2hjoY3cjadT3fIdz2XtDElLB7imU4icuJ3xOmXA8FgIfnSJfHrQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"inferno": "8.2.3"
|
||||
}
|
||||
@@ -246,19 +267,22 @@
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/inferno-vnode-flags/-/inferno-vnode-flags-8.2.3.tgz",
|
||||
"integrity": "sha512-dfC0MIwFv9PCbZCUsuk9ISejFS3fKJODC0rZ/LjxxzE+OrCk+PMwPLsUnGU6O9/jbBnPACVz1BkACDf5LWgU5Q==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
@@ -271,6 +295,7 @@
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
@@ -283,6 +308,7 @@
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
@@ -291,26 +317,30 @@
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/opencollective-postinstall": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
|
||||
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"opencollective-postinstall": "index.js"
|
||||
}
|
||||
@@ -319,19 +349,22 @@
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
"license": "(MIT AND Zlib)",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-2.0.1.tgz",
|
||||
"integrity": "sha512-VBKrlEoZCBD+iwoeag0QTtY1Cti+Ma4nLpVYcc/uus/wHhMsPOi5InH3RL1s4aekahPZpabcS2ToKyGf7RMH/g==",
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -343,13 +376,15 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
||||
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-diff": "^1.3.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
@@ -364,6 +399,7 @@
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@@ -379,6 +415,7 @@
|
||||
"resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz",
|
||||
"integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
@@ -387,25 +424,29 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@@ -414,13 +455,15 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/unplugin": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz",
|
||||
"integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"picomatch": "^4.0.3",
|
||||
@@ -434,13 +477,15 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user