using System.Globalization; using System.Net.Mime; using QuestPDF; using QuestPDF.Drawing; using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using Server.Data; namespace Server.Model { public class InvoiceDocument(InvoiceModel model) : IDocument { private InvoiceModel Model { get; } = model; public void Compose(IDocumentContainer container) { if(File.Exists("NotoSans-VariableFont_wdth,wght.ttf")) FontManager.RegisterFont(File.OpenRead("NotoSans-VariableFont_wdth,wght.ttf")); container.Page(page => { page.Size(PageSizes.A4); page.Margin(10, Unit.Millimetre); page.MarginLeft(20, Unit.Millimetre); page.PageColor(Colors.White); page.DefaultTextStyle(x => x .FontSize(10) .FontFamily("NotoSans") .Light()); page.Header().Element(ComposeHeader); page.Content().Element(ComposeContent); page.Footer().Element(ComposeFooter); }); } private void ComposeContent(IContainer container) { container.PaddingVertical(40).Column(outerColumn => { if (SettingsData.Instance.Kleinunternehmer) { outerColumn.Item().Element(ComposeItemTableKleinunternehmer); outerColumn.Item().PaddingTop(10).Text("Im Sinne der Kleinunternehmerregelung nach § 19 UStG. enthält der ausgewiesene Betrag keine Umsatzsteuer."); } else { outerColumn.Item().Element(ComposeItemTable); } outerColumn.Item().Row(row => { row.RelativeItem().PaddingRight(10).Column(col => { if (!string.IsNullOrWhiteSpace(Model.Comment)) col.Item().PaddingTop(25).Element(ComposeComments); }); if (!SettingsData.Instance.Kleinunternehmer) { row.AutoItem().Column(col => { col.Item().Element(ComposeTaxTable); }); } }); outerColumn.Spacing(5); }); } private void ComposeTaxTable(IContainer container) { container.PaddingTop(25).Table(table => { table.ColumnsDefinition(c => { c.ConstantColumn(70); c.ConstantColumn(25); c.ConstantColumn(70); c.ConstantColumn(45); }); table.Header(header => { header.Cell(); header.Cell().AlignRight().TextSmall("", true); header.Cell().AlignRight().TextSmall("Zwischensumme", true); header.Cell().AlignRight().TextSmall("", true); header.Cell().Element(c => c.BorderBottom(1).BorderColor(Colors.Black)); header.Cell().Element(c => c.BorderBottom(1).BorderColor(Colors.Black)).AlignRight().TextSmall("USt. %", true); header.Cell().Element(c => c.BorderBottom(1).BorderColor(Colors.Black)).AlignRight().TextSmall("(ohne USt.)", true); header.Cell().Element(c => c.BorderBottom(1).BorderColor(Colors.Black)).AlignRight().TextSmall("USt.", true); }); var totalTax = 0d; // 7% var tax7Items = Model.Items?.Where(x => x.TaxType == TaxType.Tax7).ToList() ?? []; if (tax7Items.Count > 0) { var tax7Netto = tax7Items.Sum(x => x.PriceNetto * x.Quantity); var tax7Tax = tax7Netto * 0.07; totalTax += tax7Tax; table.Cell(); table.Cell().AlignRight().TextSmall("7%"); table.Cell().AlignRight().TextSmall($"{tax7Netto:N2} €"); table.Cell().AlignRight().TextSmall($"{tax7Tax:N2} €"); } // 19% var tax19Items = Model.Items?.Where(x => x.TaxType == TaxType.Tax19).ToList() ?? []; if (tax19Items.Count > 0) { var tax19Netto = tax19Items.Sum(x => x.PriceNetto * x.Quantity); var tax19Tax = tax19Netto * 0.19; totalTax += tax19Tax; table.Cell(); table.Cell().AlignRight().TextSmall("19%"); table.Cell().AlignRight().TextSmall($"{tax19Netto:N2} €"); table.Cell().AlignRight().TextSmall($"{tax19Tax:N2} €"); } // SUMME table.Cell().Element(c => c.BorderTop(1).BorderColor(Colors.Black)).TextSmall("USt. Gesamt"); table.Cell().Element(c => c.BorderTop(1).BorderColor(Colors.Black)); table.Cell().Element(c => c.BorderTop(1).BorderColor(Colors.Black)).AlignRight().TextSmall($"{Model.Items?.Sum(x => x.PriceNetto * x.Quantity):N2} €"); table.Cell().Element(c => c.BorderTop(1).BorderColor(Colors.Black)).AlignRight().TextSmall($"{totalTax:N2} €"); }); } private void ComposeItemTableKleinunternehmer(IContainer container) { container.Table(table => { // Define columns table.ColumnsDefinition(columns => { columns.RelativeColumn(3); // Beschreibung columns.ConstantColumn(20, Unit.Millimetre); // Menge columns.ConstantColumn(24, Unit.Millimetre); // Einzelpreis columns.ConstantColumn(24, Unit.Millimetre); // Gesamtpreis }); // Describe header table.Header(header => { header.Cell().Element(CellStyle).Text("Beschreibung").SemiBold(); header.Cell().Element(CellStyle).AlignCenter().Text("Menge").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("Einzelpreis").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("Gesamtpreis").SemiBold(); static IContainer CellStyle(IContainer container) { return container.DefaultTextStyle(x => x.SemiBold()).BorderBottom(1).BorderColor(Colors.Black); } }); // Describe content foreach (var item in Model.Items ?? []) { // Beschreibung table.Cell().Element(CellStyle).Column(column => { column.Item().Text(item.Name); if(!string.IsNullOrWhiteSpace(item.Description)) column.Item().TextSmall(item.Description); }); table.Cell().Element(CellStyle).AlignCenter().Text(txt => txt.Span($"{item.Quantity:N2}")); // string as currency format table.Cell().Element(CellStyle).AlignRight().Text($"{item.PriceNetto:N2} €"); table.Cell().Element(CellStyle).AlignRight().Text($"{item.PriceNetto * item.Quantity:N2} €"); } table.Cell().ColumnSpan(3).Element(CellStyle).PaddingTop(10).Text("Rechnungsbetrag").Medium(); table.Cell().Element(CellStyle).PaddingTop(10).AlignRight().Text($"{Model.Items.Sum(x=> x.PriceNetto*x.Quantity):N2} €").Medium(); return; static IContainer CellStyle(IContainer container) => container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5); }); } private void ComposeItemTable(IContainer container) { container.Table(table => { // Define columns table.ColumnsDefinition(columns => { columns.RelativeColumn(3); columns.ConstantColumn(20, Unit.Millimetre); columns.ConstantColumn(24, Unit.Millimetre); columns.ConstantColumn(15, Unit.Millimetre); columns.ConstantColumn(24, Unit.Millimetre); columns.ConstantColumn(24, Unit.Millimetre); }); // Describe header table.Header(header => { header.Cell().Text("").SemiBold(); header.Cell().Text("").SemiBold(); header.Cell().AlignRight().Text("Einzelpreis").SemiBold(); header.Cell().Text("").SemiBold(); header.Cell().AlignRight().Text("Einzelpreis").SemiBold(); header.Cell().AlignRight().Text("Gesamtpreis").SemiBold(); header.Cell().Element(CellStyle).Text("Beschreibung").SemiBold(); header.Cell().Element(CellStyle).AlignCenter().Text("Menge").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("(ohne USt.)").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("USt. %").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("(inkl USt.)").SemiBold(); header.Cell().Element(CellStyle).AlignRight().Text("(inkl USt.)").SemiBold(); static IContainer CellStyle(IContainer container) { return container.DefaultTextStyle(x => x.SemiBold()).BorderBottom(1).BorderColor(Colors.Black); } }); // Describe content foreach (var item in Model.Items ?? []) { // Beschreibung table.Cell().Element(CellStyle).Column(column => { column.Item().Text(item.Name); if(!string.IsNullOrWhiteSpace(item.Description)) column.Item().TextSmall(item.Description); }); table.Cell().Element(CellStyle).AlignCenter().Text(txt => txt.Span($"{item.Quantity:N2}")); // string as currency format table.Cell().Element(CellStyle).AlignRight().Text($"{item.PriceNetto:N2} €"); table.Cell().Element(CellStyle).AlignRight().Text($"{(int)item.TaxType}%"); table.Cell().Element(CellStyle).AlignRight().Text($"{item.PriceBrutto:N2} €"); table.Cell().Element(CellStyle).AlignRight().Text($"{item.PriceBrutto * item.Quantity:N2} €"); continue; } table.Cell().ColumnSpan(5).Element(CellStyle).PaddingTop(10).Text("Rechnungsbetrag").Medium(); table.Cell().Element(CellStyle).PaddingTop(10).AlignRight().Text($"{Model.Items.Sum(x=> x.PriceBrutto*x.Quantity):N2} €").Medium(); return; static IContainer CellStyle(IContainer container) => container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5); }); } private void ComposeComments(IContainer container) { container.Background(Colors.Grey.Lighten3).Padding(10).Column(column => { column.Spacing(5); column.Item().Text(Model.Comment); }); } private void ComposeFooter(IContainer container) { container.Column(outerColumn => { outerColumn.Item().Row(row => { row.RelativeItem().Column(column => { column.Item().TextSmall(Model.Seller?.Name, true); column.Item().TextSmall(Model.Seller?.Street); column.Item().TextSmall($"{Model.Seller?.Zip} {Model.Seller?.City}"); }); row.RelativeItem().Column(column => { if(!string.IsNullOrWhiteSpace(Model.Seller?.Phone)) column.Item().TextSmall($"Tel.: {Model.Seller?.Phone}"); if(!string.IsNullOrWhiteSpace(Model.Seller?.Email)) column.Item().TextSmall($"E-Mail: {Model.Seller?.Email}"); if(!string.IsNullOrWhiteSpace(Model.Seller?.Web)) column.Item().TextSmall($"Web: {Model.Seller?.Web}"); }); row.RelativeItem().Column(column => { if(!string.IsNullOrWhiteSpace(Model.PaymentData?.BankName)) column.Item().TextSmall(Model.PaymentData?.BankName); if(!string.IsNullOrWhiteSpace(Model.PaymentData?.Iban)) column.Item().TextSmall($"IBAN: {Model.PaymentData?.Iban}"); if(!string.IsNullOrWhiteSpace(Model.PaymentData?.Bic)) column.Item().TextSmall($"BIC: {Model.PaymentData?.Bic}"); }); }); outerColumn.Item().PaddingTop(3, Unit.Millimetre).AlignCenter().Text(txt => { txt.CurrentPageNumber(); txt.Span(" / "); txt.TotalPages(); }); }); } private void ComposeHeader(IContainer container) { container.Column(outerColumn => { outerColumn.Item().Row(row => { row.RelativeItem().Column(col => { if (!string.IsNullOrWhiteSpace(SettingsData.Instance.Logo) && File.Exists(SettingsData.Instance.Logo)) col.Item().Height(25, Unit.Millimetre).Image(SettingsData.Instance.Logo); else col.Item().Height(25, Unit.Millimetre); }); row.RelativeItem().AlignMiddle().PaddingRight(10, Unit.Millimetre).Background(Colors.Red.Medium).Column(col => { //col.Item().AlignRight().Text("www.example.com"); }); }); outerColumn.Item().PaddingTop(15, Unit.Millimetre).Row(row => { row.AutoItem().Column(column => { column.Item().PaddingBottom(5).TextSmall(Model.Seller?.ToString(), true); if(!string.IsNullOrWhiteSpace(Model.Customer?.Name)) column.Item().Text(Model.Customer?.Name); if(!string.IsNullOrWhiteSpace(Model.Customer?.Name2)) column.Item().Text(Model.Customer?.Name2); column.Item().Text(Model.Customer?.Street); column.Item().Text($"{Model.Customer?.Zip} {Model.Customer?.City}"); }); row.RelativeItem(); row.AutoItem().Column(col => { col.Item().Table(table => { table.ColumnsDefinition(c => { c.ConstantColumn(50); c.ConstantColumn(50); }); table.Cell().TextSmall("Steuer-Nr:", true); table.Cell().TextSmall(Model.Seller?.TaxId); table.Cell().TextSmall("Datum:", true); table.Cell().TextSmall(Model.IssueDate.ToShortDateString()); table.Cell().TextSmall("Rechnung:", true); table.Cell().TextSmall($"#{Model.InvoiceId}"); }); }); }); }); } } }