using Elwig.Helpers; using Elwig.Models; using Elwig.Models.Entities; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace Elwig.Documents { public abstract class BusinessDocument : Document { public Member Member; public string? Location; public bool IncludeSender = false; public bool UseBillingAddress = false; public bool ShowDateAndLocation = false; public string Aside; public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) { Member = m; Location = App.BranchLocation; IncludeSender = includeSender; var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>"); Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" + $"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" + $"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" + $"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" + $"<tr><th>UID</th><td>{uid}</td></tr>" + $"</tbody></table>"; } public string Address { get { IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member; var plz = addr.PostalDest.AtPlz; return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}"; } } private static string GetColGroup(IEnumerable<double> cols) { return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString(CultureInfo.InvariantCulture)}mm;\"/>")) + "\n</colgroup>\n"; } public static string PrintSortenaufteilung(List<MemberStat> stats) { List<string> discrs = [""]; List<string> names = ["ohne Attr./Bewirt."]; List<string> bucketAttrs = [ .. stats .Select(s => s.Discr) .Distinct() .Where(s => s.Length > 0) .Order() ]; names.AddRange(bucketAttrs); names.Add("Gesamt"); discrs.AddRange(bucketAttrs); List<double> cols = [40]; cols.AddRange(names.Select(_ => 125.0 / names.Count)); string tbl = GetColGroup(cols); tbl += "<thead><tr>" + $"<th><b>Sortenaufteilung</b> [kg]</th>" + string.Join("", names.Select(c => $"<th>{c}</th>")) + "</tr></thead>"; tbl += string.Join("\n", stats .GroupBy(b => b.Variety) .OrderBy(b => b.Key) .Select(g => { var dict = g.ToDictionary(a => a.Discr, a => a.Weight); var vals = discrs.Select(a => dict.GetValueOrDefault(a, 0)).ToList(); return $"<tr><th>{g.Key}</th>" + string.Join("", vals.Select(v => "<td class=\"number\">" + (v == 0 ? "-" : $"{v:N0}") + "</td>")) + $"<td class=\"number\">{dict.Values.Sum():N0}</td></tr>"; }) ); 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 += "<tr class=\"sum bold\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) + $"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>"; return "<table class=\"sortenaufteilung small number cohere\">" + tbl + "</table>"; } private static string FormatRow( int obligation, int right, int delivery, int? payment = null, int? area = null, bool isGa = false, bool showPayment = false, bool showArea = false ) { payment ??= delivery; var baseline = showPayment ? payment : delivery; if (showArea) { return $"<td>{(area == null ? "" : $"{area:N0}")}</td>" + $"<td>{obligation:N0}</td>" + $"<td>{right:N0}</td>"; } return $"<td>{(obligation == 0 ? "-" : $"{obligation:N0}")}</td>" + $"<td>{(right == 0 ? "-" : $"{right:N0}")}</td>" + $"<td>{(baseline < obligation ? $"<b>{obligation - baseline:N0}</b>" : "-")}</td>" + $"<td>{(baseline >= obligation && delivery <= right ? $"{right - delivery:N0}" : "-")}</td>" + $"<td>{(obligation == 0 && right == 0 ? "-" : (delivery > right ? ((isGa ? "<b>" : "") + $"{delivery - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" + (showPayment ? $"<td>{(isGa ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" : "") + $"<td>{delivery:N0}</td>"; } private static string FormatRow(MemberBucket bucket, bool isGa = false, bool showPayment = false, bool showArea = false) { return FormatRow(bucket.Obligation, bucket.Right, bucket.Delivery, bucket.Payment, bucket.Area, isGa, showPayment, showArea); } public string PrintBucketTable( Season season, Dictionary<string, MemberBucket> buckets, bool includeDelivery = true, bool includePayment = false, bool isTiny = false, IEnumerable<string>? filter = null ) { includePayment = includePayment && includeDelivery; string tbl = GetColGroup(!includeDelivery ? [105, 20, 20, 20] : includePayment ? [45, 17, 17, 17, 19, 16, 17, 17] : [45, 20, 20, 20, 20, 20, 20]); tbl += $""" <thead> <tr> <th{(!includeDelivery ? " rowspan=\"2\"" : "")}> <b>{(includeDelivery ? "Lese " + season.Year : "Zusammengefasste Flächenbindungen")}</b> per {Date:dd.MM.yyyy} {(includeDelivery ? "[kg]" : "")} </th> {(!includeDelivery ? "<th>Fläche</th>" : "")} <th>Lieferpflicht</th> <th>Lieferrecht</th> {(includeDelivery ? "<th>Unterliefert</th>" : "")} {(includeDelivery ? "<th>Noch lieferbar</th>" : "")} {(includeDelivery ? "<th>Überliefert</th>" : "")} {(includePayment ? "<th>Zugeteilt</th>" : "")} {(includeDelivery ? "<th>Geliefert</th>" : "")} </tr> {(!includeDelivery ? "<tr><th class=\"unit\">[m²]</th><th class=\"unit\">[kg]</th><th class=\"unit\">[kg]</th></tr>" : "")} </thead> """; 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 += "\n<tbody>\n"; tbl += $"<tr><th>Gesamtlieferung lt. gez. GA</th>{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 )}</tr>"; if (fbs.Any()) { tbl += $"<tr class=\"subheading{(filter == null ? " border" : "")}\"><th colspan=\"{(includePayment ? 8 : 7)}\">" + $"Flächenbindungen{(vtr.Any() ? " (inkl. Verträge)" : "")}:</th></tr>"; foreach (var (id, b) in fbs) { tbl += $"<tr><th>{b.Name}</th>{FormatRow(b, showPayment: includePayment, showArea: !includeDelivery)}</tr>"; } } if (vtr.Any()) { tbl += $"<tr class=\"subheading{(filter == null ? " border" : "")}\"><th colspan=\"{(includePayment ? 8 : 7)}\">" + "Verträge:</th></tr>"; foreach (var (id, b) in vtr) { tbl += $"<tr><th>{b.Name}</th>{FormatRow(b, showPayment: includePayment, showArea: !includeDelivery)}</tr>"; } } tbl += "\n</tbody>\n"; return $"<table class=\"buckets {(isTiny ? "tiny" : "small")} number cohere\">\n" + tbl + "\n</table>"; } } }