Compare commits

...
4 Commits
Author SHA1 Message Date
lorenz.stechauner f0de16ede3 [WIP] Bump version to 1.2.0.0
Test / Run tests (push) Successful in 2m9s
2026-10-02 14:58:34 +02:00
lorenz.stechauner fcdbc8ce17 Elwig: Update dependencies
Test / Run tests (push) Successful in 2m13s
2026-10-02 14:57:47 +02:00
lorenz.stechauner fa19d4dcb5 AppDbUpdater: Fix update to v42
Test / Run tests (push) Successful in 2m31s
2026-10-02 14:54:02 +02:00
lorenz.stechauner e370f949f6 MemberAdminWindow: Add pending member movement export 2026-10-02 14:53:59 +02:00
19 changed files with 395 additions and 110 deletions

No files matched your search

+36
View File
@@ -2,6 +2,42 @@
Changelog Changelog
========= =========
[v1.2.0.0][v1.2.0.0] (2026-09-30) {#v1.2.0.0}
---------------------------------------------
### Neue Funktionen {#v1.2.0.0-features}
- Bio. ([#67][i67], d592fef352)
- Rot/weiß bei Menge/Gradataion. (7fef895491)
- CompanyHasSigned in member_history. (83dfde9203)
- Straßennamen in Adressen werden normalisiert. (fda337c3cb, fa19d4dcb5)
- Ausstehende Mitgl.-Bewegungen. (e370f949f6)
### Behobene Fehler {#v1.2.0.0-bugfixes}
- Backup wiederherstellen nicht src-file löschen. (15e191d74e)
- Billing Address FullName nur wenn addresse auch. (d8126e254d)
- ShutdownGracefully. (155a599878)
- SysTec-IT Waage Fehlermeldung typo. (03872ca5c2)
- Es ist nun nicht mehr möglich Lieferungen mit negativem Gewicht zu speichern. (0e586cc438)
### Sonstiges {#v1.2.0.0-misc}
- Tara korrigieren: wenn keine Tara, dann änderbar. (0e9121c462)
- Kein right-padding auf Lieferscheinen (`DeliveryNote`). (f2b84053e6)
- Backup only in non-Debug. (cba949d2eb)
- PostalDest/Ort.Name. (66a1185dc3)
- Rot/weiß in DeliverySplittingDialog. (829ad106cf)
- Code cleanup. (c2bc5b221a, b886abc632, 5f2788eb9e, 3d96c31a70, db8afa59b7, ba7c37fd70)
- Tests hinzugefügt (`PaymentVariantServiceTest`). (4988081bfd)
- Abhängigkeiten aktualisiert. (c54a998c96, aea7c39734, fcdbc8ce17)
[v1.2.0.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.2.0.0
[i67]: https://git.necronda.net/winzer/elwig/issues/67
[v1.1.2.2][v1.1.2.2] (2026-09-14) {#v1.1.2.2} [v1.1.2.2][v1.1.2.2] (2026-09-14) {#v1.1.2.2}
--------------------------------------------- ---------------------------------------------
+6 -6
View File
@@ -274,11 +274,11 @@ namespace Elwig.Documents {
.AddCells(NewBusinessSharesCells(new())); .AddCells(NewBusinessSharesCells(new()));
} }
var recorded = history.Aggregate(new MemberHistoryPoint(), (s, h) => new( var recorded = history.Aggregate(new MemberHistoryPoint(), (s, h) => new(
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.Shares + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Normal ? h.Shares : 0) - (h.ToMgNr == h.FromMgNr && h.ToType == BusinessShareType.Normal && h.FromType == BusinessShareType.Normal ? 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.SharesRed + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Red ? h.Shares : 0) - (h.ToMgNr == h.FromMgNr && h.ToType == BusinessShareType.Red && h.FromType == BusinessShareType.Red ? 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.SharesWhite + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.White ? h.Shares : 0) - (h.ToMgNr == h.FromMgNr && h.ToType == BusinessShareType.White && h.FromType == BusinessShareType.White ? 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))); s.SharesDormant + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Dormant ? h.Shares : 0) - (h.ToMgNr == h.FromMgNr && h.ToType == BusinessShareType.Dormant && h.FromType == BusinessShareType.Dormant ? 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); 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) { if (cur.Shares != 0 || cur.SharesRed != 0 || cur.SharesWhite != 0 || cur.SharesDormant != 0) {
@@ -297,7 +297,7 @@ namespace Elwig.Documents {
DateOnly? lastDate = null; DateOnly? lastDate = null;
foreach (var row in history) { foreach (var row in history) {
int s = 0, r = 0, w = 0, d = 0; int s = 0, r = 0, w = 0, d = 0;
if (row.FromMgNr == Member.MgNr) { if (row.FromMgNr == Member.MgNr && !(row.FromMgNr == row.ToMgNr && row.FromType == row.ToType)) {
switch (row.FromType) { switch (row.FromType) {
case BusinessShareType.Normal: s -= row.Shares; break; case BusinessShareType.Normal: s -= row.Shares; break;
case BusinessShareType.Red: r -= row.Shares; break; case BusinessShareType.Red: r -= row.Shares; break;
+3 -3
View File
@@ -9,7 +9,7 @@
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.1.2.2</Version> <Version>1.2.0.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>App.manifest</ApplicationManifest> <ApplicationManifest>App.manifest</ApplicationManifest>
@@ -25,8 +25,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="bblanchon.PDFium.Win32" Version="156.0.8076" /> <PackageReference Include="bblanchon.PDFium.Win32" Version="156.0.8076" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="itext" Version="9.7.0" /> <PackageReference Include="itext" Version="9.8.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" /> <PackageReference Include="itext.bouncy-castle-adapter" Version="9.8.0" />
<PackageReference Include="LinqKit" Version="1.3.11" /> <PackageReference Include="LinqKit" Version="1.3.11" />
<PackageReference Include="MailKit" Version="4.18.1" /> <PackageReference Include="MailKit" Version="4.18.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.12" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.12" />
+39
View File
@@ -1,3 +1,4 @@
using Elwig.Models;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Elwig.Services; using Elwig.Services;
@@ -531,6 +532,44 @@ namespace Elwig.Helpers {
return _memberHistory[year].GetValueOrDefault(mgnr, new()); return _memberHistory[year].GetValueOrDefault(mgnr, new());
} }
public async Task<Dictionary<int, SortedDictionary<DateOnly, MemberHistoryPoint>>> GetMemberHistory() {
var today = DateOnly.FromDateTime(DateTime.Today).AddDays(1);
var history = new Dictionary<int, SortedDictionary<DateOnly, MemberHistoryPoint>>();
var members = await Members.ToListAsync();
foreach (var m in members) {
history[m.MgNr] = new() {
[today] = new(m.Shares, m.SharesRed, m.SharesWhite, m.SharesDormant),
};
}
var hist = await MemberHistory.OrderByDescending(h => h.DateNoticeString).ToListAsync();
foreach (var h in hist) {
if (h.FromMgNr is int from) {
var latest = history[from].Last().Value;
switch (h.FromType) {
case BusinessShareType.Normal: latest.Shares += h.Shares; break;
case BusinessShareType.Red: latest.SharesRed += h.Shares; break;
case BusinessShareType.White: latest.SharesWhite += h.Shares; break;
case BusinessShareType.Dormant: latest.SharesDormant += h.Shares; break;
}
history[from][h.DateNotice] = latest;
}
if (h.ToMgNr is int to) {
var latest = history[to].Last().Value;
switch (h.ToType) {
case BusinessShareType.Normal: latest.Shares -= h.Shares; break;
case BusinessShareType.Red: latest.SharesRed -= h.Shares; break;
case BusinessShareType.White: latest.SharesWhite -= h.Shares; break;
case BusinessShareType.Dormant: latest.SharesDormant -= h.Shares; break;
}
history[to][h.DateNotice] = latest;
}
}
return history;
}
public async Task<Dictionary<string, AreaCmtBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) { public async Task<Dictionary<string, AreaCmtBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberAreaCommitmentBuckets.ContainsKey(year)) if (!_memberAreaCommitmentBuckets.ContainsKey(year))
await FetchMemberAreaCommitmentBuckets(year, cnx); await FetchMemberAreaCommitmentBuckets(year, cnx);
-4
View File
@@ -46,10 +46,6 @@ namespace Elwig.Helpers.Export {
await ExportAsync(rows); await ExportAsync(rows);
} }
public void Export(int year) {
ExportAsync(year).GetAwaiter().GetResult();
}
public override string FormatRow(Row row) { public override string FormatRow(Row row) {
var (member, delivery) = row; var (member, delivery) = row;
var (lfBisNr, familyName, name, billingName, address, plz, ort, vollablieferer, area) = member; var (lfBisNr, familyName, name, billingName, address, plz, ort, vollablieferer, area) = member;
-9
View File
@@ -29,11 +29,6 @@ namespace Elwig.Helpers.Export {
_writer.Dispose(); _writer.Dispose();
} }
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) { public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) {
progress?.Report(0.0); progress?.Report(0.0);
int count = data.Count() + 2, i = 0; int count = data.Count() + 2, i = 0;
@@ -50,10 +45,6 @@ namespace Elwig.Helpers.Export {
progress?.Report(100.0); progress?.Report(100.0);
} }
public void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
ExportAsync(data, progress).GetAwaiter().GetResult();
}
public string FormatRow(IEnumerable row) { public string FormatRow(IEnumerable row) {
return string.Join(Separator, row); return string.Join(Separator, row);
} }
-9
View File
@@ -28,15 +28,6 @@ namespace Elwig.Helpers.Export {
Writer.Dispose(); Writer.Dispose();
} }
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return Writer.DisposeAsync();
}
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
ExportAsync(transactions, progress).GetAwaiter().GetResult();
}
public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) { public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
if (transactions.Any(tx => tx.Amount < 0)) { if (transactions.Any(tx => tx.Amount < 0)) {
throw new ArgumentException("Tranaction amount may not be negative"); throw new ArgumentException("Tranaction amount may not be negative");
+4 -2
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public interface IExporter<T> : IDisposable, IAsyncDisposable { public interface IExporter<T> : IDisposable {
/// <summary> /// <summary>
/// The default file extension of the exported files to be used (whithout a preceding ".") /// The default file extension of the exported files to be used (whithout a preceding ".")
/// </summary> /// </summary>
@@ -14,7 +14,9 @@ namespace Elwig.Helpers.Export {
/// </summary> /// </summary>
/// <param name="data">The data to be exported</param> /// <param name="data">The data to be exported</param>
/// <param name="progress">The progress object to report to</param> /// <param name="progress">The progress object to report to</param>
void Export(IEnumerable<T> data, IProgress<double>? progress = null); void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
ExportAsync(data, progress).GetAwaiter().GetResult();
}
/// <summary> /// <summary>
/// Asynchronosly export the given data to the given file. /// Asynchronosly export the given data to the given file.
+29
View File
@@ -0,0 +1,29 @@
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
public class MemberMovement : IExporter<MemberHistory> {
public static string FileExtension => "ods";
private readonly OdsFile _ods;
public MemberMovement(string filename) {
_ods = new OdsFile(filename);
}
public void Dispose() {
GC.SuppressFinalize(this);
_ods.Dispose();
}
public async Task ExportAsync(IEnumerable<MemberHistory> data, IProgress<double>? progress = null) {
using var ctx = new AppDbContext();
var tbl = MemberMovementData.FromMemberHistory("", data, await ctx.GetMemberHistory());
await _ods.AddTable(tbl);
}
}
}
+60 -30
View File
@@ -109,8 +109,13 @@ namespace Elwig.Helpers.Export {
<style:paragraph-properties fo:text-align="center"/> <style:paragraph-properties fo:text-align="center"/>
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/> <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style> </style:style>
<style:style style:name="sectionheader" style:family="table-cell" style:parent-style-name="default">
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/>
<style:paragraph-properties fo:text-align="left"/>
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:font-size="14pt"/>
</style:style>
<style:style style:name="th" style:family="table-cell" style:parent-style-name="default"> <style:style style:name="th" style:family="table-cell" style:parent-style-name="default">
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false" style:vertical-align="middle"/> <style:table-cell-properties style:text-align-source="fix" style:repeat-content="false" style:vertical-align="middle" fo:wrap-option="wrap"/>
<style:paragraph-properties fo:text-align="center"/> <style:paragraph-properties fo:text-align="center"/>
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/> <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style> </style:style>
@@ -211,35 +216,59 @@ namespace Elwig.Helpers.Export {
} }
} }
await Content.WriteAsync( await Content.WriteAsync($"""
$" <table:table-row>\r\n" + <table:table-row>
FormatCell(table.FullName, colSpan: totalSpan, style: "header") + {FormatCell(table.FullName, colSpan: totalSpan, style: "header")}
$" </table:table-row>\r\n" + </table:table-row>
$" <table:table-row>\r\n" + <table:table-row>
FormatCell(table.Subtitle, colSpan: totalSpan, style: "subheader") + {FormatCell(table.Subtitle, colSpan: totalSpan, style: "subheader")}
$" </table:table-row>\r\n" + </table:table-row>
$" <table:table-row>\r\n" + <table:table-row>
$" <table:table-cell table:number-columns-repeated=\"{totalSpan}\"/>\r\n" + <table:table-cell table:number-columns-repeated="{totalSpan}"/>
$" </table:table-row>\r\n" + </table:table-row>
$" <table:table-row>\r\n"); """);
foreach (var (name, span, units) in table.ColumnNames.Zip(table.ColumnSpans, table.ColumnUnits)) {
var hasUnits = units.Length > 0;
await Content.WriteAsync(FormatCell(name, colSpan: span, rowSpan: hasUnits ? 1 : 2, style: "th"));
}
await Content.WriteAsync(" </table:table-row>\r\n <table:table-row>\r\n");
foreach (var (span, units) in table.ColumnSpans.Zip(table.ColumnUnits)) {
if (units.Length == 0) {
await Content.WriteAsync($" <table:covered-table-cell table:number-columns-repeated=\"{span}\"/>\r\n");
continue;
}
foreach (var u in units) {
await Content.WriteAsync(FormatCell(u == null ? null : $"[{u}]", style: "th"));
}
}
await Content.WriteAsync(" </table:table-row>\r\n");
foreach (var row in table.GetData()) { var firstSection = true;
await FormatRow(row, table.ColumnUnits, mergeSubRowCells); foreach (var sec in table.GetData()) {
if (!firstSection) {
await Content.WriteAsync($"""
<table:table-row>
<table:table-cell table:number-columns-repeated="{totalSpan}"/>
</table:table-row>
<table:table-row>
<table:table-cell table:number-columns-repeated="{totalSpan}"/>
</table:table-row>
""");
}
if (sec.Name != null) {
await Content.WriteAsync($"""
<table:table-row>
{FormatCell(sec.Name, colSpan: totalSpan, style: "sectionheader")}
</table:table-row>
""");
}
await Content.WriteAsync(" <table:table-row>\r\n");
foreach (var (name, span, units) in table.ColumnNames.Zip(table.ColumnSpans, table.ColumnUnits)) {
var hasUnits = units.Length > 0;
await Content.WriteAsync(FormatCell(name, colSpan: span, rowSpan: hasUnits ? 1 : 2, style: "th"));
}
await Content.WriteAsync(" </table:table-row>\r\n <table:table-row>\r\n");
foreach (var (span, units) in table.ColumnSpans.Zip(table.ColumnUnits)) {
if (units.Length == 0) {
await Content.WriteAsync($" <table:covered-table-cell table:number-columns-repeated=\"{span}\"/>\r\n");
continue;
}
foreach (var u in units) {
await Content.WriteAsync(FormatCell(u == null ? null : $"[{u}]", style: "th"));
}
}
await Content.WriteAsync(" </table:table-row>\r\n");
foreach (var row in sec.Rows) {
await FormatRow(row, table.ColumnUnits, mergeSubRowCells);
}
firstSection = false;
} }
await Content.WriteAsync(" </table:table>\r\n"); await Content.WriteAsync(" </table:table>\r\n");
@@ -314,7 +343,8 @@ namespace Elwig.Helpers.Export {
} }
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>"; c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
} else { } else {
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>"; var lines = string.Join("", data.ToString()?.Split('\n').Select(l => $"<text:p>{SecurityElement.Escape(l)}</text:p>") ?? []);
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}>{lines}</{ct}>";
} }
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-columns-repeated=\"{colSpan - 1}\"/>\r\n" : ""); return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-columns-repeated=\"{colSpan - 1}\"/>\r\n" : "");
+3 -10
View File
@@ -13,7 +13,9 @@ namespace Elwig.Helpers.Export {
private readonly StreamWriter _writer; private readonly StreamWriter _writer;
public VCard(string filename) : this(filename, Utils.UTF8) { } public VCard(string filename) :
this(filename, Utils.UTF8) {
}
public VCard(string filename, Encoding encoding) { public VCard(string filename, Encoding encoding) {
_writer = new StreamWriter(filename, false, encoding); _writer = new StreamWriter(filename, false, encoding);
@@ -24,11 +26,6 @@ namespace Elwig.Helpers.Export {
_writer.Dispose(); _writer.Dispose();
} }
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public async Task ExportAsync(IEnumerable<Member> data, IProgress<double>? progress = null) { public async Task ExportAsync(IEnumerable<Member> data, IProgress<double>? progress = null) {
progress?.Report(0.0); progress?.Report(0.0);
int count = data.Count() + 1, i = 0; int count = data.Count() + 1, i = 0;
@@ -58,10 +55,6 @@ namespace Elwig.Helpers.Export {
progress?.Report(100.0); progress?.Report(100.0);
} }
public void Export(IEnumerable<Member> data, IProgress<double>? progress = null) {
ExportAsync(data, progress).GetAwaiter().GetResult();
}
private static string? Escape(string? text) { private static string? Escape(string? text) {
return text?.Replace("\\", "\\\\").Replace(",", "\\,").Replace(";", "\\;").Replace("\n", "\\n").Replace("\r", ""); return text?.Replace("\\", "\\\\").Replace(",", "\\,").Replace(";", "\\;").Replace("\n", "\\n").Replace("\r", "");
} }
+45 -27
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -9,13 +10,14 @@ namespace Elwig.Models.Dtos {
public string Name { get; set; } public string Name { get; set; }
public string FullName { get; set; } public string FullName { get; set; }
public string? Subtitle { get; set; } public string? Subtitle { get; set; }
public IEnumerable<T> Rows { get; private set; } public IEnumerable<(string? Name, IEnumerable<T> Rows)> Sections { get; private set; }
public IEnumerable<T> Rows => Sections.SelectMany(s => s.Rows);
public int RowNum => Rows.Count(); public int RowNum => Rows.Count();
public int ColNum => ColumnNames.Count(); public int ColNum => ColumnNames.Count();
public IEnumerable<(string, Type?)> ColumnDefs => _map.Select(m => (m.Item1, m.Item2?.PropertyType ?? m.Item3?.FieldType)); public IEnumerable<(string Name, Type? Type)> ColumnDefs => _map.Select(m => (m.Name, m.PInfo?.PropertyType ?? m.FInfo?.FieldType));
public IEnumerable<string> ColumnNames => ColumnDefs.Select(m => m.Item1); public IEnumerable<string> ColumnNames => ColumnDefs.Select(m => m.Name);
public IEnumerable<Type?> ColumnTypes => ColumnDefs.Select(m => m.Item2); public IEnumerable<Type?> ColumnTypes => ColumnDefs.Select(m => m.Type);
public IEnumerable<Type?> ColumnFlatTypes { get; private set; } public IEnumerable<Type?> ColumnFlatTypes { get; private set; }
public IEnumerable<int> ColumnSpans { get; private set; } public IEnumerable<int> ColumnSpans { get; private set; }
public IEnumerable<int?> ColumnWidths { get; private set; } public IEnumerable<int?> ColumnWidths { get; private set; }
@@ -23,21 +25,25 @@ namespace Elwig.Models.Dtos {
private readonly PropertyInfo[] _properties; private readonly PropertyInfo[] _properties;
private readonly FieldInfo[] _fields; private readonly FieldInfo[] _fields;
private readonly (string, PropertyInfo?, FieldInfo?)[] _map; private readonly (string Name, PropertyInfo? PInfo, FieldInfo? FInfo)[] _map;
public DataTable(string name, string fullName, string subtitle, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) : public DataTable(string name, string fullName, string subtitle, IEnumerable<(string? Name, IEnumerable<T> Rows)> sections, IEnumerable<(string Property, string Name, string? Unit, int? Width)>? colNames = null) :
this(name, fullName, rows, colNames) { this(name, fullName, sections, colNames) {
Subtitle = subtitle; Subtitle = subtitle;
} }
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) { public DataTable(string name, string fullName, string subtitle, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit, int? Width)>? colNames = null) :
this(name, fullName, subtitle, [(null, rows)], colNames) {
}
public DataTable(string name, string fullName, IEnumerable<(string? Name, IEnumerable<T> Rows)> sections, IEnumerable<(string Property, string Name, string? Unit, int? Width)>? colNames = null) {
_fields = typeof(T).GetFields(); _fields = typeof(T).GetFields();
_properties = typeof(T).GetProperties(); _properties = typeof(T).GetProperties();
colNames ??= [.. _properties.Select(p => p.Name).Union(_fields.Select(f => f.Name)).Select(i => (i, i, (string?)null, (int?)null))]; colNames ??= [.. _properties.Select(p => p.Name).Union(_fields.Select(f => f.Name)).Select(i => (i, i, (string?)null, (int?)null))];
_map = [.. colNames.Select(n => (n.Item2, _properties.FirstOrDefault(p => p?.Name == n.Item1, null), _fields.FirstOrDefault(f => f?.Name == n.Item1, null)))]; _map = [.. colNames.Select(n => (n.Name, _properties.FirstOrDefault(p => p?.Name == n.Property, null), _fields.FirstOrDefault(f => f?.Name == n.Property, null)))];
Name = name; Name = name;
FullName = fullName; FullName = fullName;
Rows = rows; Sections = sections;
ColumnFlatTypes = [.. ColumnTypes.SelectMany(type => { ColumnFlatTypes = [.. ColumnTypes.SelectMany(type => {
var elType = type?.GetElementType(); var elType = type?.GetElementType();
return type != null && type.IsValueType && type.Name.StartsWith("ValueTuple") ? type.GetFields().Select(f => (Type?)f.FieldType) : return type != null && type.IsValueType && type.Name.StartsWith("ValueTuple") ? type.GetFields().Select(f => (Type?)f.FieldType) :
@@ -50,44 +56,56 @@ namespace Elwig.Models.Dtos {
type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Length : type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Length :
1; 1;
})]; })];
ColumnWidths = [.. colNames.Select(c => c.Item4)]; ColumnWidths = [.. colNames.Select(c => c.Width)];
ColumnUnits = [.. colNames.Select(c => c.Item3?.Split("|").Select(p => p.Length == 0 ? null : p).ToArray() ?? [])]; ColumnUnits = [.. colNames.Select(c => c.Unit?.Split("|").Select(p => p.Length == 0 ? null : p).ToArray() ?? [])];
} }
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?)>? colNames = null) : public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit, int? Width)>? colNames = null) :
this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, c.Item3, (int?)null))) { this(name, fullName, [(null, rows)], colNames) {
} }
public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string, string, string?)>? colNames = null) : public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit)>? colNames = null) :
this(name, fullName, rows, colNames?.Select(c => (c.Property, c.Name, c.Unit, (int?)null))) {
}
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit, int Width)>? colNames = null) :
this(name, fullName, rows, colNames?.Select(c => (c.Property, c.Name, c.Unit, (int?)c.Width))) {
}
public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit, int? Width)>? colNames = null) :
this(name, name, rows, colNames) { this(name, name, rows, colNames) {
} }
public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) : public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit)>? colNames = null) :
this(name, name, rows, colNames) { this(name, name, rows, colNames) {
} }
public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string, string, string?, int)>? colNames = null) : public DataTable(string name, IEnumerable<T> rows, IEnumerable<(string Property, string Name, string? Unit, int Width)>? colNames = null) :
this(name, name, rows, colNames) { this(name, name, rows, colNames) {
} }
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?, int)>? colNames = null) : public DataTable(string name, string fullName, IEnumerable<(string? Name, IEnumerable<T> Rows)> sections, IEnumerable<(string Property, string Name, string? Unit)>? colNames = null) :
this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, c.Item3, (int?)c.Item4))) { this(name, fullName, sections, colNames?.Select(c => (c.Property, c.Name, c.Unit, (int?)null))) {
} }
protected IEnumerable<(string, object?)> GetNamedRowData(T row) { public DataTable(string name, string fullName, IEnumerable<(string? Name, IEnumerable<T> Rows)> sections, IEnumerable<(string Property, string Name, string? Unit, int Width)>? colNames = null) :
return _map.Select(i => (i.Item1, i.Item2?.GetValue(row) ?? i.Item3?.GetValue(row))); this(name, fullName, sections, colNames?.Select(c => (c.Property, c.Name, c.Unit, (int?)c.Width))) {
} }
protected IEnumerable<object?> GetRowData(T row) { protected IEnumerable<(string Name, object? Value)> GetNamedRowValues(T row) {
return GetNamedRowData(row).Select(i => i.Item2); return _map.Select(i => (i.Name, i.PInfo?.GetValue(row) ?? i.FInfo?.GetValue(row)));
} }
public IEnumerable<IEnumerable<object?>> GetData() { protected IEnumerable<object?> GetRowValues(T row) {
return Rows.Select(GetRowData); return GetNamedRowValues(row).Select(i => i.Value);
} }
public IEnumerable<IEnumerable<(string, object?)>> GetNamedData() { public IEnumerable<(string? Name, IEnumerable<IEnumerable<object?>> Rows)> GetData() {
return Rows.Select(GetNamedRowData); return Sections.Select(s => (s.Name, s.Rows.Select(GetRowValues)));
}
public IEnumerable<(string? Name, IEnumerable<IEnumerable<(string Name, object? Value)>> Rows)> GetNamedData() {
return Sections.Select(s => (s.Name, s.Rows.Select(GetNamedRowValues)));
} }
} }
} }
+132
View File
@@ -0,0 +1,132 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Models.Dtos {
public class MemberMovementData : DataTable<MemberMovementDataRow> {
private static readonly (string, string, string?, int)[] FieldNames = [
("Type", "\u2026 durch", null, 30),
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 25),
("Address", "Adresse", null, 50),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 50),
("Date", "Datum", null, 20),
("SharesOld", "GA\nalt", null, 10),
("SharesDiff", "GA\nDiff.", null, 10),
("SharesNew", "GA\nneu", null, 10),
("Costs", "Kosten", "€", 15),
];
private MemberMovementData(string name, IEnumerable<(string? Name, IEnumerable<MemberMovementDataRow> Rows)> sections) :
base("Mitgl.-Bewegungen", name, sections, FieldNames) {
}
public static MemberMovementData FromMemberHistory(string name, IEnumerable<MemberHistory> rows, Dictionary<int, SortedDictionary<DateOnly, MemberHistoryPoint>> history) {
(string? Name, List<MemberMovementDataRow> Rows)[] sections = [
("Beitritt", []),
("Nachzeichnung (GA)", []),
("Überschreibung", []),
("Kündigung (GA)", []),
("Austritt", []),
];
foreach (var h in rows.OrderBy(r => r.DateNotice).ThenBy(r => r.ToMgNr).ThenBy(r => r.FromMgNr)) {
int cat;
if (h.ToMgNr == h.FromMgNr) {
// Umwandlung
if (h.ToType == BusinessShareType.Dormant) {
// Kündigung (GA)
cat = 3;
} else if (h.FromType == h.ToType) {
// Überschreibung
cat = 2;
} else {
throw new NotImplementedException();
}
} else if (h.DateNotice <= h.ToMember?.EntryDate && h.DateNotice >= h.FromMember?.ExitDate) {
// Überschreibung
cat = 2;
} else if (h.DateNotice <= h.ToMember?.EntryDate) {
// Beitritt
cat = 0;
} else if (h.DateNotice >= h.FromMember?.ExitDate) {
// Austritt
cat = 4;
} else if (h.ToMember == null) {
// Auszahlung
continue;
} else {
// Nachzeichnung (GA)
cat = 1;
}
var a = h.FromMember ?? h.ToMember;
var b = (a != h.ToMember) ? h.ToMember : null;
if (a == null) throw new ArgumentException();
if (b != null && cat <= 1) {
(b, a) = (a, b);
}
var oldA = history[a.MgNr].GetValueOrDefault(h.DateNotice);
var d = new MemberMovementDataRow {
Type = [h.ReasonPretty],
MgNr = [a.MgNr],
Name1 = [a.Name],
Name2 = [a.AdministrativeName2],
Address = [a.Address],
Plz = [a.PostalDest.AtPlz!.Plz],
Locality = [a.PostalDest.AtPlz.Ort.Name],
Date = h.DateNotice,
SharesOld = [oldA.Shares + oldA.SharesRed + oldA.SharesWhite],
SharesDiff = [cat != 3 && a == h.ToMember ? h.Shares : -h.Shares],
Costs = [h.FromMember == null ? h.TotalValue : null],
};
d.SharesNew = [d.SharesOld[^1] + (h.FromMgNr == h.ToMgNr ? 0 : d.SharesDiff[^1])];
if (b != null) {
var oldB = history[b.MgNr].GetValueOrDefault(h.DateNotice);
d.Type = [..d.Type, "\u2026 " + (cat <= 1 ? "von" : "an")];
d.MgNr = [.. d.MgNr, b.MgNr];
d.Name1 = [.. d.Name1, b.Name];
d.Name2 = [.. d.Name2, b.AdministrativeName2];
d.Address = [.. d.Address, b.Address];
d.Plz = [.. d.Plz, b.PostalDest.AtPlz!.Plz];
d.Locality = [.. d.Locality, b.PostalDest.AtPlz.Ort.Name];
d.SharesOld = [.. d.SharesOld, oldB.Shares + oldB.SharesRed + oldB.SharesWhite];
d.SharesDiff = [.. d.SharesDiff, b == h.ToMember ? h.Shares : -h.Shares];
d.SharesNew = [.. d.SharesNew, d.SharesOld[^1] + d.SharesDiff[^1]];
}
sections[cat].Rows.Add(d);
}
return new MemberMovementData($"Mitglieder-Bewegungen {name}", sections.Select(s => (s.Name, (IEnumerable<MemberMovementDataRow>)s.Rows)));
}
}
public class MemberMovementDataRow {
public string[] Type = [];
public int[] MgNr = [];
public string[] Name1 = [];
public string?[] Name2 = [];
public string[] Address = [];
public int[] Plz = [];
public string[] Locality = [];
public DateOnly Date;
public int?[] SharesOld = [];
public int[] SharesDiff = [];
public int?[] SharesNew = [];
public decimal?[] Costs = [];
}
}
+3 -3
View File
@@ -104,10 +104,10 @@ namespace Elwig.Models.Entities {
public string FormatReasonPhrase(int mgnr) { public string FormatReasonPhrase(int mgnr) {
if (Reason == REASON_TRANSFER) { if (Reason == REASON_TRANSFER) {
if (FromMgNr == mgnr) { if (ToMgNr == 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} von {FromMember?.FullAdministrativeName ?? "Vorgänger"}" + (Comment != null ? $", {Comment}" : "");
} else if (FromMgNr == mgnr) {
return $"{ReasonPretty} an {ToMember?.FullAdministrativeName ?? "Nachfolger"}" + (Comment != null ? $", {Comment}" : "");
} }
} }
return ReasonPretty + (Comment != null ? $", {Comment}" : ""); return ReasonPretty + (Comment != null ? $", {Comment}" : "");
+2 -2
View File
@@ -52,8 +52,8 @@ UPDATE client_parameter SET param = 'CLIENT_ORGANIC_AUTHORITYCODE' WHERE param =
UPDATE member_telephone_number SET comment = NULL WHERE comment = ''; UPDATE member_telephone_number SET comment = NULL WHERE comment = '';
UPDATE member_email_address SET comment = NULL WHERE comment = ''; UPDATE member_email_address SET comment = NULL WHERE comment = '';
UPDATE member SET address = 'Untere Weißgerberstraße' WHERE address = 'U. Weißgasse Straße'; UPDATE member SET address = CONCAT('Untere Weißgerberstraße ', SUBSTR(address, 21)) WHERE address LIKE 'U. Weißgasse Straße %';
UPDATE member SET address = 'Edgar-Penzig-Franz-Straße' WHERE address = 'Edgar-Penzing-Franz-Straße'; UPDATE member SET address = CONCAT('Edgar-Penzig-Franz-Straße ', SUBSTR(address, 28)) WHERE address LIKE 'Edgar-Penzing-Franz-Straße %';
UPDATE member UPDATE member
SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest)) SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest))
@@ -1,4 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models; using Elwig.Models;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Elwig.ViewModels; using Elwig.ViewModels;
@@ -10,6 +11,10 @@ using System.Threading.Tasks;
namespace Elwig.Services { namespace Elwig.Services {
public static class MemberBusinessSharesService { public static class MemberBusinessSharesService {
public enum ExportSubject {
Pending,
};
public static async Task InitInputs(this MemberBusinessSharesViewModel vm) { public static async Task InitInputs(this MemberBusinessSharesViewModel vm) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
vm.DateNoticeString = $"{DateTime.Today:dd.MM.yyyy}"; vm.DateNoticeString = $"{DateTime.Today:dd.MM.yyyy}";
@@ -97,5 +102,18 @@ namespace Elwig.Services {
await tx.CommitAsync(); await tx.CommitAsync();
}); });
} }
public static async Task GenerateMemberMovements(ExportSubject subject, ExportMode mode) {
var today = DateTime.Today;
var filename = InteractionService.SaveFile($"Ausstehende Mitgl.-Bewegungen {today:dd.MM.yyyy}", $"MitglBewegungen_{today:yyyy-MM-dd}", "ods");
if (filename == null)
return;
await Utils.RunForeground(async () => {
using var ctx = new AppDbContext();
var hist = await ctx.MemberHistory.Where(h => !h.CompanyHasSigned).ToListAsync();
using var exporter = new MemberMovement(filename);
await exporter.ExportAsync(hist);
});
}
} }
} }
+7 -3
View File
@@ -110,7 +110,7 @@
Click="Menu_CreditNote_Email_Click"/> Click="Menu_CreditNote_Email_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Mitgliederliste" x:Name="Menu_List"> <MenuItem Header="Mitgliederliste" x:Name="Menu_List">
<MenuItem x:Name="Menu_List_SaveActive" Header="...mit Aktiven speichern (Excel)" <MenuItem x:Name="Menu_List_SaveActive" Header="...mit Aktiven speichern... (Excel)"
Click="Menu_List_SaveActive_Click" InputGestureText="Strg+L"/> Click="Menu_List_SaveActive_Click" InputGestureText="Strg+L"/>
<MenuItem x:Name="Menu_List_ShowActive" Header="...mit Aktiven anzeigen (PDF)" <MenuItem x:Name="Menu_List_ShowActive" Header="...mit Aktiven anzeigen (PDF)"
Click="Menu_List_ShowActive_Click" InputGestureText="Strg+O"/> Click="Menu_List_ShowActive_Click" InputGestureText="Strg+O"/>
@@ -119,7 +119,7 @@
<MenuItem x:Name="Menu_List_PrintActive" Header="...mit Aktiven drucken" <MenuItem x:Name="Menu_List_PrintActive" Header="...mit Aktiven drucken"
Click="Menu_List_PrintActive_Click" InputGestureText="Strg+Shift+O"/> Click="Menu_List_PrintActive_Click" InputGestureText="Strg+Shift+O"/>
<Separator/> <Separator/>
<MenuItem x:Name="Menu_List_SaveFilters" Header="...aus Filtern speichern (Excel)" <MenuItem x:Name="Menu_List_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_List_SaveFilters_Click"/> Click="Menu_List_SaveFilters_Click"/>
<MenuItem x:Name="Menu_List_ShowFilters" Header="...aus Filtern anzeigen (PDF)" <MenuItem x:Name="Menu_List_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
Click="Menu_List_ShowFilters_Click"/> Click="Menu_List_ShowFilters_Click"/>
@@ -128,7 +128,7 @@
<MenuItem x:Name="Menu_List_PrintFilters" Header="...aus Filtern drucken" <MenuItem x:Name="Menu_List_PrintFilters" Header="...aus Filtern drucken"
Click="Menu_List_PrintFilters_Click"/> Click="Menu_List_PrintFilters_Click"/>
<Separator/> <Separator/>
<MenuItem x:Name="Menu_List_SaveAll" Header="...mit allen speichern (Excel)" <MenuItem x:Name="Menu_List_SaveAll" Header="...mit allen speichern... (Excel)"
Click="Menu_List_SaveAll_Click"/> Click="Menu_List_SaveAll_Click"/>
<MenuItem x:Name="Menu_List_ShowAll" Header="...mit allen anzeigen (PDF)" <MenuItem x:Name="Menu_List_ShowAll" Header="...mit allen anzeigen (PDF)"
Click="Menu_List_ShowAll_Click"/> Click="Menu_List_ShowAll_Click"/>
@@ -147,6 +147,10 @@
IsCheckable="True" IsChecked="{Binding MemberListOrderByOrt, Mode=TwoWay}" IsCheckable="True" IsChecked="{Binding MemberListOrderByOrt, Mode=TwoWay}"
Click="Menu_List_Order_Click"/> Click="Menu_List_Order_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Geschäftsanteile" x:Name="Menu_BusinessShares">
<MenuItem x:Name="Menu_BusinessShares_SavePending" Header="Ausstehende Mitlg.-Bewegungen speichern... (Excel)"
Click="Menu_BusinessShares_SavePending_Click"/>
</MenuItem>
<MenuItem Header="Export"> <MenuItem Header="Export">
<MenuItem x:Name="Menu_Export_ExportSelected" Header="...von ausgewähltem Mitglied speichern..." IsEnabled="False" <MenuItem x:Name="Menu_Export_ExportSelected" Header="...von ausgewähltem Mitglied speichern..." IsEnabled="False"
Click="Menu_Export_ExportSelected_Click"/> Click="Menu_Export_ExportSelected_Click"/>
+6
View File
@@ -1,6 +1,7 @@
using Elwig.Dialogs; using Elwig.Dialogs;
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Elwig.Services; using Elwig.Services;
using Elwig.ViewModels; using Elwig.ViewModels;
@@ -114,6 +115,7 @@ namespace Elwig.Windows {
} }
if (!App.Client.EnableMemberHistory) { if (!App.Client.EnableMemberHistory) {
Menu_BusinessShares.Visibility = Visibility.Collapsed;
BusinessSharesButton.Visibility = Visibility.Hidden; BusinessSharesButton.Visibility = Visibility.Hidden;
} }
@@ -809,6 +811,10 @@ namespace Elwig.Windows {
await App.Client.UpdateValues(); await App.Client.UpdateValues();
} }
private async void Menu_BusinessShares_SavePending_Click(object sender, RoutedEventArgs evt) {
await MemberBusinessSharesService.GenerateMemberMovements(MemberBusinessSharesService.ExportSubject.Pending, ExportMode.SaveList);
}
private void FocusSearchInput(object sender, RoutedEventArgs evt) { private void FocusSearchInput(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) { if (!IsEditing && !IsCreating) {
SearchInput.Focus(); SearchInput.Focus();
+2 -2
View File
@@ -13,7 +13,7 @@ About
**Product:** Elwig **Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives **Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system **Type:** ERP system
**Version:** 1.1.2.2 ([Changelog](./CHANGELOG.md)) **Version:** 1.2.0.0 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig **Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig **Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung **Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System) **Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.1.2.2 ([Änderungsprotokoll](./CHANGELOG.md)) **Version:** 1.2.0.0 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig **Quellcode:** https://git.necronda.net/winzer/elwig