diff --git a/Elwig/Documents/BusinessDocument.cs b/Elwig/Documents/BusinessDocument.cs index 75e20ed..4016729 100644 --- a/Elwig/Documents/BusinessDocument.cs +++ b/Elwig/Documents/BusinessDocument.cs @@ -18,7 +18,7 @@ namespace Elwig.Documents { public class BusinessDocument : Document { public Member Member; - public MemberHistoryPoint MemberHistory; + public MemberHistoryPoint MemberShares; public string? Location; public bool IncludeSender = false; public bool UseBillingAddress = false; @@ -52,7 +52,7 @@ namespace Elwig.Documents { public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) : base(title) { Member = m; - MemberHistory = new(m.Shares, m.SharesRed, m.SharesWhite, m.SharesDormant); + MemberShares = new(m.Shares, m.SharesRed, m.SharesWhite, m.SharesDormant); Location = App.BranchLocation; IncludeSender = includeSender; DateFrom = dateFrom; @@ -318,19 +318,19 @@ namespace Elwig.Documents { .Where(b => !fbVars.Contains(b.Key)) .OrderBy(b => b.Value.Name); - if (MemberHistory.Shares != 0 || (MemberHistory.SharesRed == 0 && MemberHistory.SharesWhite == 0)) { + if (MemberShares.Shares != 0 || (MemberShares.SharesRed == 0 && MemberShares.SharesWhite == 0)) { tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny)); - tbl.AddCells(FormatRow(MemberHistory.Shares * (season.MinKgPerShare ?? 0), MemberHistory.Shares * (season.MaxKgPerShare ?? 0), + tbl.AddCells(FormatRow(MemberShares.Shares * (season.MinKgPerShare ?? 0), MemberShares.Shares * (season.MaxKgPerShare ?? 0), deliveredWeightRed + deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); } - if (MemberHistory.SharesRed != 0 || MemberHistory.SharesWhite != 0) { + if (MemberShares.SharesRed != 0 || MemberShares.SharesWhite != 0) { tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (rot)", isTiny: isTiny)); - tbl.AddCells(FormatRow(MemberHistory.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0), MemberHistory.SharesRed * (season.MaxKgPerShareRed ?? season.MaxKgPerShare ?? 0), + tbl.AddCells(FormatRow(MemberShares.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0), MemberShares.SharesRed * (season.MaxKgPerShareRed ?? season.MaxKgPerShare ?? 0), deliveredWeightRed, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (weiß)", isTiny: isTiny)); - tbl.AddCells(FormatRow(MemberHistory.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0), MemberHistory.SharesWhite * (season.MaxKgPerShareWhite ?? season.MaxKgPerShare ?? 0), + tbl.AddCells(FormatRow(MemberShares.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0), MemberShares.SharesWhite * (season.MaxKgPerShareWhite ?? season.MaxKgPerShare ?? 0), deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny)); } diff --git a/Elwig/Documents/DeliveryConfirmation.cs b/Elwig/Documents/DeliveryConfirmation.cs index 517c316..dbc9af9 100644 --- a/Elwig/Documents/DeliveryConfirmation.cs +++ b/Elwig/Documents/DeliveryConfirmation.cs @@ -44,7 +44,7 @@ namespace Elwig.Documents { .ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight)); MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0); MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0); - MemberHistory = await ctx.GetMemberHistory(Season.Year, Member.MgNr); + MemberShares = await ctx.GetMemberHistory(Season.Year, Member.MgNr); MemberBuckets = await ctx.GetMemberBuckets(Season.Year, Member.MgNr); MemberStats = await AppDbContext.GetMemberStats(Season.Year, Member.MgNr); Data ??= await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, Season.Year, Member); diff --git a/Elwig/Documents/DeliveryNote.cs b/Elwig/Documents/DeliveryNote.cs index a396367..a95f45f 100644 --- a/Elwig/Documents/DeliveryNote.cs +++ b/Elwig/Documents/DeliveryNote.cs @@ -66,7 +66,7 @@ namespace Elwig.Documents { .ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight)); MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0); MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0); - MemberHistory = await ctx.GetMemberHistory(Delivery.Year, Member.MgNr); + MemberShares = await ctx.GetMemberHistory(Delivery.Year, Member.MgNr); MemberBuckets = await ctx.GetMemberBuckets(Delivery.Year, Member.MgNr) ?? []; } diff --git a/Elwig/Documents/MemberDataSheet.cs b/Elwig/Documents/MemberDataSheet.cs index 1d54152..732377b 100644 --- a/Elwig/Documents/MemberDataSheet.cs +++ b/Elwig/Documents/MemberDataSheet.cs @@ -20,6 +20,7 @@ namespace Elwig.Documents { public Season? Season; public int MemberDeliveredWeightRed; public int MemberDeliveredWeightWhite; + public List MemberHistory = []; public Dictionary MemberBuckets = []; public List ActiveAreaCommitments = []; @@ -37,6 +38,10 @@ namespace Elwig.Documents { protected override async Task LoadData(AppDbContext ctx) { await base.LoadData(ctx); Season = await ctx.FetchSeasons().FirstOrDefaultAsync() ?? throw new ArgumentException("Invalid season"); + MemberHistory = await ctx.MemberHistory + .Where(h => h.FromMgNr == Member.MgNr || h.ToMgNr == Member.MgNr) + .OrderBy(h => h.DateString).ThenBy(h => h.HistNr) + .ToListAsync(); MemberBuckets = await ctx.GetMemberBuckets(Utils.CurrentYear, Member.MgNr); ActiveAreaCommitments = await Member.ActiveAreaCommitments(ctx) .Include(c => c.Contract).ThenInclude(c => c.Revisions) @@ -61,9 +66,18 @@ namespace Elwig.Documents { doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE)); firstOnPage = true; } - doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 12, 0)); + doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0)); doc.Add(NewAreaComTable(ActiveAreaCommitments)); } + if (App.Client.EnableMemberHistory) { + bool firstOnPage = false; + if (pdf.GetNumberOfPages() == 1) { + doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE)); + firstOnPage = true; + } + doc.Add(new KernedParagraph(12).Add(Bold($"Verlauf der Geschäftsanteile per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0)); + doc.Add(NewMemberBusinessSharesTable(MemberHistory)); + } } protected Cell NewDataHdr(string title, int colspan) { @@ -169,10 +183,10 @@ namespace Elwig.Documents { }).OrderByDescending(a => a.Size).ToList(); var tbl = new Table(ColsMM(40, 30, 35, 15, 25, 20), true) - .SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout() - .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) - .SetBorder(Border.NO_BORDER) - .SetFontSize(10); + .SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout() + .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) + .SetBorder(Border.NO_BORDER) + .SetFontSize(10); tbl.AddHeaderCell(NewTh("Katastralgemeinde", rowspan: 2, left: true)) .AddHeaderCell(NewTh("Ried", rowspan: 2, left: true)) @@ -209,5 +223,135 @@ namespace Elwig.Documents { return tbl; } + + protected Cell[] NewBusinessSharesCells(MemberHistoryPoint p, bool isDiff = true) { + if (App.Client.HasRedWhite) { + return [ + NewTd(isDiff ? (p.SharesRed == 0 ? "" : p.SharesRed < 0 ? $"–{-p.SharesRed:N0}" : $"+{p.SharesRed:N0}") : $"{p.SharesRed:N0}", + right: true, bold: !isDiff, borderTop: !isDiff), + NewTd(isDiff ? (p.SharesWhite == 0 ? "" : p.SharesWhite < 0 ? $"–{-p.SharesWhite:N0}" : $"+{p.SharesWhite:N0}") : $"{p.SharesWhite:N0}", + right: true, bold: !isDiff, borderTop: !isDiff), + NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"–{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}", + right: true, bold: !isDiff, borderTop: !isDiff), + ]; + } else { + return [ + NewTd(isDiff ? (p.Shares == 0 ? "" : p.Shares < 0 ? $"–{-p.Shares:N0}" : $"+{p.Shares:N0}") : $"{p.Shares:N0}", + right: true, bold: !isDiff, borderTop: !isDiff), + NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"–{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}", + right : true, bold: !isDiff, borderTop: !isDiff), + ]; + } + } + + protected Table NewMemberBusinessSharesTable(IEnumerable history) { + var tbl = new Table(ColsMM(18, 111, 12, 12, 12), true) + .SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout() + .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) + .SetBorder(Border.NO_BORDER) + .SetFontSize(10); + + var tw = App.Client.HasRedWhite ? 1 : 2; + tbl.AddHeaderCell(NewTh("Datum", rowspan: 2)) + .AddHeaderCell(NewTh("Text", rowspan: 2, colspan: tw, left: true)) + .AddHeaderCell(NewTh("Geschäftsanteile", colspan: App.Client.HasRedWhite ? 3 : 2)); + if (App.Client.HasRedWhite) { + tbl.AddHeaderCell(NewTh("rot")) + .AddHeaderCell(NewTh("weiß")) + .AddHeaderCell(NewTh("ruhend")); + } else { + tbl.AddHeaderCell(NewTh("normal")) + .AddHeaderCell(NewTh("ruhend")); + } + + if (!history.Any() || Member.EntryDate <= history.First().Date) { + tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}")) + .AddCell(NewTd("Eintritt", colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(new())); + } + + var recorded = history.Aggregate(new MemberHistoryPoint(), (s, h) => new( + s.Shares + (h.ToMgNr == Member.MgNr && h.ToType == 1 ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == 1 ? h.Shares : 0), + s.SharesRed + (h.ToMgNr == Member.MgNr && h.ToType == 2 ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == 2 ? h.Shares : 0), + s.SharesWhite + (h.ToMgNr == Member.MgNr && h.ToType == 3 ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == 3 ? h.Shares : 0), + s.SharesDormant + (h.ToMgNr == Member.MgNr && h.ToType == 9 ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == 9 ? h.Shares : 0))); + var cur = new MemberHistoryPoint(Member.Shares - recorded.Shares, Member.SharesRed - recorded.SharesRed, Member.SharesWhite - recorded.SharesWhite, Member.SharesDormant - recorded.SharesDormant); + + if (cur.Shares != 0 || cur.SharesRed != 0 || cur.SharesWhite != 0 || cur.SharesDormant != 0) { + tbl.AddCell(NewTd()) + .AddCell(NewTd("Nicht erfasst", colspan: tw, italic: true)) + .AddCells(NewBusinessSharesCells(cur)); + if (history.Any()) { + tbl.AddCell(NewTd(borderTop: true)) + .AddCell(NewTd($"Bis Lese {history.First().Date.Year - 1}", borderTop: true, colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(cur, isDiff: false)); + } + } + + DateOnly? lastDate = null; + foreach (var row in history) { + int s = 0, r = 0, w = 0, d = 0; + if (row.FromMgNr == Member.MgNr) { + switch (row.FromType) { + case 1: s -= row.Shares; break; + case 2: r -= row.Shares; break; + case 3: w -= row.Shares; break; + case 9: d -= row.Shares; break; + } + } + if (row.ToMgNr == Member.MgNr) { + switch (row.ToType) { + case 1: s += row.Shares; break; + case 2: r += row.Shares; break; + case 3: w += row.Shares; break; + case 9: d += row.Shares; break; + } + } + if (lastDate < Member.EntryDate && row.Date <= Member.EntryDate) { + tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}")) + .AddCell(NewTd("Eintritt", colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(new())); + } + if (lastDate != null && row.Date.Year != lastDate?.Year) { + tbl.AddCell(NewTd(borderTop: true)) + .AddCell(NewTd(lastDate?.Year == row.Date.Year - 1 ? $"Lese {lastDate?.Year}" : $"Lese {lastDate?.Year}–{row.Date.Year - 1}", borderTop: true, colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(cur, isDiff: false)); + } + if (lastDate < Member.ExitDate && row.Date <= Member.ExitDate) { + tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}")) + .AddCell(NewTd("Austritt", colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(new())); + } + cur = new(cur.Shares + s, cur.SharesRed + r, cur.SharesWhite + w, cur.SharesDormant + d); + + var reason = row.Reason; + if (reason == "auto") { + reason = "Automatische Nachzeichnung"; + } else if (reason == "transfer") { + if (row.ToMgNr == Member.MgNr) { + reason = $"Übertragen von {row.FromMember.AdministrativeName} (MgNr. {row.FromMgNr})"; + } else { + reason = $"Übertragen an {row.ToMember.AdministrativeName} (MgNr. {row.ToMgNr})"; + } + } + + tbl.AddCell(NewTd($"{row.Date:dd.MM.yyyy}")) + .AddCell(NewTd(reason, colspan: tw)) + .AddCells(NewBusinessSharesCells(new(s, r, w, d))); + lastDate = row.Date; + } + + if (Member.ExitDate != null && (!history.Any() || Member.ExitDate > history.Last().Date)) { + tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}")) + .AddCell(NewTd("Austritt", colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(new())); + } + + tbl.AddCell(NewTd(borderTop: true)) + .AddCell(NewTd(lastDate == null ? "Aktuell" : $"Ab Lese {lastDate?.Year}", borderTop: true, colspan: tw, bold: true)) + .AddCells(NewBusinessSharesCells(cur, isDiff: false)); + + return tbl; + } } }