[#20] MemberAdminWindow: Add MemberBusinessSharesAdminWindow to manage member shares

This commit is contained in:
2026-07-08 22:04:20 +02:00
parent 1169ba5101
commit e397c9d73d
29 changed files with 988 additions and 150 deletions
+4
View File
@@ -344,6 +344,10 @@ namespace Elwig {
return FocusWindow<DeliveryAdminWindow>(() => new(true), w => w.ViewModel.IsReceipt);
}
public static MemberBusinessSharesAdminWindow FocusMemberBusinessSharesWindow(int mgnr) {
return FocusWindow<MemberBusinessSharesAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
public static DeliveryAdminWindow FocusMemberDeliveries(int mgnr) {
return FocusWindow<DeliveryAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
+1 -1
View File
@@ -122,7 +122,7 @@ namespace Elwig.Controls {
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
SelectItemsReverse();
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
var dmp = !string.IsNullOrWhiteSpace(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrWhiteSpace(DisplayMemberPath) ? DisplayMemberPath : null;
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
AllItemsSelected = true;
} else if (SelectedItems.Count == 0) {
+45 -37
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using iText.Kernel.Colors;
using iText.Kernel.Pdf;
@@ -40,7 +41,7 @@ namespace Elwig.Documents {
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)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
MemberBuckets = await ctx.GetMemberBuckets(Utils.CurrentYear, Member.MgNr);
ActiveAreaCommitments = await Member.ActiveAreaCommitments(ctx)
@@ -245,15 +246,16 @@ namespace Elwig.Documents {
}
protected Table NewMemberBusinessSharesTable(IEnumerable<MemberHistory> history) {
var tbl = new Table(ColsMM(18, 111, 12, 12, 12), true)
var tbl = new Table(ColsMM(18, 93, App.Client.HasRedWhite ? 18 : 12, App.Client.HasRedWhite ? 12 : 18, 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))
tbl.AddHeaderCell(NewTh("Meldung am", rowspan: 2))
.AddHeaderCell(NewTh("Text", rowspan: 2, colspan: tw, left: true))
.AddHeaderCell(NewTh("Wirksam ab", rowspan: 2))
.AddHeaderCell(NewTh("Geschäftsanteile", colspan: App.Client.HasRedWhite ? 3 : 2));
if (App.Client.HasRedWhite) {
tbl.AddHeaderCell(NewTh("rot"))
@@ -264,26 +266,29 @@ namespace Elwig.Documents {
.AddHeaderCell(NewTh("ruhend"));
}
if (!history.Any() || Member.EntryDate <= history.First().Date) {
if (!history.Any() || Member.EntryDate <= history.First().DateNotice) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.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)));
s.Shares + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Normal ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Normal ? h.Shares : 0),
s.SharesRed + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Red ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Red ? h.Shares : 0),
s.SharesWhite + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.White ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.White ? h.Shares : 0),
s.SharesDormant + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Dormant ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Dormant ? 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))
.AddCell(NewTd("Nicht in Software erfasst", colspan: tw, italic: true))
.AddCell(NewTd())
.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))
.AddCell(NewTd($"Bis Lese {history.First().DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
}
@@ -293,62 +298,65 @@ namespace Elwig.Documents {
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;
case BusinessShareType.Normal: s -= row.Shares; break;
case BusinessShareType.Red: r -= row.Shares; break;
case BusinessShareType.White: w -= row.Shares; break;
case BusinessShareType.Dormant: 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;
case BusinessShareType.Normal: s += row.Shares; break;
case BusinessShareType.Red: r += row.Shares; break;
case BusinessShareType.White: w += row.Shares; break;
case BusinessShareType.Dormant: d += row.Shares; break;
}
}
if (lastDate < Member.EntryDate && row.Date <= Member.EntryDate) {
if (lastDate < Member.EntryDate && row.DateNotice <= Member.EntryDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (lastDate != null && row.Date.Year != lastDate?.Year) {
if (lastDate != null && row.DateNotice.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))
.AddCell(NewTd(lastDate?.Year == row.DateNotice.Year - 1 ? $"Lese {lastDate?.Year}" : $"Lese {lastDate?.Year}{row.DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
if (lastDate < Member.ExitDate && row.Date <= Member.ExitDate) {
if (lastDate < Member.ExitDate && row.DateNotice <= Member.ExitDate) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.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))
tbl.AddCell(NewTd($"{row.DateNotice:dd.MM.yyyy}"))
.AddCell(NewTd(row.FormatReasonPhrase(Member.MgNr), colspan: tw))
.AddCell(NewTd($"{row.DateEffective:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new(s, r, w, d)));
lastDate = row.Date;
lastDate = row.DateNotice;
}
if (Member.ExitDate != null && (!history.Any() || Member.ExitDate > history.Last().Date)) {
if (Member.EntryDate != null && history.Any() && Member.EntryDate > lastDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (Member.ExitDate != null && (!history.Any() || Member.ExitDate > lastDate)) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.AddCells(NewBusinessSharesCells(new()));
}
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(lastDate == null ? "Aktuell" : $"Ab Lese {lastDate?.Year}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
return tbl;
+1 -1
View File
@@ -86,7 +86,7 @@ namespace Elwig.Documents {
.AddCell(NewTd($"{m.Plz}", 8))
.AddCell(NewTd(m.Locality, 6))
.AddCell(NewTd(m.LfbisNr ?? "", 8))
.AddCell(NewTd($"{m.SharesTotal:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd($"{m.SharesActive:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd(m.DefaultKg ?? "", 6));
if (AreaComFilters.Length > 0) {
foreach (var v in AreaComFilters) {
+4
View File
@@ -315,6 +315,10 @@ namespace Elwig.Helpers {
return (await DeliverySchedules.Where(s => s.Year == year).Select(v => (int?)v.DsNr).MaxAsync() ?? 0) + 1;
}
public async Task<int> NextHistNr() {
return (await MemberHistory.Select(c => (int?)c.HistNr).MaxAsync() ?? 0) + 1;
}
public IAsyncEnumerable<Branch> FetchBranches(string? zwstid = null, bool includeWithoutMembers = true) {
return _compiledQueryBranches.Invoke(this, zwstid, includeWithoutMembers);
}
+5 -5
View File
@@ -54,14 +54,14 @@ namespace Elwig.Helpers.Billing {
if (addMinShares < 1) addMinShares = 1;
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
DELETE FROM member_history WHERE source = 'elwig' AND reason = 'auto' AND deduct_year = {Year};
INSERT INTO member_history (histnr, from_mgnr, from_type, to_mgnr, to_type, date, reason, source, deduct_year, shares, value_per_share, currency)
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
INSERT INTO member_history (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, deduct_year, shares, value_per_share, currency, comment)
SELECT COALESCE((SELECT MAX(histnr) AS histnr FROM member_history), 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr),
NULL, NULL, u.mgnr, 1, '{date:yyyy-MM-dd}', 'auto', 'elwig', s.year,
NULL, NULL, u.mgnr, 1, '{date:yyyy-MM-dd}', '{date:yyyy-MM-dd}', '{MemberHistory.REASON_PRESCRIPTION}', 'elwig', s.year,
CEIL((u.diff - {allowanceKg}.0 - {allowanceKgPerShare}.0 * u.shares) / s.max_kg_per_share
- {allowanceShares.ToString(CultureInfo.InvariantCulture)}
- {allowanceRel.ToString(CultureInfo.InvariantCulture)} * u.shares) AS adjust_shares,
s.share_value / POW(10, s.precision - 2), s.currency
s.share_value / POW(10, s.precision - 2), s.currency, 'durch automatische Nachzeichnung'
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
JOIN member m ON m.mgnr = u.mgnr
@@ -72,7 +72,7 @@ namespace Elwig.Helpers.Billing {
public async Task UnAdjustBusinessShares() {
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
DELETE FROM member_history WHERE source = 'elwig' AND reason = 'auto' AND deduct_year = {Year};
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
""");
}
+17 -7
View File
@@ -1,3 +1,4 @@
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
@@ -9,6 +10,7 @@ using System.IO.Compression;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Export {
public static class ElwigData {
@@ -239,6 +241,8 @@ namespace Elwig.Helpers.Export {
var device = meta.Device;
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var kgnrs = wbKgs.Select(k => k.KgNr).ToList();
var duplicateKgNrs = await ctx.WbKgs
@@ -402,9 +406,9 @@ namespace Elwig.Helpers.Export {
importedDeliveries.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, deliveries.Count - n - o, meta.DeliveryFilters));
}
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
var primaryKeys = new Dictionary<string, string>() {
["member"] = "mgnr, 0, 0",
@@ -748,15 +752,18 @@ namespace Elwig.Helpers.Export {
return new JsonObject {
["histnr"] = h.HistNr,
["from_mgnr"] = h.FromMgNr,
["from_type"] = h.FromType,
["from_type"] = h.FromType?.ToString().ToLower(),
["to_mgnr"] = h.ToMgNr,
["to_type"] = h.ToType,
["date"] = h.DateString,
["to_type"] = h.ToType?.ToString().ToLower(),
["date_notice"] = h.DateNoticeString,
["date_effective"] = h.DateEffectiveString,
["reason"] = h.Reason,
["source"] = h.Source,
["shares"] = h.Shares,
["signed"] = h.MemberHasSigned,
["value_per_share"] = h.ValuePerShare,
["currency"] = h.CurrencyCode,
["deduct_year"] = h.DeductYear,
["comment"] = h.Comment,
};
}
@@ -765,15 +772,18 @@ namespace Elwig.Helpers.Export {
return new MemberHistory {
HistNr = json["histnr"]!.AsValue().GetValue<int>(),
FromMgNr = json["from_mgnr"]?.AsValue().GetValue<int>(),
FromType = json["from_type"]?.AsValue().GetValue<int>(),
FromType = Enum.TryParse<BusinessShareType>(json["from_type"]?.AsValue().GetValue<string>(), true, out var from) ? from : null,
ToMgNr = json["to_mgnr"]?.AsValue().GetValue<int>(),
ToType = json["to_type"]?.AsValue().GetValue<int>(),
DateString = json["date"]!.AsValue().GetValue<string>(),
ToType = Enum.TryParse<BusinessShareType>(json["to_type"]?.AsValue().GetValue<string>(), true, out var to) ? to : null,
DateNoticeString = json["date_notice"]!.AsValue().GetValue<string>(),
DateEffectiveString = json["date_effective"]!.AsValue().GetValue<string>(),
Reason = json["reason"]!.AsValue().GetValue<string>(),
Source = json["source"]!.AsValue().GetValue<string>(),
Shares = json["shares"]!.AsValue().GetValue<int>(),
MemberHasSigned = json["signed"]?.AsValue().GetValue<bool>() ?? false,
ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(),
CurrencyCode = json["currency"]?.AsValue().GetValue<string>(),
DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(),
};
}
+8 -14
View File
@@ -136,8 +136,7 @@ namespace Elwig.Helpers {
return new(false, "PLZ zu kurz");
}
using var ctx = new AppDbContext();
int plz = int.Parse(input.Text);
if (ctx.Postleitzahlen.Find(plz) == null) {
if (!int.TryParse(input.Text, out var plz) || ctx.Postleitzahlen.Find(plz) == null) {
return new(false, "Ungültige PLZ");
}
return new(true, null);
@@ -423,8 +422,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || !ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ungültige Mitgliedsnummer");
}
@@ -440,8 +438,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Mitgliedsnummer wird bereits verwendet");
}
@@ -476,7 +473,7 @@ namespace Elwig.Helpers {
return res;
} else if (!required && input.Text.Length == 0) {
return new(true, null);
} else if (!ctx.MgNrExists(int.Parse(input.Text)).GetAwaiter().GetResult()) {
} else if (!int.TryParse(input.Text, out var nr) ||!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ein Mitglied mit dieser Mitgliedsnummer existiert nicht");
}
@@ -537,7 +534,7 @@ namespace Elwig.Helpers {
return (parts[0]?.Length == 4) ? new(true, null) : new(false, "Datum ist ungültig");
} else if (p == 1) {
// only month and year provided
int m = parts[0] != null && parts[0] != "" ? int.Parse(parts[0] ?? "0") : 0;
int m = parts[0] != null && parts[0] != "" && int.TryParse(parts[0], out var v) ? v : 0;
if (parts[1]?.Length != 4 || parts[0]?.Length != 2 || m < 1 || m > 12)
return new(false, "Datum ist ungültig");
return new(true, null);
@@ -589,8 +586,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult())) {
return new(false, "Flächenbindungsnummer wird bereits verwendet");
}
@@ -605,8 +601,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var oe = double.Parse(input.Text);
if (oe < 10 || oe >= 300) {
if (!double.TryParse(input.Text, out var oe) || oe < 10 || oe >= 300) {
return new(false, "Ungültiger Oechsle-Wert");
}
@@ -621,8 +616,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var kmw = double.Parse(input.Text);
if (kmw < 5 || kmw >= 50) {
if (!double.TryParse(input.Text, out var kmw) || kmw < 5 || kmw >= 50) {
return new(false, "Ungültiger KMW-Wert");
}
+5
View File
@@ -0,0 +1,5 @@
namespace Elwig.Models {
public enum BusinessShareType {
Normal = 1, Red = 2, White = 3, Dormant = 9,
}
}
+3 -5
View File
@@ -19,7 +19,7 @@ namespace Elwig.Models.Dtos {
("Locality", "Ort", null, 60),
("DefaultKg", "Stammgemeinde", null, 60),
("Branch", "Zweigstelle", null, 40),
("SharesTotal", "GA", null, 10),
("SharesActive", "GA", null, 10),
("BillingName", "Rechnungsname", null, 60),
("BillingAddress", "Rechnungsadresse", null, 60),
("BillingPlz", "PLZ", null, 10),
@@ -69,8 +69,7 @@ namespace Elwig.Models.Dtos {
public int Shares;
public int SharesRed;
public int SharesWhite;
public int SharesDormant;
public int SharesTotal;
public int SharesActive;
public string Address;
public int Plz;
public string Locality;
@@ -105,8 +104,7 @@ namespace Elwig.Models.Dtos {
Shares = m.Shares;
SharesRed = m.SharesRed;
SharesWhite = m.SharesWhite;
SharesDormant = m.SharesDormant;
SharesTotal = Shares + SharesRed + SharesWhite + SharesDormant;
SharesActive = Shares + SharesRed + SharesWhite;
Address = m.Address;
Plz = m.PostalDest.AtPlz!.Plz;
Locality = m.PostalDest.AtPlz!.Ort.Name;
+2 -2
View File
@@ -42,6 +42,8 @@ namespace Elwig.Models.Entities {
[NotMapped]
public string ShortName => (!string.IsNullOrWhiteSpace(GivenName) ? $"{GivenName} " : "") + Name;
[NotMapped]
public string FullAdministrativeName => $"{AdministrativeName} ({MgNr})";
[NotMapped]
public string AdministrativeName => AdministrativeName1 + (!string.IsNullOrWhiteSpace(AdministrativeName2) ? $" {AdministrativeName2}" : "");
[NotMapped]
public string AdministrativeName1 => IsJuridicalPerson ? Name : Name.Replace('ß', 'ẞ').ToUpper();
@@ -79,8 +81,6 @@ namespace Elwig.Models.Entities {
[Column("shares_dormant")]
public int SharesDormant { get; set; }
[NotMapped]
public int SharesTotal => Shares + SharesRed + SharesWhite + SharesDormant;
[NotMapped]
public int SharesActive => Shares + SharesRed + SharesWhite;
+68 -9
View File
@@ -6,29 +6,69 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("member_history"), PrimaryKey("HistNr")]
public class MemberHistory {
public const string REASON_PURCHASE = "purchase"; // Kauf
public const string REASON_CONVERT = "convert"; // Änderung
public const string REASON_TRANSFER = "transfer"; // Übertragung
public const string REASON_PRESCRIPTION = "prescription"; // Vorschreibung
public const string REASON_EXCLUSION = "exclusion"; // Ausschluss
public const string REASON_DECEASED = "deceased"; // Tod
public const string REASON_RETIREMENT = "retirement"; // Kündigung/stilllegen
public const string REASON_PAYOUT = "payout"; // Auszahlung
[Column("histnr")]
public int HistNr { get; set; }
[Column("from_mgnr")]
public int? FromMgNr { get; set; }
[Column("from_type")]
public int? FromType { get; set; }
public int? FromTypeValue { get; set; }
[NotMapped]
public BusinessShareType? FromType {
get => (BusinessShareType?)FromTypeValue;
set => FromTypeValue = (int?)value;
}
[Column("to_mgnr")]
public int? ToMgNr { get; set; }
[Column("to_type")]
public int? ToType { get; set; }
[Column("date")]
public required string DateString { get; set; }
public int? ToTypeValue { get; set; }
[NotMapped]
public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
public BusinessShareType? ToType {
get => (BusinessShareType?)ToTypeValue;
set => ToTypeValue = (int?)value;
}
[Column("date_notice")]
public required string DateNoticeString { get; set; }
[NotMapped]
public DateOnly DateNotice {
get => DateOnly.ParseExact(DateNoticeString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("date_effective")]
public required string DateEffectiveString { get; set; }
[NotMapped]
public DateOnly DateEffective {
get => DateOnly.ParseExact(DateEffectiveString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("reason")]
public required string Reason { get; set; }
[NotMapped]
public string ReasonPretty => Reason switch {
REASON_PURCHASE => "Kauf",
REASON_CONVERT => "Änderung",
REASON_TRANSFER => "Übertragung",
REASON_PRESCRIPTION => "Vorschreibung",
REASON_EXCLUSION => "Ausschluss",
REASON_DECEASED => "Tod",
REASON_RETIREMENT => "Kündigung",
REASON_PAYOUT => "Auszahlung",
_ => Reason,
};
[Column("source")]
public required string Source { get; set; }
@@ -36,6 +76,9 @@ namespace Elwig.Models.Entities {
[Column("shares")]
public int Shares { get; set; }
[Column("signed")]
public bool MemberHasSigned { get; set; }
[Column("value_per_share")]
public long? ValuePerShareValue { get; set; }
[NotMapped]
@@ -56,11 +99,27 @@ namespace Elwig.Models.Entities {
[Column("comment")]
public string? Comment { get; set; }
public string FormatReasonPhrase(int mgnr) {
if (Reason == REASON_TRANSFER) {
if (FromMgNr == mgnr) {
return $"{ReasonPretty} an {ToMember?.FullAdministrativeName ?? "Nachfolger"}" + (Comment != null ? $", {Comment}" : "");
} else if (ToMgNr == mgnr) {
return $"{ReasonPretty} von {FromMember?.FullAdministrativeName ?? "Vorgänger"}" + (Comment != null ? $", {Comment}" : "");
}
}
return ReasonPretty + (Comment != null ? $", {Comment}" : "");
}
[NotMapped]
public int? PoVMgNr { get; set; }
[NotMapped]
public string PoVReasonPhrase => FormatReasonPhrase(PoVMgNr ?? 0);
[ForeignKey("FromMgNr")]
public virtual Member FromMember { get; private set; } = null!;
public virtual Member? FromMember { get; private set; } = null!;
[ForeignKey("ToMgNr")]
public virtual Member ToMember { get; private set; } = null!;
public virtual Member? ToMember { get; private set; } = null!;
[ForeignKey("CurrencyCode")]
public virtual Currency Currency { get; private set; } = null!;
+22 -11
View File
@@ -7,6 +7,13 @@ ALTER TABLE member ADD COLUMN shares_red INTEGER NOT NULL DEFAULT 0;
ALTER TABLE member ADD COLUMN shares_white INTEGER NOT NULL DEFAULT 0;
ALTER TABLE member ADD COLUMN shares_dormant INTEGER NOT NULL DEFAULT 0;
UPDATE sqlite_schema SET sql = REPLACE(sql, CHAR(10) ||
') STRICT',
',' || CHAR(10) ||
' CONSTRAINT c_member_shares CHECK (shares >= 0 AND shares_red >= 0 AND shares_white >= 0 AND shares_dormant >= 0)' || CHAR(10) ||
') STRICT')
WHERE type = 'table' AND name = 'member';
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
UPDATE member
SET shares_dormant = shares + shares_red + shares_white,
@@ -20,11 +27,13 @@ CREATE TABLE member_history_new (
from_type INTEGER,
to_mgnr INTEGER,
to_type INTEGER,
date TEXT NOT NULL CHECK (date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
reason TEXT NOT NULL CHECK (reason REGEXP '^[a-z_]+$'),
source TEXT NOT NULL CHECK (source REGEXP '^[a-z_]+$'),
date_notice TEXT NOT NULL CHECK (date_notice REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
date_effective TEXT NOT NULL CHECK (date_effective REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
reason TEXT NOT NULL CHECK (reason REGEXP '^[a-z_]+$'),
source TEXT NOT NULL CHECK (source REGEXP '^[a-z_]+$'),
shares INTEGER NOT NULL,
shares INTEGER NOT NULL CHECK (shares > 0),
signed INTEGER NOT NULL CHECK (signed IN (TRUE, FALSE)) DEFAULT FALSE,
value_per_share INTEGER DEFAULT NULL,
currency TEXT DEFAULT NULL,
deduct_year INTEGER DEFAULT NULL,
@@ -45,8 +54,10 @@ CREATE TABLE member_history_new (
((to_mgnr IS NULL AND to_type IS NULL) OR (to_mgnr IS NOT NULL AND to_type IS NOT NULL)))
) STRICT;
INSERT INTO member_history_new (histnr, from_mgnr, from_type, to_mgnr, to_type, date, reason, source, shares, value_per_share, currency, deduct_year, comment)
SELECT ROW_NUMBER() OVER(ORDER BY h.date, h.mgnr), NULL, NULL, h.mgnr, 1, h.date, h.type, 'elwig', h.business_shares, s.bs_value / POW(10, s.precision - 2), s.currency, IIF(h.type = 'auto', s.year, NULL), comment
INSERT INTO member_history_new (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, shares, value_per_share, currency, deduct_year, comment)
SELECT ROW_NUMBER() OVER(ORDER BY h.date, h.mgnr), NULL, NULL, h.mgnr, 1, h.date, h.date,
IIF(h.type = 'auto', 'prescription', h.type), 'elwig', h.business_shares, s.bs_value / POW(10, s.precision - 2), s.currency,
IIF(h.type = 'auto', s.year, NULL), 'automatische Nachzeichnung'
FROM member_history h
JOIN season s ON s.year = SUBSTR(h.date, 1, 4);
@@ -70,7 +81,7 @@ BEGIN
END;
CREATE TRIGGER t_member_history_u_member
AFTER UPDATE ON member_history FOR EACH ROW
AFTER UPDATE OF from_mgnr, from_type, to_mgnr, to_type, shares ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 1;
@@ -247,8 +258,8 @@ SELECT m.mgnr, s.year,
m.shares_white + SUM(IIF(h1.from_type = 3, h1.shares, 0)) - SUM(IIF(h2.to_type = 3, h2.shares, 0)) AS shares_white,
m.shares_dormant + SUM(IIF(h1.from_type = 9, h1.shares, 0)) - SUM(IIF(h2.to_type = 9, h2.shares, 0)) AS shares_dormant
FROM member m, v_virtual_season s
LEFT JOIN member_history h1 ON (SUBSTR(h1.date, 1, 4) + 0) > s.year AND h1.from_mgnr = m.mgnr
LEFT JOIN member_history h2 ON (SUBSTR(h2.date, 1, 4) + 0) > s.year AND h2.to_mgnr = m.mgnr
LEFT JOIN member_history h1 ON (SUBSTR(h1.date_notice, 1, 4) + 0) > s.year AND h1.from_mgnr = m.mgnr
LEFT JOIN member_history h2 ON (SUBSTR(h2.date_notice, 1, 4) + 0) > s.year AND h2.to_mgnr = m.mgnr
GROUP BY m.mgnr, s.year
ORDER BY m.mgnr, s.year;
@@ -307,12 +318,12 @@ ORDER BY u.year, u.mgnr;
DROP VIEW v_auto_business_shares;
CREATE VIEW v_auto_business_shares AS
SELECT (SUBSTR(h.date, 1, 4) + 0) AS year,
SELECT (SUBSTR(h.date_notice, 1, 4) + 0) AS year,
h.to_mgnr AS mgnr,
SUM(h.shares) AS shares,
SUM(h.shares * COALESCE(h.value_per_share, 0)) AS total_amount
FROM member_history h
WHERE h.reason = 'auto' AND h.source = 'elwig'
WHERE h.reason = 'prescription' AND h.source = 'elwig'
GROUP BY year, h.to_mgnr
ORDER BY year, h.to_mgnr;
+1 -2
View File
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Services {
public static class AreaComService {
@@ -115,7 +114,7 @@ namespace Elwig.Services {
using var ctx = new AppDbContext();
var c = new AreaComContract {
FbNr = oldFbNr ?? newFbNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
KgNr = vm.Kg!.KgNr,
RdNr = vm.Rd?.RdNr,
};
-1
View File
@@ -8,7 +8,6 @@ using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using System.Windows;
using System;
using LinqKit;
using System.Windows.Controls;
+3 -13
View File
@@ -6,7 +6,6 @@ using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System;
using Elwig.ViewModels;
using LinqKit;
@@ -23,15 +22,6 @@ namespace Elwig.Services {
FromFilters, FromToday, FromSeason, FromSeasonAndBranch, Selected,
};
public static async Task<Member?> GetMemberAsync(int mgnr) {
using var ctx = new AppDbContext();
return await ctx.FetchMembers(mgnr).SingleOrDefaultAsync();
}
public static Member? GetMember(int mgnr) {
return GetMemberAsync(mgnr).GetAwaiter().GetResult();
}
public static void ClearInputs(this DeliveryAdminViewModel vm) {
}
@@ -486,7 +476,7 @@ namespace Elwig.Services {
if (partNew && timeIsDefault) {
newTimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (partNew || timeHasChanged) {
newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00";
newTimeString = string.IsNullOrWhiteSpace(vm.Time) ? null : vm.Time + ":00";
}
var d = new Delivery {
@@ -498,7 +488,7 @@ namespace Elwig.Services {
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = vm.MgNr!.Value,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
};
p = new DeliveryPart {
@@ -521,7 +511,7 @@ namespace Elwig.Services {
Unloading = vm.Unloading,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Comment = string.IsNullOrWhiteSpace(vm.PartComment) ? null : vm.PartComment,
Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing,
+13
View File
@@ -48,6 +48,19 @@ namespace Elwig.Services {
}
}
public static bool? AskContinueYesNo(string title, string text) {
if (NextContinue != null) {
var r = NextContinue(title, text);
NextContinue = null;
return r;
} else if (Override != null) {
return Override("continue", title, text) != null;
} else {
var r = MessageBox.Show(text, title, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
return r == MessageBoxResult.Yes ? true : r == MessageBoxResult.No ? false : null;
}
}
public static bool AskConfirmation(string title, string text) {
if (NextConfirmation != null) {
var r = NextConfirmation(title, text);
@@ -0,0 +1,98 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Services {
public static class MemberBusinessSharesService {
public static async Task InitInputs(this MemberBusinessSharesViewModel vm) {
using var ctx = new AppDbContext();
vm.DateNoticeString = $"{Utils.Today:dd.MM.yyyy}";
vm.ValuePerShare = (await ctx.Seasons.OrderBy(s => s.Year).LastOrDefaultAsync())?.BusinessShareValue;
}
public static void ClearInputs(this MemberBusinessSharesViewModel vm) {
}
public static void FillInputs(this MemberBusinessSharesViewModel vm, MemberHistory h) {
var mgnr = vm.FilterMember.MgNr;
vm.DateNotice = h.DateNotice;
vm.DateEffective = h.DateEffective;
vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : "");
vm.Shares = h.Shares;
vm.MemberHasSigned = h.MemberHasSigned;
vm.ValuePerShare = h.ValuePerShare;
vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null;
vm.DeductYear = h.DeductYear ?? h.DateNotice.Year;
vm.Deduct = h.DeductYear != null;
vm.Comment = h.Comment;
}
public static async Task<int> UpdateMemberHistory(this MemberBusinessSharesViewModel vm, int? oldHistNr, bool enableTriggers) {
return await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var h = new MemberHistory {
HistNr = oldHistNr ?? await ctx.NextHistNr(),
FromMgNr =
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_from" ? vm.OtherMgNr :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
vm.FilterMember.MgNr,
FromType =
vm.Reason == MemberHistory.REASON_PAYOUT ? BusinessShareType.Dormant :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
BusinessShareType.Normal,
ToMgNr =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_to" ? vm.OtherMgNr :
vm.FilterMember.MgNr,
ToType =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == MemberHistory.REASON_RETIREMENT || vm.Reason == MemberHistory.REASON_DECEASED ? BusinessShareType.Dormant :
BusinessShareType.Normal,
DateNoticeString = $"{vm.DateNotice:yyyy-MM-dd}",
DateEffectiveString = $"{vm.DateEffective:yyyy-MM-dd}",
Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT,
Source = "manual",
Shares = vm.Shares ?? 0,
MemberHasSigned = vm.MemberHasSigned,
ValuePerShare = vm.ValuePerShare,
CurrencyCode = vm.ValuePerShare != null ? "EUR" : null,
DeductYear = vm.Deduct ? vm.DeductYear : null,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment.Trim(),
};
if (oldHistNr != null) {
ctx.Update(h);
} else {
ctx.Add(h);
}
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
return h.HistNr;
});
}
public static async Task DeleteMemberHistory(int histnr, bool enableTriggers) {
await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await ctx.MemberHistory.Where(h => h.HistNr == histnr).ExecuteDeleteAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
});
}
}
}
+13 -10
View File
@@ -501,7 +501,10 @@ namespace Elwig.Services {
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2).DistinctBy(h => h.HistNr).OrderBy(h => h.HistNr).ToList();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
@@ -541,33 +544,33 @@ namespace Elwig.Services {
Name = vm.Name!,
Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
Birthday = string.IsNullOrWhiteSpace(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
IsDeceased = vm.IsDeceased,
CountryNum = 40, // Austria AT AUT
PostalDestId = vm.Ort!.Id,
Address = vm.Address!,
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
Iban = string.IsNullOrWhiteSpace(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrWhiteSpace(vm.Bic) ? null : vm.Bic,
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic,
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
EntryDateString = string.IsNullOrWhiteSpace(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrWhiteSpace(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
Shares = App.Client.HasRedWhite ? 0 : vm.BusinessShares1 ?? 0,
SharesRed = App.Client.HasRedWhite ? vm.BusinessShares1 ?? 0 : 0,
SharesWhite = App.Client.HasRedWhite ? vm.BusinessShares2 ?? 0 : 0,
SharesDormant = App.Client.HasRedWhite ? vm.BusinessShares3 ?? 0 : vm.BusinessShares2 ?? 0,
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
AccountingNr = string.IsNullOrWhiteSpace(vm.AccountingNr) ? null : vm.AccountingNr,
IsActive = vm.IsActive,
IsVollLieferant = vm.IsVollLieferant,
IsFunktionär = vm.IsFunktionär,
ZwstId = vm.Branch?.ZwstId,
DefaultKgNr = vm.DefaultKg?.KgNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
ContactViaPost = vm.ContactViaPost,
ContactViaEmail = vm.ContactViaEmail,
};
+1 -1
View File
@@ -300,7 +300,7 @@ namespace Elwig.Services {
TransferDate = vm.TransferDate,
TestVariant = vm.SelectedPaymentVariant?.TestVariant ?? true,
CalcTimeUnix = vm.SelectedPaymentVariant?.CalcTimeUnix,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
Data = JsonSerializer.Serialize(d.Data),
};
avnr = v.AvNr;
+4 -1
View File
@@ -33,7 +33,10 @@ namespace Elwig.Services {
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2).DistinctBy(h => h.HistNr).OrderBy(h => h.HistNr).ToList();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
@@ -0,0 +1,76 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Windows;
namespace Elwig.ViewModels {
public partial class MemberBusinessSharesViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Geschäftsanteilbewegungen - Elwig";
public required Member FilterMember { get; set; }
[ObservableProperty]
private MemberHistory? _selectedHistoryEntry;
[ObservableProperty]
private IEnumerable<MemberHistory> _history = [];
[ObservableProperty]
private string? _dateNoticeString;
public DateOnly? DateNotice {
get => DateOnly.TryParseExact(DateNoticeString, "dd.MM.yyyy", out var d) ? d : null;
set => DateNoticeString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _dateEffectiveString;
public DateOnly? DateEffective {
get => DateOnly.TryParseExact(DateEffectiveString, "dd.MM.yyyy", out var d) ? d : null;
set => DateEffectiveString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _reason;
[ObservableProperty]
private string? _sharesString;
public int? Shares {
get => int.TryParse(SharesString, out var shares) ? shares : null;
set => SharesString = $"{value}";
}
[ObservableProperty]
private string? _otherMgNrString;
public int? OtherMgNr {
get => int.TryParse(OtherMgNrString, out var mgnr) ? mgnr : null;
set => OtherMgNrString = $"{value}";
}
[ObservableProperty]
private string? _deductYearString;
public int? DeductYear {
get => int.TryParse(DeductYearString, out var year) ? year : null;
set => DeductYearString = $"{value}";
}
[ObservableProperty]
private bool _deduct;
[ObservableProperty]
private bool _memberHasSigned;
[ObservableProperty]
private string? _valuePerShareString;
public decimal? ValuePerShare {
get => decimal.TryParse(ValuePerShareString, out var v) ? v : null;
set => ValuePerShareString = $"{value:0.00}";
}
[ObservableProperty]
private string? _totalAmount;
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
[ObservableProperty]
private Visibility _personalNameVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _juridicalNameVisibility = Visibility.Hidden;
}
}
+9 -4
View File
@@ -164,10 +164,10 @@ namespace Elwig.Windows {
} else if (input is TextBox tb && tb.Text.Length == 0) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.HasItems) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null && lb.ItemsSource.Cast<object>().Any()) {
} else if (input is ListBox lb && lb.SelectedItem == null && lb.HasItems) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
@@ -285,8 +285,13 @@ namespace Elwig.Windows {
var binding = cb.GetBindingExpression(ComboBox.SelectedItemProperty);
binding?.UpdateSource();
}
foreach (var lb in ListBoxInputs)
lb.SelectedItems.Clear();
foreach (var lb in ListBoxInputs) {
if (lb.SelectionMode == SelectionMode.Single) {
lb.SelectedItem = null;
} else {
lb.SelectedItems.Clear();
}
}
foreach (var cb in CheckBoxInputs) {
cb.IsChecked = cb.IsThreeState ? null : false;
var binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
+13 -13
View File
@@ -361,29 +361,29 @@ namespace Elwig.Windows {
private async Task UpdateClientParameters(ClientParameters p) {
p.Name = ClientNameInput.Text;
p.NameSuffix = string.IsNullOrEmpty(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameSuffix = string.IsNullOrWhiteSpace(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameType = ClientNameTypeInput.Text;
p.NameToken = ClientNameTokenInput.Text;
p.NameShort = ClientNameShortInput.Text;
p.Address = ClientAddressInput.Text;
p.Plz = int.Parse(ClientPlzInput.Text);
p.Ort = ClientOrtInput.Text;
p.Iban = string.IsNullOrEmpty(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrEmpty(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrEmpty(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrEmpty(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrEmpty(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrEmpty(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrEmpty(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrEmpty(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrEmpty(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.Iban = string.IsNullOrWhiteSpace(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrWhiteSpace(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrWhiteSpace(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.ModeBusinessShares = ParameterBusinessShareModeInput.SelectedIndex;
p.EnableMemberHistory = ParameterBusinessShareHistoryInput.IsChecked ?? false;
p.TextDeliveryNote = string.IsNullOrEmpty(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.TextDeliveryNote = string.IsNullOrWhiteSpace(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
p.TextDeliveryConfirmation = string.IsNullOrEmpty(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrEmpty(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.TextDeliveryConfirmation = string.IsNullOrWhiteSpace(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrWhiteSpace(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.ExportEbicsVersion = ParameterExportEbicsVersion.SelectedIndex + 3;
p.ExportEbicsAddress = ParameterExportEbicsAddress.SelectedIndex;
+4 -3
View File
@@ -124,7 +124,8 @@ namespace Elwig.Windows {
public DeliveryAdminWindow(int mgnr) :
this() {
ViewModel.FilterMember = DeliveryService.GetMember(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ViewModel.EnableAllSeasons = true;
}
@@ -482,7 +483,7 @@ namespace Elwig.Windows {
await base.OnRenewContext(ctx);
if (ViewModel.FilterMember != null) {
if (await DeliveryService.GetMemberAsync(ViewModel.FilterMember.MgNr) is not Member m) {
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
@@ -1200,7 +1201,7 @@ namespace Elwig.Windows {
}
private async Task UpdateLsNr() {
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
if (string.IsNullOrWhiteSpace(ViewModel.Date) || ViewModel.Branch == null) {
ViewModel.LsNr = "";
} else {
try {
+1 -1
View File
@@ -233,7 +233,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="140"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/>
<DataGridTextColumn Header="GA" Binding="{Binding SharesTotal, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn Header="GA" Binding="{Binding SharesActive, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
+6 -9
View File
@@ -205,14 +205,12 @@ namespace Elwig.Windows {
if (rA > 0) a.Add((rA, Brushes.DarkRed));
if (wA > 0) a.Add((wA, Brushes.DarkGreen));
if (a.Count == 0) a.Add((0, null));
if (dA > 0) a.Add((dA, null));
List<(int Count, Brush? Color)> b = [];
if (stat.Shares > 0) b.Add((stat.Shares, null));
if (stat.SharesRed > 0) b.Add((stat.SharesRed, Brushes.DarkRed));
if (stat.SharesWhite > 0) b.Add((stat.SharesWhite, Brushes.DarkGreen));
if (a.Count == 0) b.Add((0, null));
if (stat.SharesDormant > 0) b.Add((stat.SharesDormant, null));
Run[] inlines = [
new("Geschäftsanteile: "),
@@ -605,8 +603,7 @@ namespace Elwig.Windows {
}
private void BusinessSharesButton_Click(object sender, RoutedEventArgs evt) {
// TODO
// App.FocusBusinessSharesWindow(ViewModel.SelectedMember!.MgNr);
App.FocusMemberBusinessSharesWindow(ViewModel.SelectedMember!.MgNr);
}
private void AreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
@@ -851,7 +848,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = EmailAddressInputs[i];
var vis = !string.IsNullOrEmpty(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetEmailAddressInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -859,7 +856,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < PhoneNrInputs.Length; i++) {
var input = PhoneNrInputs[i];
var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -928,9 +925,9 @@ namespace Elwig.Windows {
var oldMember = await ctx.FetchMembers(mgnr).SingleAsync();
var newName = $"{ViewModel.Name?.Replace('ß', 'ẞ').ToUpper()} " +
$"{ViewModel.Prefix}{(!string.IsNullOrEmpty(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrEmpty(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrEmpty(ViewModel.Suffix) ? " " : "")}";
$"{ViewModel.Prefix}{(!string.IsNullOrWhiteSpace(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrWhiteSpace(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrWhiteSpace(ViewModel.Suffix) ? " " : "")}";
var d = new AreaComTransferDialog(oldMember.AdministrativeName, newName, areaComs.Count, areaComs.Sum(c => c.Area));
if (d.ShowDialog() != true)
return;
@@ -0,0 +1,214 @@
<local:AdministrationWindow
x:Class="Elwig.Windows.MemberBusinessSharesAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls"
xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="{Binding Title}" Height="450" Width="800" MinHeight="400" MinWidth="700">
<Window.DataContext>
<vm:MemberBusinessSharesViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ctrl:UnitTextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="27"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="300"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*" MinWidth="350"/>
</Grid.ColumnDefinitions>
<Grid Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="MemberHistoryList" Margin="5,10,5,0" Grid.ColumnSpan="3"
ItemsSource="{Binding History, Mode=TwoWay}" SelectedItem="{Binding SelectedHistoryEntry, Mode=TwoWay}"
SelectionChanged="MemberHistoryList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DateNotice, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding DateEffective, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Shares, StringFormat='{}{0:N0}'}" Width="15" TextAlignment="Right" Margin="0,0,10,0"/>
<TextBlock Text="{Binding PoVReasonPhrase}" TextAlignment="Left" Margin="0,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="NewHistoryButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="NewHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditHistoryButton" Content="Bearbeiten" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="EditHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteHistoryButton" Content="Löschen" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="DeleteHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Entf</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="SaveButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="ResetButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}" IsCancel="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="CancelButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Esc</TextBlock>
</Button.ToolTip>
</Button>
</Grid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="95"/>
</Grid.RowDefinitions>
<GroupBox Header="Geschäftsanteilbewegung" Margin="5,10,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Grund:" Margin="10,10,0,0" Grid.Column="0"/>
<ComboBox x:Name="ReasonInput" SelectedValue="{Binding Reason, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1"
SelectedValuePath="Tag"
SelectionChanged="ReasonInput_SelectionChanged">
<ComboBoxItem Tag="purchase">Kauf</ComboBoxItem>
<ComboBoxItem Tag="transfer_to">Übertragung an...</ComboBoxItem>
<ComboBoxItem Tag="transfer_from">Übertragung von...</ComboBoxItem>
<ComboBoxItem Tag="prescription">Vorschreibung</ComboBoxItem>
<ComboBoxItem Tag="exclusion">Ausschluss</ComboBoxItem>
<ComboBoxItem Tag="deceased">Tod</ComboBoxItem>
<ComboBoxItem Tag="retirement">Kündigung</ComboBoxItem>
<ComboBoxItem Tag="payout">Auszahlung</ComboBoxItem>
</ComboBox>
<Label Content="Anderes Mitgl.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="OtherMgNrInput" Text="{Binding OtherMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="48" Grid.Column="1" Margin="0,40,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="OtherMgNrInput_TextChanged" LostFocus="OtherMgNrInput_LostFocus"/>
<ComboBox x:Name="OtherMemberInput"
Grid.Column="1" Margin="53,40,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="OtherMemberInput_SelectionChanged"/>
<Button x:Name="OtherMemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="10,40,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="OtherMemberReferenceButton_Click"/>
<Label Content="Anmerkung:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="1"/>
<Label Content="Meldung:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="DateNoticeInput" Text="{Binding DateNoticeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/>
<TextBox x:Name="DateEffeciveInput" Text="{Binding DateEffectiveString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="SharesInput" Text="{Binding SharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,10,0" Width="32" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="SharesInput_TextChanged"/>
<CheckBox x:Name="DeductInput" IsChecked="{Binding Deduct, Mode=TwoWay}"
Margin="42,165,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed">
<TextBlock>
bei Auszahlung <Run Text="{Binding DeductYearString}"/> abziehen
</TextBlock>
</CheckBox>
<Label Content="Betrag:" Margin="10,190,0,10"/>
<ctrl:UnitTextBox x:Name="ValuePerShareInput" Text="{Binding ValuePerShareString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Unit="€/GA" TextChanged="AmountPerShareInput_TextChanged"
Grid.Column="1" Width="76" Margin="0,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="TotalAmountInput" Text="{Binding TotalAmount}" Unit="€" IsEnabled="False"
Grid.Column="1" Width="77" Margin="81,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
<GroupBox Header="Mitglied" Grid.Row="1" Margin="5,5,5,10">
<Grid>
<TextBlock x:Name="MemberInfo" Text="Name (1234)"
FontSize="14" Margin="10,10,10,10"/>
</Grid>
</GroupBox>
</Grid>
</Grid>
</local:AdministrationWindow>
@@ -0,0 +1,347 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Elwig.Windows {
public partial class MemberBusinessSharesAdminWindow : AdministrationWindow {
public MemberBusinessSharesViewModel ViewModel => (MemberBusinessSharesViewModel)DataContext;
public MemberBusinessSharesAdminWindow(int mgnr) {
InitializeComponent();
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ExemptInputs = [
MemberHistoryList, TotalAmountInput
];
RequiredInputs = [
ReasonInput, DateNoticeInput, DateEffeciveInput, SharesInput
];
}
public void FocusMemberHistory(int histnr) {
if (IsEditing || IsCreating) return;
var item = ViewModel.History.FirstOrDefault(h => h.HistNr == histnr);
if (item != null) {
ControlUtils.SelectItem(MemberHistoryList, item);
}
}
private async Task RefreshList() {
var vm = ViewModel;
var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var history = await Task.Run(async () => {
using var ctx = new AppDbContext();
return await ctx.MemberHistory
.Where(h => h.FromMgNr == vm.FilterMember.MgNr || h.ToMgNr == vm.FilterMember.MgNr)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
});
if (!cursor) Mouse.OverrideCursor = null;
history.ForEach(h => h.PoVMgNr = vm.FilterMember.MgNr);
ControlUtils.RenewItemsSource(MemberHistoryList, history,
MemberHistoryList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
RefreshInputs();
}
private void RefreshInputs(bool validate = false) {
ClearInputStates();
if (ViewModel.SelectedHistoryEntry is MemberHistory h) {
EditHistoryButton.IsEnabled = true;
DeleteHistoryButton.IsEnabled = true;
FillInputs(h);
} else {
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ClearOriginalValues();
ClearDefaultValues();
ClearInputs(validate);
ClearInputStates();
}
GC.Collect();
}
private void FillInputs(MemberHistory h) {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.FillInputs(h);
ReasonInput.SelectedValue = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.FromMgNr == ViewModel.FilterMember.MgNr ? "to" : "from")}" : "");
FinishInputFilling();
}
private async Task InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.InitInputs();
SetDefaultValue(DateNoticeInput);
if (ViewModel.ValuePerShare != null)
SetDefaultValue(ValuePerShareInput);
ValidateRequiredInputs();
}
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
base.ClearInputs(validate);
}
override protected void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch;
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
ViewModel.FilterMember = m;
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ControlUtils.RenewItemsSource(OtherMemberInput, await ctx.FetchMembers(includeNotActive: true).ToListAsync());
MemberInfo.Text = $"{m.FullAdministrativeName}\nGA: {m.Shares:N0} (normal) / {m.SharesRed:N0} (rot) / {m.SharesWhite:N0} (weiß) / {m.SharesDormant:N0} (ruhend)";
await RefreshList();
}
private void MemberHistoryList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
RefreshInputs();
}
protected override void ShortcutNew() {
if (!NewHistoryButton.IsEnabled || NewHistoryButton.Visibility != Visibility.Visible)
return;
NewHistoryButton_Click(null, null);
}
private async void NewHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true;
MemberHistoryList.IsEnabled = false;
ViewModel.SelectedHistoryEntry = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
await InitInputs();
DeductInput.IsEnabled = false;
OtherMgNrInput.IsEnabled = false;
OtherMemberInput.IsEnabled = false;
}
protected override void ShortcutEdit() {
if (!EditHistoryButton.IsEnabled || EditHistoryButton.Visibility != Visibility.Visible)
return;
EditHistoryButton_Click(null, null);
}
private void EditHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry == null) return;
IsEditing = true;
MemberHistoryList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
}
protected override void ShortcutDelete() {
if (!DeleteHistoryButton.IsEnabled || DeleteHistoryButton.Visibility != Visibility.Visible)
return;
DeleteHistoryButton_Click(null, null);
}
private async void DeleteHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry is not MemberHistory h)
return;
if (InteractionService.AskContinueYesNo("Geschäftsanteilbewegung löschen", $"Soll die Geschäftsanteilbewegung wirklich unwiderruflich\ngelöscht werden?\n\nFalls ja:\nSoll die Änderung der aktuellen GA wieder rückgängig gemacht werden?") is bool enableTriggers) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await MemberBusinessSharesService.DeleteMemberHistory(h.HistNr, enableTriggers);
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung löschen", exc);
}
Mouse.OverrideCursor = null;
}
}
protected override void ShortcutSave() {
if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
return;
SaveButton_Click(null, null);
}
private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
Mouse.OverrideCursor = Cursors.Wait;
SaveButton.IsEnabled = false;
int mgnr;
try {
mgnr = await ViewModel.UpdateMemberHistory(ViewModel.SelectedHistoryEntry?.HistNr, IsEditing || ViewModel.DateNotice >= DateOnly.FromDateTime(Utils.Today).AddDays(-14));
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
}
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
FinishInputFilling();
await EnsureContextRenewed();
Mouse.OverrideCursor = null;
if (mgnr is int m)
FocusMemberHistory(m);
}
protected override void ShortcutReset() {
if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
return;
ResetButton_Click(null, null);
}
private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
ClearInputs();
await InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
RefreshInputs();
LockInputs();
}
private void ShowSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
ViewModel.EditingButtonsVisibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
ViewModel.EditingButtonsVisibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = true;
EditHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
DeleteHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
ViewModel.ControlButtonsVisibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = false;
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ViewModel.ControlButtonsVisibility = Visibility.Hidden;
}
private void OtherMgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
var text = OtherMgNrInput.Text;
var caret = OtherMgNrInput.CaretIndex;
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
OtherMgNrInput.Text = text;
OtherMgNrInput.CaretIndex = caret;
}
private void OtherMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
}
private void OtherMemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = OtherMemberInput.SelectedItem as Member;
ViewModel.OtherMgNr = m?.MgNr;
}
private void OtherMemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (OtherMemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
private void ReasonInput_SelectionChanged(object sender, RoutedEventArgs evt) {
ComboBox_SelectionChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
if (!DeductInput.IsEnabled) DeductInput.IsChecked = false;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
if (!OtherMgNrInput.IsEnabled) OtherMgNrInput.Text = "";
}
private void DateNoticeInput_TextChanged(object sender, TextChangedEventArgs evt) {
DateInput_TextChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
ViewModel.DeductYear = d.Year;
if (ViewModel.DateEffective == null && ViewModel.Reason != null) {
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
}
}
private void UpdateAmount() {
ViewModel.TotalAmount = $"{ViewModel.Shares * ViewModel.ValuePerShare:N2}";
}
private void SharesInput_TextChanged(object sender, TextChangedEventArgs evt) {
IntegerInput_TextChanged(sender, evt);
UpdateAmount();
}
private void AmountPerShareInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 3, 2));
UpdateAmount();
}
}
}