using Elwig.Helpers; using Elwig.Models; using Elwig.Models.Entities; using iText.Kernel.Colors; using iText.Kernel.Pdf; using iText.Kernel.Pdf.Action; using iText.Kernel.Pdf.Canvas; using iText.Layout; using iText.Layout.Borders; using iText.Layout.Element; using iText.Layout.Properties; using System; using System.Collections.Generic; using System.Linq; namespace Elwig.Documents { public class BusinessDocument : Document { public Member Member; public string? Location; public bool IncludeSender = false; public bool UseBillingAddress = false; public bool ShowDateAndLocation = false; protected Table? Aside; public string Address { get { IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member; var plz = addr.PostalDest.AtPlz; return string.Join("\n", ((string?[])[Member.BillingAddress?.FullName, Member.AdministrativeName, Member.ForTheAttentionOf, addr.Address, $"{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}", addr.PostalDest.Country.Name]).Where(s => !string.IsNullOrWhiteSpace(s))); } } protected Cell NewAsideCell(Paragraph text, int colspan = 1, bool isName = false) { var cell = NewCell(text, colspan: colspan).SetPaddingsMM(0.25f, 0.5f, 0.25f, isName ? 1 : 0); if (colspan > 1) { cell.SetTextAlignment(TextAlignment.CENTER).SetFont(BF) .SetBackgroundColor(new DeviceRgb(0xe0, 0xe0, 0xe0)) .SetBorderTop(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .SetPaddingsMM(0.5f, 1, 0.5f, 1); } return cell; } protected Cell NewAsideCell(string text, int colspan = 1, bool isName = false) { return NewAsideCell(new KernedParagraph(text, 10), colspan, isName); } public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) { Member = m; Location = App.BranchLocation; IncludeSender = includeSender; } protected override void RenderHeader(iText.Layout.Document doc, PdfDocument pdf) { base.RenderHeader(doc, pdf); var uid = new KernedParagraph(Member.UstIdNr ?? "-", 10); if (!Member.IsBuchführend) uid.Add(Normal(" ")).Add(Italic("(pauschaliert)")); Aside = new Table(ColsMM(22.5, 42.5)) .SetFont(NF).SetFontSize(10) .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) .SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .AddCell(NewAsideCell("Mitglied", 2)) .AddCell(NewAsideCell("Mitglieds-Nr.:", isName: true)).AddCell(NewAsideCell($"{Member.MgNr}")) .AddCell(NewAsideCell("Betriebs-Nr.:", isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "")) .AddCell(NewAsideCell("UID:", isName: true)).AddCell(NewAsideCell(uid)); } protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) { base.RenderBody(doc, pdf); var page = pdf.AddNewPage(); var pageSize = page.GetPageSize(); var pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdf); using (var canvas = new Canvas(pdfCanvas, pageSize)) { // header var header = new Div() .SetFixedPositionMM(25, 10, pageSize.GetWidth() / PtInMM - 45, 45, pageSize) .SetTextAlignment(TextAlignment.CENTER); header.Add(new KernedParagraph(App.Client.Name, 18).SetFont(BF).SetMarginsMM(8, 0, 0, 0)); if (App.Client.NameSuffix != null) header.Add(new KernedParagraph(App.Client.NameSuffix, 14).SetFont(BF).SetMargin(0)); header.Add(new KernedParagraph(App.Client.NameTypeFull, 12).SetFont(NF).SetMargin(0)); canvas.Add(header); // address canvas.Add(new Div() .SetFixedPositionMM(25, 50, 80, 45, pageSize) .SetFont(NF) .Add(new KernedParagraph(IncludeSender ? $"{App.Client.Sender1}\n{App.Client.Sender2}" : "", 8).SetMargin(0).SetHeight(16).SetPaddings(8, 0, 8, 0)) .Add(new KernedParagraph(Address, 12).SetMargin(0).SetHeight(12 * 5))); // aside if (Aside != null) { canvas.Add(new Div() .SetFixedPositionMM(125, 50, 65, 50, pageSize) .Add(Aside)); } } doc.Add(new KernedParagraph(ShowDateAndLocation ? $"{Location}, am {Date:dd.MM.yyyy}" : "", 12).SetTextAlignment(TextAlignment.RIGHT).SetVerticalAlignment(VerticalAlignment.MIDDLE).SetHeight(24).SetMargin(0)); doc.Add(new KernedParagraph(Title, 12).SetFont(BF).SetMargins(0, 0, 12, 0)); } protected override Paragraph GetFooter() { var c = App.Client; var link1 = new Link(c.EmailAddress ?? "", PdfAction.CreateURI($"mailto:{Uri.EscapeDataString(c.Name)}%20<{c.EmailAddress}>")); var link2 = new Link(c.Website ?? "", PdfAction.CreateURI($"http://{c.Website}/")); link1.GetLinkAnnotation().SetBorder(new PdfAnnotationBorder(0, 0, 0)); link2.GetLinkAnnotation().SetBorder(new PdfAnnotationBorder(0, 0, 0)); return new KernedParagraph(10) .AddAll(Utils.GenerateFooter("\n", " \u00b7 ") .Item(c.NameFull).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.EmailAddress != null ? link1 : null) .Item(c.Website != null ? link2 : null) .Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine() .Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban) .ToLeafElements()); } protected Cell NewWeightsHdr(Paragraph p) { return NewCell(p) .SetPaddingsMM(0.125f, 0, 0.125f, 0); } protected Cell NewWeightsHdr(string text) { return NewCell(new KernedParagraph(text, 8).SetFont(IF).SetTextAlignment(TextAlignment.RIGHT)) .SetPaddingsMM(0.125f, 0, 0.125f, 0); } protected Cell NewWeightsTh(string name) { return NewCell(new KernedParagraph(name, 10).SetFont(IF)) .SetPaddingsMM(0.125f, 0, 0.125f, 0); } protected Cell NewWeightsTd(string text, bool sum = false) { return NewCell(new KernedParagraph(text, 10).SetFont(sum ? BF : NF).SetTextAlignment(TextAlignment.RIGHT)) .SetPaddingsMM(sum ? 0.25f : 0.125f, 0, sum ? 0.25f : 0.125f, 0) .SetBorderTop(sum ? new SolidBorder(BorderThickness) : Border.NO_BORDER); } protected Table NewWeightsTable(List stats) { List discrs = [""]; List names = ["ohne Attr./Bewirt."]; List bucketAttrs = [ .. stats .Select(s => s.Discr) .Distinct() .Where(s => s.Length > 0) .Order() ]; names.AddRange(bucketAttrs); names.Add("Gesamt"); discrs.AddRange(bucketAttrs); List cols = [40]; cols.AddRange(names.Select(_ => 125.0 / names.Count)); var tbl = new Table(ColsMM([.. cols])) .AddHeaderCell(NewWeightsHdr(new KernedParagraph(8).Add(BoldItalic("Sortenaufteilung ")).Add(Italic("[kg]")))); foreach (var name in names) tbl.AddHeaderCell(NewWeightsHdr(name)); foreach (var g in stats.GroupBy(b => b.Variety).OrderBy(b => b.Key)) { var dict = g.ToDictionary(a => a.Discr, a => a.Weight); var vals = discrs.Select(a => dict.GetValueOrDefault(a, 0)).ToList(); tbl.AddCell(NewWeightsTh(g.Key)); foreach (var v in vals) tbl.AddCell(NewWeightsTd(v == 0 ? "-" : $"{v:N0}")); tbl.AddCell(NewWeightsTd($"{dict.Values.Sum():N0}")); } var totalDict = stats.GroupBy(s => s.Discr).ToDictionary(g => g.Key, g => g.Sum(a => a.Weight)); var totals = discrs.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0); tbl.AddCell(NewWeightsTd("", true)); foreach (var v in totals) tbl.AddCell(NewWeightsTd($"{v:N0}", true)); tbl.AddCell(NewWeightsTd($"{totalDict.Values.Sum():N0}", true)); return tbl; } protected Cell NewBucketHdr(Paragraph p, int rowspan = 1, bool left = false, bool unit = false) { p.SetProperty(Property.NO_SOFT_WRAP_INLINE, true); p.SetProperty(Property.OVERFLOW_X, OverflowPropertyValue.HIDDEN); return NewCell(p, rowspan: rowspan) .SetPaddingsMM(0.125f, unit ? 2 : 0, 0.125f, 0) .SetTextAlignment(left ? TextAlignment.LEFT : TextAlignment.RIGHT).SetVerticalAlignment(VerticalAlignment.MIDDLE) .SetFont(IF); } protected Cell NewBucketHdr(string text, int rowspan = 1, bool left = false, bool unit = false) { return NewBucketHdr(new KernedParagraph(text, 8), rowspan, left, unit); } protected Cell NewBucketSubHdr(string text, int colspan, bool isTiny = false) { var p = new KernedParagraph(text, 8); p.SetProperty(Property.NO_SOFT_WRAP_INLINE, true); p.SetProperty(Property.OVERFLOW_X, OverflowPropertyValue.HIDDEN); return NewCell(p, colspan: colspan) .SetBorderTop(!isTiny ? new SolidBorder(BorderThickness) : Border.NO_BORDER) .SetPaddingsMM(isTiny ? 0 : 0.125f, 0, isTiny ? 0 : 0.125f, 0).SetVerticalAlignment(VerticalAlignment.MIDDLE) .SetTextAlignment(TextAlignment.LEFT).SetFont(BI); } protected Cell NewBucketTh(string text, bool isTiny = false) { var p = new KernedParagraph(text, isTiny ? 8 : 10); p.SetProperty(Property.NO_SOFT_WRAP_INLINE, true); p.SetProperty(Property.OVERFLOW_X, OverflowPropertyValue.HIDDEN); return NewCell(p) .SetPaddingsMM(isTiny ? 0 : 0.125f, 0, isTiny ? 0 : 0.125f, 0) .SetTextAlignment(TextAlignment.LEFT).SetFont(IF); } protected Cell NewBucketTd(string text, bool bold = false, bool isTiny = false) { var p = new KernedParagraph(text, isTiny ? 8 : 10); p.SetProperty(Property.NO_SOFT_WRAP_INLINE, true); p.SetProperty(Property.OVERFLOW_X, OverflowPropertyValue.HIDDEN); return NewCell(p) .SetPaddingsMM(isTiny ? 0 : 0.125f, 0, isTiny ? 0 : 0.125f, 0) .SetTextAlignment(TextAlignment.RIGHT).SetFont(bold ? BF : NF); } protected Cell[] FormatRow( int obligation, int right, int delivery, int? totalDelivery = null, int? payment = null, int? area = null, bool isGa = false, bool showPayment = false, bool showArea = false, bool isTiny = false ) { totalDelivery ??= delivery; payment ??= delivery; if (showArea) { return [ NewBucketTd(area == null ? "" : $"{area:N0}", isTiny: isTiny), NewBucketTd($"{obligation:N0}", isTiny: isTiny), NewBucketTd($"{right:N0}", isTiny: isTiny), ]; } return [ NewBucketTd(obligation == 0 ? "-" : $"{obligation:N0}", isTiny: isTiny), NewBucketTd(right == 0 ? "-" : $"{right:N0}", isTiny: isTiny), NewBucketTd(totalDelivery < obligation ? $"{obligation - totalDelivery:N0}" : "-", totalDelivery < obligation, isTiny: isTiny), NewBucketTd(delivery <= right ? $"{right - delivery:N0}" : "-", isTiny: isTiny), NewBucketTd(obligation == 0 && right == 0 ? "-" : (delivery > right ? $"{delivery - right:N0}" : "-"), delivery > right && isGa, isTiny: isTiny), ..(showPayment ? new List() { NewBucketTd(isGa ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}", isTiny: isTiny) } : []), NewBucketTd($"{totalDelivery:N0}", isTiny: isTiny), ]; } protected Cell[] FormatRow(MemberBucket bucket, bool isGa = false, bool showPayment = false, bool showArea = false, bool isTiny = false) { return FormatRow(bucket.Obligation, bucket.Right, bucket.Delivery, bucket.DeliveryTotal, bucket.Payment, bucket.Area, isGa, showPayment, showArea, isTiny); } protected Table NewBucketTable( Season season, Dictionary buckets, bool includeDelivery = true, bool includePayment = false, bool isTiny = false, IEnumerable? filter = null ) { includePayment = includePayment && includeDelivery; var tbl = new Table(ColsMM(!includeDelivery ? [105, 20, 20, 20] : includePayment ? [45, 17, 17, 17, 19, 16, 17, 17] : [45, 20, 20, 20, 20, 20, 20])) .SetBorder(Border.NO_BORDER).SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) .SetFont(NF).SetFontSize(isTiny ? 8 : 10); if (includeDelivery) { tbl.AddHeaderCell(NewBucketHdr(new KernedParagraph(8) .Add(BoldItalic($"Lese {season.Year}")) .Add(Italic($" per {Date:dd.MM.yyyy} [kg]")), left: true)) .AddHeaderCell(NewBucketHdr("Lieferpflicht")) .AddHeaderCell(NewBucketHdr("Lieferrecht")) .AddHeaderCell(NewBucketHdr("Unterliefert")) .AddHeaderCell(NewBucketHdr("Noch lieferbar")) .AddHeaderCell(NewBucketHdr("Überliefert")); if (includePayment) tbl.AddHeaderCell(NewBucketHdr("Zugeteilt")); tbl.AddHeaderCell(NewBucketHdr("Geliefert")); } else { tbl.AddHeaderCell(NewBucketHdr(new KernedParagraph(8) .Add(BoldItalic("Zusammengefasste Flächenbindungen")) .Add(Italic($" per {Date:dd.MM.yyyy}")), 2, left: true)) .AddHeaderCell(NewBucketHdr("Fläche")) .AddHeaderCell(NewBucketHdr("Lieferpflicht")) .AddHeaderCell(NewBucketHdr("Lieferrecht")) .AddHeaderCell(NewBucketHdr("[m²]", unit: true)) .AddHeaderCell(NewBucketHdr("[kg]", unit: true)) .AddHeaderCell(NewBucketHdr("[kg]", unit: true)); } var mBuckets = buckets .Where(b => ((!includeDelivery && b.Value.Area > 0) || (includeDelivery && (b.Value.Right > 0 || b.Value.Obligation > 0 || b.Value.Delivery > 0))) && (filter == null || filter.Contains(b.Key[..2]))) .ToList(); var fbVars = mBuckets .Where(b => b.Value.Right > 0 || b.Value.Obligation > 0) .Select(b => b.Key.Replace("_", "")) .Order() .ToArray(); var fbs = mBuckets .Where(b => fbVars.Contains(b.Key) && b.Key.Length == 2) .OrderBy(b => b.Value.Name); var vtr = mBuckets .Where(b => fbVars.Contains(b.Key) && b.Key.Length > 2) .OrderBy(b => b.Value.Name); var rem = mBuckets .Where(b => !fbVars.Contains(b.Key)) .OrderBy(b => b.Value.Name); tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny)); tbl.AddCells(FormatRow(Member.BusinessShares * season.MinKgPerBusinessShare, Member.BusinessShares * season.MaxKgPerBusinessShare, season.Deliveries.Where(d => d.MgNr == Member.MgNr).Sum(d => d.Weight), isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); if (fbs.Any()) { tbl.AddCell(NewBucketSubHdr("Flächenbindungen" + (vtr.Any() ? " (inkl. Verträge)" : "") + ":", includePayment ? 8 : 7, isTiny: isTiny)); foreach (var (id, b) in fbs) { tbl.AddCell(NewBucketTh(b.Name, isTiny: isTiny)).AddCells(FormatRow(b, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); } } if (vtr.Any()) { tbl.AddCell(NewBucketSubHdr("Verträge:", includePayment ? 8 : 7, isTiny: isTiny)); foreach (var (id, b) in vtr) { tbl.AddCell(NewBucketTh(b.Name, isTiny: isTiny)).AddCells(FormatRow(b, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); } } return tbl; } } }