[#20] MemberDataSheet: Show members business share history

This commit is contained in:
2026-07-03 11:56:49 +02:00
parent 765edbdae6
commit 314a0d37a8
4 changed files with 158 additions and 14 deletions
+149 -5
View File
@@ -20,6 +20,7 @@ namespace Elwig.Documents {
public Season? Season;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public List<MemberHistory> MemberHistory = [];
public Dictionary<string, MemberBucket> MemberBuckets = [];
public List<AreaCom> 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<MemberHistory> 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;
}
}
}