Compare commits
25 Commits
9adc9ac4ed
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de4e9421af | ||
|
|
b622df4187 | ||
|
|
746465d8fe | ||
|
|
559829882a | ||
|
|
bec9ea2356 | ||
|
|
1cc617de42 | ||
|
|
b515b4f523 | ||
|
|
f19251ac1a | ||
|
|
6dd1fd71da | ||
|
|
762f76c920 | ||
|
|
ba570687b2 | ||
|
|
84a4c182e2 | ||
|
|
a55e53521f | ||
|
|
f7cac8c0a7 | ||
|
|
8065c589bc | ||
|
|
c9ba7912fa | ||
|
|
920dce13d5 | ||
|
|
43d63e975d | ||
|
|
03599addb8 | ||
|
|
2ae2bbdaf6 | ||
|
|
a4c33e6bac | ||
|
|
346750e933 | ||
|
|
a087baa089 | ||
|
|
98226f239b | ||
|
|
1144c45826 |
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>
|
||||
|
||||
@(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="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>
|
||||
|
||||
<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,10 +2,25 @@
|
||||
@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)">
|
||||
<i class="dx-icon-pdffile"></i> Ergebnis anzeigen
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (Model.Invoice is null)
|
||||
{
|
||||
@@ -41,4 +56,239 @@ else
|
||||
</td>
|
||||
</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")
|
||||
.Width("90%")
|
||||
.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;
|
||||
var viewerUrl = '/js/pdfjs/web/viewer.html?file=' + encodeURIComponent(pdfUrl);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function onPdfPopupHiding() {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
2
DXApp.TemplateKitProject/Pages/Invoices/ViewPdf.cshtml
Normal file
2
DXApp.TemplateKitProject/Pages/Invoices/ViewPdf.cshtml
Normal file
@@ -0,0 +1,2 @@
|
||||
@page
|
||||
@model DXApp.TemplateKitProject.Pages.Invoices.ViewPdfModel
|
||||
24
DXApp.TemplateKitProject/Pages/Invoices/ViewPdf.cshtml.cs
Normal file
24
DXApp.TemplateKitProject/Pages/Invoices/ViewPdf.cshtml.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using DXApp.TemplateKitProject.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DXApp.TemplateKitProject.Pages.Invoices;
|
||||
|
||||
public class ViewPdfModel(AppDbContext db) : PageModel
|
||||
{
|
||||
public async Task<IActionResult> OnGetAsync(int id)
|
||||
{
|
||||
var invoice = await db.ZugferdInvoices
|
||||
.FirstOrDefaultAsync(i => i.Id == id);
|
||||
|
||||
if (invoice is null || string.IsNullOrEmpty(invoice.ResultFilePath))
|
||||
return NotFound();
|
||||
|
||||
if (!System.IO.File.Exists(invoice.ResultFilePath))
|
||||
return NotFound();
|
||||
|
||||
var bytes = await System.IO.File.ReadAllBytesAsync(invoice.ResultFilePath);
|
||||
return File(bytes, "application/pdf");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -28,7 +29,13 @@ if (!app.Environment.IsDevelopment())
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
// MIME-Types für PDF.js registrieren (einmaliger UseStaticFiles-Aufruf)
|
||||
var provider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider();
|
||||
provider.Mappings[".mjs"] = "text/javascript";
|
||||
provider.Mappings[".ftl"] = "text/plain";
|
||||
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthorization();
|
||||
app.MapRazorPages();
|
||||
|
||||
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
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class PdfAttachmentExtractorService(
|
||||
};
|
||||
result.PdfAWarning = compatibility == PdfACompatibility.None;
|
||||
|
||||
logger.LogInformation(
|
||||
logger.LogDebug(
|
||||
"PDF '{FileName}': Konformität = {Level}",
|
||||
sourceFileName, result.PdfALevel);
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PdfAttachmentExtractorService(
|
||||
{
|
||||
result.ZugferdGuidelineId = ExtractGuidelineId(xmpData);
|
||||
if (!string.IsNullOrEmpty(result.ZugferdGuidelineId))
|
||||
logger.LogInformation(
|
||||
logger.LogDebug(
|
||||
"PDF '{FileName}': Guideline-ID = {GuidelineId}",
|
||||
sourceFileName, result.ZugferdGuidelineId);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class PdfAttachmentExtractorService(
|
||||
|
||||
var isZugferd = IsZugferdXml(attachment.FileName);
|
||||
|
||||
logger.LogInformation(
|
||||
logger.LogDebug(
|
||||
" → Gespeichert: '{FileName}' ({Bytes} Bytes){Zugferd}",
|
||||
safeFileName, data.Length,
|
||||
isZugferd ? " [ZUGFeRD/Factur-X XML]" : string.Empty);
|
||||
|
||||
@@ -21,7 +21,7 @@ public class PdfResultPackageService(
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
logger.LogDebug(
|
||||
"Ergebnisbericht gefunden: '{ReportPath}'.", reportPath);
|
||||
|
||||
// 2. Ausgabepfad bestimmen
|
||||
@@ -35,35 +35,121 @@ public class PdfResultPackageService(
|
||||
// 3. Original auf PDF/A-3b hochstufen + Bericht anhängen
|
||||
await Task.Run(() =>
|
||||
{
|
||||
// Original in MemoryStream laden
|
||||
using var inputStream = new MemoryStream(originalPdfBytes);
|
||||
using var outputStream = new MemoryStream();
|
||||
|
||||
// PDF/A-3b Konvertierung
|
||||
var converter = new PdfDocumentConverter(inputStream);
|
||||
// Schritt 1: PDF/A-3b Konvertierung
|
||||
PdfDocumentConverter converter;
|
||||
try
|
||||
{
|
||||
converter = new PdfDocumentConverter(inputStream);
|
||||
converter.Convert(PdfCompatibility.PdfA3b);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Konvertierung nach PDF/A-3b fehlgeschlagen. " +
|
||||
"Die Originaldatei ist möglicherweise beschädigt oder nicht konvertierbar.", ex);
|
||||
}
|
||||
|
||||
// Konvertiertes PDF in MemoryStream speichern
|
||||
// Schritt 2: Konvertiertes PDF puffern
|
||||
using var convertedStream = new MemoryStream();
|
||||
converter.SaveDocument(convertedStream);
|
||||
convertedStream.Position = 0;
|
||||
|
||||
// Bericht als Anhang einbetten
|
||||
// Schritt 3: Hauptdokument laden
|
||||
using var processor = new PdfDocumentProcessor();
|
||||
processor.LoadDocument(convertedStream);
|
||||
|
||||
processor.AttachFile(new PdfFileAttachment
|
||||
{
|
||||
FileName = Path.GetFileName(reportPath),
|
||||
Description = "Ergebnisbericht",
|
||||
MimeType = "application/pdf",
|
||||
Relationship = PdfAssociatedFileRelationship.Supplement,
|
||||
CreationDate = DateTime.Now,
|
||||
Data = File.ReadAllBytes(reportPath)
|
||||
});
|
||||
// Metadaten des Originals entfernen → Result-PDF gehört uns, nicht dem Ersteller
|
||||
processor.Document.Author = "DXApp Verarbeitungssystem";
|
||||
processor.Document.Creator = "DXApp";
|
||||
processor.Document.Producer = "DXApp";
|
||||
processor.Document.Title = string.Empty;
|
||||
processor.Document.Subject = string.Empty;
|
||||
processor.Document.Keywords = string.Empty;
|
||||
|
||||
// Speichern
|
||||
// Schritt 3a: Berichts-Seiten ans Ende anhängen
|
||||
int reportPageCount;
|
||||
using (var reportStream = new FileStream(reportPath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
// 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];
|
||||
using (var graphics = processor.CreateGraphicsWorldSystem())
|
||||
{
|
||||
// Stempel-Position: oben rechts
|
||||
// Welt-Koordinaten: Ursprung oben links, X nach rechts, Y nach unten
|
||||
var stampX = (float)firstPage.CropBox.Width - 200;
|
||||
var stampY = 20f;
|
||||
var stampWidth = 175f;
|
||||
var stampHeight = 50f;
|
||||
|
||||
// Hintergrund weiß
|
||||
using var whiteBrush = new DevExpress.Drawing.DXSolidBrush(
|
||||
System.Drawing.Color.White);
|
||||
graphics.FillRectangle(whiteBrush,
|
||||
new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));
|
||||
|
||||
// Rahmen grün
|
||||
using var greenPen = new DevExpress.Drawing.DXPen(
|
||||
System.Drawing.Color.Green, 1.5f);
|
||||
graphics.DrawRectangle(greenPen,
|
||||
new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));
|
||||
|
||||
// Text Zeile 1: VERARBEITET
|
||||
using var greenBrush = new DevExpress.Drawing.DXSolidBrush(
|
||||
System.Drawing.Color.Green);
|
||||
var fontBold = new DevExpress.Drawing.DXFont(
|
||||
"Arial", 11, DevExpress.Drawing.DXFontStyle.Bold);
|
||||
graphics.DrawString(
|
||||
"✔ VERARBEITET",
|
||||
fontBold,
|
||||
greenBrush,
|
||||
new System.Drawing.PointF(stampX + 8, stampY + 6));
|
||||
|
||||
// Text Zeile 2: Datum/Uhrzeit
|
||||
using var grayBrush = new DevExpress.Drawing.DXSolidBrush(
|
||||
System.Drawing.Color.DimGray);
|
||||
var fontNormal = new DevExpress.Drawing.DXFont("Arial", 9);
|
||||
graphics.DrawString(
|
||||
DateTime.Now.ToString("dd.MM.yyyy HH:mm"),
|
||||
fontNormal,
|
||||
grayBrush,
|
||||
new System.Drawing.PointF(stampX + 8, stampY + 28));
|
||||
|
||||
graphics.AddToPageForeground(
|
||||
processor.Document.Pages[0], 96, 96);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Result-PDF konnte nicht gespeichert werden unter '{outputPath}'. " +
|
||||
"Prüfe ob das Verzeichnis existiert und beschreibbar ist.", ex);
|
||||
}
|
||||
});
|
||||
|
||||
logger.LogInformation(
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27653
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.mjs
Normal file
27653
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.mjs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
222
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.sandbox.mjs
Normal file
222
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.sandbox.mjs
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
63872
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.worker.mjs
Normal file
63872
DXApp.TemplateKitProject/wwwroot/js/pdfjs/build/pdf.worker.mjs
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/78-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/78-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/78-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/78-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Add-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Add-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Add-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Add-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5-H.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5-V.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5pc-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5pc-H.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5pc-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/B5pc-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS1-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS1-H.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS1-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS1-V.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS2-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/CNS2-H.bcmap
Normal file
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||
All rights reserved.
|
||||
See ./LICENSEáCNS2-H
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||
All rights reserved.
|
||||
See ./LICENSEá ETen-B5-H` ^
|
||||
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/EUC-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/EUC-H.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/EUC-V.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/EUC-V.bcmap
Normal file
Binary file not shown.
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Ext-H.bcmap
Normal file
BIN
DXApp.TemplateKitProject/wwwroot/js/pdfjs/web/cmaps/Ext-H.bcmap
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user