[#20] MemberDataSheet: Show members business share history
Test / Run tests (push) Successful in 1m57s

This commit is contained in:
2026-07-03 11:56:49 +02:00
parent 7d5cdf1049
commit af8334a912
4 changed files with 158 additions and 14 deletions
+7 -7
View File
@@ -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));
}
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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) ?? [];
}
+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;
}
}
}