Compare commits

..

14 Commits

Author SHA1 Message Date
367c3ac357 Bump version to 0.10.0
All checks were successful
Test / Run tests (push) Successful in 2m8s
Deploy / Build and Deploy (push) Successful in 2m37s
2024-08-13 15:23:58 +02:00
4d89a17e80 Billing: Allow users to add custom member modifiers before VAT
All checks were successful
Test / Run tests (push) Successful in 2m8s
2024-08-13 14:43:12 +02:00
f52c11b91e PaymentVariantSummary: Subtract member modifiers from sum
All checks were successful
Test / Run tests (push) Successful in 2m20s
2024-08-12 15:44:04 +02:00
f48c6a02cb [#54] Member: Add IsJuridicalPerson
All checks were successful
Test / Run tests (push) Successful in 2m49s
2024-08-12 15:18:34 +02:00
025ff08d84 [#14] Documents: Add DeliveryAncmtList
All checks were successful
Test / Run tests (push) Successful in 2m35s
2024-08-10 15:45:37 +02:00
b091bd0ec3 [#14] Windows: Add DeliveryAncmtWindow
All checks were successful
Test / Run tests (push) Successful in 2m5s
2024-08-09 22:11:47 +02:00
804a17911c [#14] Windows: Add DeliveryScheduleAdminWindow
All checks were successful
Test / Run tests (push) Successful in 2m58s
2024-08-09 17:45:14 +02:00
170cfda37e Windows: Minor cleanups 2024-08-09 17:44:48 +02:00
2d737e2780 [#14] Models: Add DeliveryAncmt 2024-08-09 17:44:21 +02:00
2333077aa5 Billing: Include predecessors in Treuebonus for WGM
All checks were successful
Test / Run tests (push) Successful in 2m29s
2024-08-08 19:09:38 +02:00
9127cd3f03 MemberAdminWindow: Fix area commitment transfer for new members
All checks were successful
Test / Run tests (push) Successful in 2m5s
2024-08-08 17:35:58 +02:00
036a0dc978 App: Use Version class
All checks were successful
Test / Run tests (push) Successful in 2m28s
2024-08-02 20:48:53 +02:00
7749f6ab45 Bump version to 0.9.3
All checks were successful
Test / Run tests (push) Successful in 2m29s
Deploy / Build and Deploy (push) Successful in 2m38s
2024-08-02 12:22:52 +02:00
cf05a0c658 DeliveryJournalData: Add delivery and member branch to excel export
All checks were successful
Test / Run tests (push) Successful in 2m52s
2024-08-02 11:42:27 +02:00
74 changed files with 2951 additions and 227 deletions

View File

@ -3,6 +3,47 @@ Changelog
========= =========
[v0.10.0][v0.10.0] (2024-08-13) {#v0.10.0}
------------------------------------------
> [!NOTE]
> Mitglieder können ab dieser Version als juristische Person markiert werden.
> Es ist empfohlen, dies bei entsprechenden Mitglieder zu überprüfen und einzusetzen (z.B. Bezirksbauernkammer, Lagerhaus).
### Neue Funktionen {#v0.10.0-features}
* Traubenanmeldungen und Leseplanung sind ab jetzt verfügbar. ([#14][i14])
* Mitglieder können nun auch als juristische Person markiert werden. ([#54][i54])
* Im Auszahlungsvarianten-Fenster kann unter _Anpassen_ (`PaymentAdjustmentWindow`) nun auch ein Absoluter oder Relativer Zu-/Abschlag pro Mitglied festgelegt werden, der **vor der MwSt.** angewendet wird. (4d89a17e80)
### Behobene Fehler {#v0.10.0-bugfixes}
* Beim Erstellen eines neuen Mitgliedes war es nicht möglich die Flächenbindungen des Vorgängers zu übernehmen. (9127cd3f03)
### Sonstiges {#v0.10.0-misc}
* Für den Treuebonus der WG Matzen werden nun auch Lieferungen der Vorgänger miteinbezogen. (2333077aa5)
* In den Variantendaten einer Auszahlungsvariante (`PaymentVariantSummary`) werden Zu-/Abschläge pro Mitglied explizit angegeben, somit ist die Zwischensumme korrekt. (f52c11b91e)
[v0.10.0]: https://git.necronda.net/winzer/elwig/releases/tag/v0.10.0
[i14]: https://git.necronda.net/winzer/elwig/issues/14
[i54]: https://git.necronda.net/winzer/elwig/issues/54
[v0.9.3][v0.9.3] (2024-08-02) {#v0.9.3}
---------------------------------------
### Sonstiges {#v0.9.3-misc}
* Das Lieferjournal als Excel-Liste beinhaltet nun auch Liefer-Zweigstelle und Stamm-Zweigstelle des Liefernaten. (cf05a0c658)
[v0.9.3]: https://git.necronda.net/winzer/elwig/releases/tag/v0.9.3
[v0.9.2][v0.9.2] (2024-08-01) {#v0.9.2} [v0.9.2][v0.9.2] (2024-08-01) {#v0.9.2}
--------------------------------------- ---------------------------------------

View File

@ -34,18 +34,7 @@ namespace Elwig {
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static Config Config { get; private set; } = new(ConfigPath); public static Config Config { get; private set; } = new(ConfigPath);
public static int VersionMajor { get; private set; } public static Version Version { get; private set; } = new();
public static int VersionMinor { get; private set; }
public static int VersionPatch { get; private set; }
public static string Version {
get => $"{VersionMajor}.{VersionMinor}.{VersionPatch}";
private set {
var p = value.Split(".").Select(p => int.Parse(p.Trim())).ToArray();
VersionMajor = p.ElementAtOrDefault(0);
VersionMinor = p.ElementAtOrDefault(1);
VersionPatch = p.ElementAtOrDefault(2);
}
}
public static int BranchNum { get; private set; } public static int BranchNum { get; private set; }
public static string ZwstId { get; private set; } public static string ZwstId { get; private set; }
@ -110,7 +99,7 @@ namespace Elwig {
} }
protected override async void OnStartup(StartupEventArgs evt) { protected override async void OnStartup(StartupEventArgs evt) {
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0"; Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
try { try {
await AppDbUpdater.CheckDb(); await AppDbUpdater.CheckDb();
@ -231,7 +220,7 @@ namespace Elwig {
public static async Task CheckForUpdates(bool showAlert = false) { public static async Task CheckForUpdates(bool showAlert = false) {
if (Config.UpdateUrl == null) return; if (Config.UpdateUrl == null) return;
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl); var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) { if (latest != null && new Version(latest.Value.Version) > Version) {
await MainDispatcher.BeginInvoke(() => { await MainDispatcher.BeginInvoke(() => {
var d = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size); var d = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size);
if (d.ShowDialog() == true) { if (d.ShowDialog() == true) {
@ -303,6 +292,14 @@ namespace Elwig {
return w; return w;
} }
public static DeliveryAncmtAdminWindow FocusDeliveryAncmt() {
return FocusWindow<DeliveryAncmtAdminWindow>(() => new());
}
public static DeliveryScheduleAdminWindow FocusDeliverySchedule() {
return FocusWindow<DeliveryScheduleAdminWindow>(() => new());
}
public static PaymentVariantsWindow FocusPaymentVariants(int year) { public static PaymentVariantsWindow FocusPaymentVariants(int year) {
return FocusWindow<PaymentVariantsWindow>(() => new(year), w => w.Year == year); return FocusWindow<PaymentVariantsWindow>(() => new(year), w => w.Year == year);
} }

View File

@ -20,6 +20,12 @@ namespace Elwig.Controls {
set => SetValue(DelimiterProperty, value); set => SetValue(DelimiterProperty, value);
} }
public static readonly DependencyProperty ListDisplayMemberPathProperty = DependencyProperty.Register(nameof(ListDisplayMemberPath), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata(null));
public string ListDisplayMemberPath {
get => (string)GetValue(ListDisplayMemberPathProperty);
set => SetValue(ListDisplayMemberPathProperty, value);
}
public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register(nameof(AllItemsSelectedContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All")); public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register(nameof(AllItemsSelectedContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
public string AllItemsSelectedContent { public string AllItemsSelectedContent {
get => (string)GetValue(AllItemsSelectedContentProperty); get => (string)GetValue(AllItemsSelectedContentProperty);
@ -116,7 +122,7 @@ namespace Elwig.Controls {
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) { private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
SelectItemsReverse(); SelectItemsReverse();
var dmp = DisplayMemberPath != null && DisplayMemberPath != "" ? DisplayMemberPath : null; var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) { if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
_textBox.Text = AllItemsSelectedContent; _textBox.Text = AllItemsSelectedContent;
AllItemsSelected = true; AllItemsSelected = true;

View File

@ -32,7 +32,7 @@ namespace Elwig.Documents {
get { get {
IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member; IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member;
var plz = addr.PostalDest.AtPlz; var plz = addr.PostalDest.AtPlz;
return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}"; return string.Join("\n", ((string?[])[Member.BillingAddress?.FullName, Member.AdministrativeName, Member.ForTheAttentionOf, addr.Address, $"{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}", addr.PostalDest.Country.Name]).Where(s => !string.IsNullOrWhiteSpace(s)));
} }
} }

View File

@ -33,15 +33,21 @@ namespace Elwig.Documents {
bool considerCustomModifiers, bool considerCustomModifiers,
Dictionary<string, UnderDelivery>? underDeliveries = null Dictionary<string, UnderDelivery>? underDeliveries = null
) : ) :
base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.Name)} {p.Variant.Name}", p.Member) { base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.FullName)} {p.Variant.Name}", p.Member) {
UseBillingAddress = true; UseBillingAddress = true;
ShowDateAndLocation = true; ShowDateAndLocation = true;
Data = data; Data = data;
Payment = p; Payment = p;
Credit = p.Credit; Credit = p.Credit;
var season = p.Variant.Season; var season = p.Variant.Season;
if (considerCustomModifiers) {
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
}
var mod = App.Client.IsMatzen ? ctx.Modifiers.Where(m => m.Year == season.Year && m.Name.StartsWith("Treue")).FirstOrDefault() : null; var mod = App.Client.IsMatzen ? ctx.Modifiers.Where(m => m.Year == season.Year && m.Name.StartsWith("Treue")).FirstOrDefault() : null;
if (mod != null) { if (CustomPayment?.ModComment != null) {
MemberModifier = CustomPayment.ModComment;
} else if (mod != null) {
MemberModifier = $"{mod.Name} ({mod.ValueStr})"; MemberModifier = $"{mod.Name} ({mod.ValueStr})";
} else { } else {
MemberModifier = "Sonstige Zu-/Abschläge"; MemberModifier = "Sonstige Zu-/Abschläge";
@ -88,8 +94,5 @@ namespace Elwig.Documents {
.Where(u => u.Item3 != 0) .Where(u => u.Item3 != 0)
.ToList(); .ToList();
} }
if (considerCustomModifiers) {
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
}
} }
}} }}

View File

@ -157,9 +157,9 @@
@Raw(FormatRow($"Autom. Nachz. von GA ({Model.MemberAutoBusinessShares})", Model.MemberAutoBusinessSharesAmount, add: true)); @Raw(FormatRow($"Autom. Nachz. von GA ({Model.MemberAutoBusinessShares})", Model.MemberAutoBusinessSharesAmount, add: true));
penalty += Model.MemberAutoBusinessSharesAmount; penalty += Model.MemberAutoBusinessSharesAmount;
} }
@if (Model.CustomPayment != null) { @if (Model.CustomPayment?.Amount != null) {
@Raw(FormatRow(Model.CustomPayment.Comment ?? (Model.CustomPayment.Amount < 0 ? "Weitere Abzüge" : "Weitere Zuschläge"), Model.CustomPayment.Amount, add: true)); @Raw(FormatRow(Model.CustomPayment.Comment ?? ((Model.CustomPayment.Amount.Value) < 0 ? "Weitere Abzüge" : "Weitere Zuschläge"), Model.CustomPayment.Amount.Value, add: true));
penalty += Model.CustomPayment.Amount; penalty += Model.CustomPayment.Amount.Value;
} }
@if (Model.Credit == null) { @if (Model.Credit == null) {

View File

@ -0,0 +1,21 @@
using Elwig.Models.Dtos;
using System.Collections.Generic;
namespace Elwig.Documents {
public class DeliveryAncmtList : Document {
public new static string Name => "Anmeldeliste";
public string Filter;
public IEnumerable<DeliveryAncmtListRow> Announcements;
public DeliveryAncmtList(string filter, IEnumerable<DeliveryAncmtListRow> announcements) : base($"{Name} {filter}") {
Filter = filter;
Announcements = announcements;
}
public DeliveryAncmtList(string filter, DeliveryAncmtListData data) :
this(filter, data.Rows) {
}
}
}

View File

@ -0,0 +1,46 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.DeliveryAncmtList>
@model Elwig.Documents.DeliveryAncmtList
@{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryAncmtList.css" />
<main>
<h1>Anmeldeliste</h1>
<h2>@Model.Filter</h2>
<table class="announcement-list">
<colgroup>
<col style="width: 18mm;"/>
<col style="width: 12mm;"/>
<col style="width: 81mm;"/>
<col style="width: 40mm;"/>
<col style="width: 14mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="2">Datum</th>
<th rowspan="2">MgNr.</th>
<th rowspan="2" style="text-align: left;">Mitglied</th>
<th rowspan="2" style="text-align: left;">Sorte</th>
<th>Gewicht</th>
</tr>
<tr>
<th class="unit">[kg]</th>
</tr>
</thead>
<tbody>
@foreach (var a in Model.Announcements) {
<tr>
<td>@($"{a.Date:dd.MM.yyyy}")</td>
<td class="number">@a.MgNr</td>
<td>@a.AdministrativeName</td>
<td>@a.Variety</td>
<td class="number">@($"{a.Weight:N0}")</td>
</tr>
}
<tr class="sum bold">
<td colspan="2">Gesamt:</td>
<td colspan="2">Anmeldungen: @($"{Model.Announcements.Count():N0}")</td>
<td class="number">@($"{Model.Announcements.Sum(a => a.Weight):N0}")</td>
</tr>
</tbody>
</table>
</main>

View File

@ -0,0 +1,13 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-bottom: 2mm;
}
h2 {
text-align: center;
font-size: 14pt;
margin-top: 2mm;
}

View File

@ -90,6 +90,8 @@ namespace Elwig.Documents {
name = "WineQualityStatistics"; name = "WineQualityStatistics";
} else if (this is PaymentVariantSummary) { } else if (this is PaymentVariantSummary) {
name = "PaymentVariantSummary"; name = "PaymentVariantSummary";
} else if (this is DeliveryAncmtList) {
name = "DeliveryAncmtList";
} else { } else {
throw new InvalidOperationException("Invalid document object"); throw new InvalidOperationException("Invalid document object");
} }
@ -156,7 +158,7 @@ namespace Elwig.Documents {
public void Show() { public void Show() {
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : "")); Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.FullName}" : ""));
} }
public MimePart AsEmailAttachment(string filename) { public MimePart AsEmailAttachment(string filename) {

View File

@ -2,7 +2,7 @@ using Elwig.Models.Entities;
namespace Elwig.Documents { namespace Elwig.Documents {
public class Letterhead : BusinessDocument { public class Letterhead : BusinessDocument {
public Letterhead(Member m) : base($"Briefkopf {m.Name}", m, true) { public Letterhead(Member m) : base($"Briefkopf {m.FullName}", m, true) {
Aside = ""; Aside = "";
} }
} }

View File

@ -18,21 +18,31 @@
<tbody> <tbody>
<tr class="sectionheading"><th colspan="6">Persönliche Daten</th></tr> <tr class="sectionheading"><th colspan="6">Persönliche Daten</th></tr>
<tr> <tr>
<th class="small">Titel (vorangestellt)</th> @if (Model.Member.IsJuridicalPerson) {
<th class="small">Vorname</th> <th colspan="3" class="small">Name</th>
<th colspan="3" class="small">Nachname</th> <th colspan="3" class="small">Zu Handen</th>
<th class="small">Titel (nachgestellt)</th> } else {
<th class="small">Titel (vorangestellt)</th>
<th class="small">Vorname</th>
<th colspan="3" class="small">Nachname</th>
<th class="small">Titel (nachgestellt)</th>
}
</tr> </tr>
<tr> <tr>
<td class="large">@Model.Member.Prefix</td> @if (Model.Member.IsJuridicalPerson) {
<td class="large">@Model.Member.GivenName @Model.Member.MiddleName</td> <td colspan="3" class="large">@Model.Member.Name</td>
<td class="large" colspan="3">@Model.Member.FamilyName</td> <td colspan="3" class="large">@Model.Member.ForTheAttentionOf</td>
<td class="large">@Model.Member.Suffix</td> } else {
<td class="large">@Model.Member.Prefix</td>
<td class="large">@Model.Member.GivenName @Model.Member.MiddleName</td>
<td class="large" colspan="3">@Model.Member.Name</td>
<td class="large">@Model.Member.Suffix</td>
}
</tr> </tr>
<tr> <tr>
<th>Mitglieds-Nr.:</th> <th>Mitglieds-Nr.:</th>
<td>@Model.Member.MgNr</td> <td>@Model.Member.MgNr</td>
<th colspan="2">Geburtsjahr/-tag:</th> <th colspan="2">@(Model.Member.IsJuridicalPerson ? "Gründungsjahr/-tag" : "Geburtsjahr/-tag"):</th>
<td colspan="2">@(string.Join('.', Model.Member.Birthday?.Split('-')?.Reverse() ?? Array.Empty<string>()))</td> <td colspan="2">@(string.Join('.', Model.Member.Birthday?.Split('-')?.Reverse() ?? Array.Empty<string>()))</td>
</tr> </tr>
<tr> <tr>
@ -50,7 +60,7 @@
<tr class="sectionheading"><th colspan="6">Rechnungsadresse (optional)</th></tr> <tr class="sectionheading"><th colspan="6">Rechnungsadresse (optional)</th></tr>
<tr> <tr>
<th>Name:</th> <th>Name:</th>
<td colspan="5">@Model.Member.BillingAddress?.Name</td> <td colspan="5">@Model.Member.BillingAddress?.FullName</td>
</tr> </tr>
<tr> <tr>
<th>Adresse:</th> <th>Adresse:</th>

View File

@ -20,9 +20,10 @@
@{ @{
//var sum1 = Model.Variant.DeliveryPartPayments.Sum(p => p.NetAmount); //var sum1 = Model.Variant.DeliveryPartPayments.Sum(p => p.NetAmount);
//var sum2 = Model.Variant.Credits.Sum(p => p.); //Model.Variant.MemberPayments.Sum(p => p.Amount); //var sum2 = Model.Variant.Credits.Sum(p => p.); //Model.Variant.MemberPayments.Sum(p => p.Amount);
var modifiers = Model.Variant.DeliveryPartPayments.Sum(p => p.Amount - p.NetAmount); var deliveryModifiers = Model.Variant.DeliveryPartPayments.Sum(p => p.Amount - p.NetAmount);
var memberModifiers = Model.Variant.Credits.Sum(c => c.Payment.Amount - c.Payment.NetAmount);
var sum2 = Model.Variant.Credits.Sum(p => p.NetAmount); var sum2 = Model.Variant.Credits.Sum(p => p.NetAmount);
var sum1 = sum2 - modifiers; var sum1 = sum2 - deliveryModifiers - memberModifiers;
var payed = -Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m); var payed = -Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m);
var netSum = Model.Variant.Credits.Sum(p => p.NetAmount) - Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m); var netSum = Model.Variant.Credits.Sum(p => p.NetAmount) - Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m);
var vat = Model.Variant.Credits.Sum(p => p.VatAmount); var vat = Model.Variant.Credits.Sum(p => p.VatAmount);
@ -49,7 +50,7 @@
<td class="center">@(Model.BillingData.ConsiderContractPenalties ? "Ja" : "Nein")</td> <td class="center">@(Model.BillingData.ConsiderContractPenalties ? "Ja" : "Nein")</td>
</tr> </tr>
<tr> <tr>
<th style="overflow: visible;">Nto./bto.-Zuschl:</th> <th style="overflow: visible;">Rebel-Zuschl.:</th>
<td colspan="3" class="center"> <td colspan="3" class="center">
@($"{Utils.GetSign(Model.BillingData.NetWeightModifier)}{Math.Abs(Model.BillingData.NetWeightModifier) * 100:N2}") % / @($"{Utils.GetSign(Model.BillingData.NetWeightModifier)}{Math.Abs(Model.BillingData.NetWeightModifier) * 100:N2}") % /
@($"{Utils.GetSign(Model.BillingData.GrossWeightModifier)}{Math.Abs(Model.BillingData.GrossWeightModifier) * 100:N2}") % @($"{Utils.GetSign(Model.BillingData.GrossWeightModifier)}{Math.Abs(Model.BillingData.GrossWeightModifier) * 100:N2}") %
@ -84,30 +85,30 @@
<td colspan="2" class="number">@($"{Model.MemberNum:N0}")</td> <td colspan="2" class="number">@($"{Model.MemberNum:N0}")</td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Zu-/Abschläge (Lieferungen):</th> <th colspan="2">Zu-/Abschläge (Mitglieder):</th>
<td class="number">@Utils.GetSign(modifiers)</td> <td class="number">@Utils.GetSign(memberModifiers)</td>
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(modifiers):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(memberModifiers):N2}")</td>
<th class="lborder">Lieferungen:</th> <th class="lborder">Lieferungen:</th>
<td colspan="2" class="number">@($"{Model.DeliveryNum:N0}")</td> <td colspan="2" class="number">@($"{Model.DeliveryNum:N0}")</td>
</tr> </tr>
<tr>
<th colspan="2">Zu-/Abschläge (Lieferungen):</th>
<td class="number">@Utils.GetSign(deliveryModifiers)</td>
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(deliveryModifiers):N2}")</td>
<th class="lborder">Teillieferungen:</th>
<td colspan="2" class="number">@($"{Model.DeliveryPartNum:N0}")</td>
</tr>
<tr> <tr>
<th colspan="2">Gesamtsumme:</th> <th colspan="2">Gesamtsumme:</th>
<td class="number tborder"></td> <td class="number tborder"></td>
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{sum2:N2}")</td> <td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{sum2:N2}")</td>
<th class="lborder">Teillieferungen:</th> <th class="lborder"></th>
<td colspan="2" class="number">@($"{Model.DeliveryPartNum:N0}")</td> <td colspan="2"></td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Bisher ausgezahlt:</th> <th colspan="2">Bisher ausgezahlt:</th>
<td class="number">@Utils.GetSign(payed)</td> <td class="number">@Utils.GetSign(payed)</td>
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
<th class="lborder"></th>
<td colspan="2"></td>
</tr>
<tr>
<th colspan="2">Nettosumme:</th>
<td class="number tborder"></td>
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
@{ @{
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein"); var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
var minWei = weiRows.Min(r => r.Ungeb.Price); var minWei = weiRows.Min(r => r.Ungeb.Price);
@ -117,9 +118,9 @@
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Mehrwertsteuer:</th> <th colspan="2">Nettosumme:</th>
<td class="number">@Utils.GetSign(vat)</td> <td class="number tborder"></td>
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td> <td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
@{ @{
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein"); var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
var minPrice = quwRows.Min(r => r.Ungeb.Price); var minPrice = quwRows.Min(r => r.Ungeb.Price);
@ -129,9 +130,9 @@
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Bruttosumme:</th> <th colspan="2">Mehrwertsteuer:</th>
<td class="number tborder"></td> <td class="number">@Utils.GetSign(vat)</td>
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{grossSum:N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
@{ @{
var gebRows = Model.Data.Rows var gebRows = Model.Data.Rows
.Where(r => r.Geb.Price != null && r.Ungeb.Price != null) .Where(r => r.Geb.Price != null && r.Ungeb.Price != null)
@ -144,6 +145,13 @@
@(minGeb != maxGeb ? $"{minGeb:N4}{maxGeb:N4} {Model.CurrencySymbol}/kg" : minGeb == 0 ? "-" : $"{minGeb:N4} {Model.CurrencySymbol}/kg") @(minGeb != maxGeb ? $"{minGeb:N4}{maxGeb:N4} {Model.CurrencySymbol}/kg" : minGeb == 0 ? "-" : $"{minGeb:N4} {Model.CurrencySymbol}/kg")
</td> </td>
</tr> </tr>
<tr>
<th colspan="2">Bruttosumme:</th>
<td class="number tborder"></td>
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{grossSum:N2}")</td>
<th class="lborder"></th>
<td colspan="2"></td>
</tr>
<tr> <tr>
<th colspan="2">Abzüge (Strafen/Pönalen, GA, ...):</th> <th colspan="2">Abzüge (Strafen/Pönalen, GA, ...):</th>
<td class="number">@Utils.GetSign(totalMods)</td> <td class="number">@Utils.GetSign(totalMods)</td>

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.9.2</Version> <Version>0.10.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>

View File

@ -51,6 +51,9 @@ namespace Elwig.Helpers {
public DbSet<MemberHistory> MemberHistory { get; private set; } public DbSet<MemberHistory> MemberHistory { get; private set; }
public DbSet<AreaCom> AreaCommitments { get; private set; } public DbSet<AreaCom> AreaCommitments { get; private set; }
public DbSet<Season> Seasons { get; private set; } public DbSet<Season> Seasons { get; private set; }
public DbSet<DeliverySchedule> DeliverySchedules { get; private set; }
public DbSet<DeliveryScheduleWineVar> DeliveryScheduleWineVarieties { get; private set; }
public DbSet<DeliveryAncmt> DeliveryAnnouncements { get; private set; }
public DbSet<Modifier> Modifiers { get; private set; } public DbSet<Modifier> Modifiers { get; private set; }
public DbSet<Delivery> Deliveries { get; private set; } public DbSet<Delivery> Deliveries { get; private set; }
public DbSet<DeliveryPart> DeliveryParts { get; private set; } public DbSet<DeliveryPart> DeliveryParts { get; private set; }
@ -236,6 +239,13 @@ namespace Elwig.Helpers {
return c + 1; return c + 1;
} }
public async Task<int> NextDsNr(int year) {
int c = 0;
(await DeliverySchedules.Where(s => s.Year == year).Select(s => s.DsNr).ToListAsync())
.ForEach(a => { if (a <= c + 100) c = a; });
return c + 1;
}
public async Task<WineQualLevel> GetWineQualityLevel(double kmw) { public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
return await WineQualityLevels return await WineQualityLevels
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw)) .Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
@ -266,6 +276,31 @@ namespace Elwig.Helpers {
} }
} }
public void UpdateDeliveryScheduleWineVarieties(DeliverySchedule schedule, IEnumerable<(WineVar, int)> oldVarieties, IEnumerable<(WineVar, int)> newVarieties) {
foreach (var v in WineVarieties) {
var e = new DeliveryScheduleWineVar {
Year = schedule.Year,
DsNr = schedule.DsNr,
SortId = v.SortId,
Priority = 1,
};
var o = oldVarieties.Where(x => x.Item1.SortId == e.SortId).Select(x => x.Item2).FirstOrDefault(-1);
var n = newVarieties.Where(x => x.Item1.SortId == e.SortId).Select(x => x.Item2).FirstOrDefault(-1);
if (n != -1) {
e.Priority = n;
if (o == -1) {
Add(e);
} else {
Update(e);
}
} else {
if (o != -1) {
Remove(e);
}
}
}
}
private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) { private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null; var ownCnx = cnx == null;
cnx ??= await ConnectAsync(); cnx ??= await ConnectAsync();

View File

@ -9,11 +9,11 @@ namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat! // Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 25; public static readonly int RequiredSchemaVersion = 28;
private static int VersionOffset = 0; private static int VersionOffset = 0;
public static async Task<string> CheckDb() { public static async Task<Version> CheckDb() {
using var cnx = AppDbContext.Connect(); using var cnx = AppDbContext.Connect();
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0; var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
@ -28,18 +28,14 @@ namespace Elwig.Helpers {
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion); await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0; var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
var major = userVers >> 24; var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)(userVers & 0xFFFF));
var minor = (userVers >> 16) & 0xFF;
var patch = userVers & 0xFFFF;
if (App.VersionMajor > major || if (App.Version > v) {
(App.VersionMajor == major && App.VersionMinor > minor) || long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | App.Version.Build;
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}"); await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}");
} }
return $"{major}.{minor}.{patch}"; return v;
} }
private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) { private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {

View File

@ -29,6 +29,8 @@ namespace Elwig.Helpers.Billing {
await CalculatePrices(cnx); await CalculatePrices(cnx);
if (Data.ConsiderDelieryModifiers) { if (Data.ConsiderDelieryModifiers) {
await CalculateDeliveryModifiers(cnx); await CalculateDeliveryModifiers(cnx);
}
if (Data.ConsiderCustomModifiers) {
await CalculateMemberModifiers(cnx); await CalculateMemberModifiers(cnx);
} }
await tx.CommitAsync(); await tx.CommitAsync();
@ -104,18 +106,21 @@ namespace Elwig.Helpers.Billing {
if (App.Client.IsMatzen) { if (App.Client.IsMatzen) {
var lastYears = 3; var lastYears = 3;
var multiplier = 0.50; var multiplier = 0.50;
var includePredecessor = true;
var modName = "Treue%"; var modName = "Treue%";
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT c.year, {AvNr}, s.mgnr, 0, SELECT c.year, {AvNr}, s.mgnr, 0,
ROUND(s.sum * COALESCE(m.abs, 0)), ROUND(s.sum * COALESCE(m.abs, 0)),
COALESCE(m.rel, 0) COALESCE(m.rel, 0)
FROM (SELECT {Year} AS year, mgnr, FROM (SELECT {Year} AS year, m.mgnr,
ROUND(AVG(sum) * {multiplier}) AS baseline, ROUND(AVG(COALESCE(a.sum, b.sum)) * {multiplier}) AS baseline,
COUNT(*) = {lastYears} AND MIN(sum) > 0 AS allowed COUNT(*) = {lastYears} AND MIN(COALESCE(a.sum, b.sum)) > 0 AS allowed
FROM v_stat_member FROM member m
WHERE year > {Year} - {lastYears} LEFT JOIN v_stat_member a ON a.mgnr = m.mgnr
GROUP BY mgnr FULL OUTER JOIN v_stat_member b ON b.mgnr = m.predecessor_mgnr AND b.year = a.year AND {(includePredecessor ? "TRUE" : "FALSE")}
WHERE a.year > {Year} - {lastYears}
GROUP BY m.mgnr
HAVING allowed) c HAVING allowed) c
JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr) JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr)
LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}' LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}'
@ -125,6 +130,16 @@ namespace Elwig.Helpers.Billing {
mod_rel = mod_rel + excluded.mod_rel mod_rel = mod_rel + excluded.mod_rel
"""); """);
} }
await AppDbContext.ExecuteBatch(cnx, $"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
FROM payment_custom x
JOIN season s ON s.year = x.year
WHERE x.year = {Year}
ON CONFLICT DO UPDATE
SET mod_abs = mod_abs + excluded.mod_abs,
mod_rel = mod_rel + excluded.mod_rel
""");
} }
protected async Task CalculatePrices(SqliteConnection cnx) { protected async Task CalculatePrices(SqliteConnection cnx) {

View File

@ -29,7 +29,7 @@ namespace Elwig.Helpers.Export {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand(); using var cmd = cnx.CreateCommand();
cmd.CommandText = $""" cmd.CommandText = $"""
SELECT lfbis_nr, family_name, name, billing_name, address, plz, ort, area, SELECT lfbis_nr, name, other_names, billing_name, address, plz, ort, area,
date, weight, type, sortid, qualid, year, hkid, kmw, oe date, weight, type, sortid, qualid, year, hkid, kmw, oe
FROM v_bki_delivery FROM v_bki_delivery
WHERE year = {year} WHERE year = {year}

View File

@ -83,7 +83,7 @@ namespace Elwig.Helpers.Export {
<PmtId><EndToEndId>{id}</EndToEndId></PmtId> <PmtId><EndToEndId>{id}</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt> <Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt>
<Cdtr> <Cdtr>
<Nm>{SecurityElement.Escape(a.Name[..Math.Min(140, a.Name.Length)])}</Nm> <Nm>{SecurityElement.Escape(a.FullName[..Math.Min(140, a.FullName.Length)])}</Nm>
"""); """);
if (ShowAddresses != AddressMode.Omit) { if (ShowAddresses != AddressMode.Omit) {
var full = ShowAddresses == AddressMode.Full; var full = ShowAddresses == AddressMode.Full;

View File

@ -401,11 +401,12 @@ namespace Elwig.Helpers.Export {
return new JsonObject { return new JsonObject {
["mgnr"] = m.MgNr, ["mgnr"] = m.MgNr,
["predecessor_mgnr"] = m.PredecessorMgNr, ["predecessor_mgnr"] = m.PredecessorMgNr,
["name"] = m.Name,
["prefix"] = m.Prefix, ["prefix"] = m.Prefix,
["given_name"] = m.GivenName, ["given_name"] = m.GivenName,
["middle_names"] = m.MiddleName, ["middle_names"] = m.MiddleName,
["family_name"] = m.FamilyName,
["suffix"] = m.Suffix, ["suffix"] = m.Suffix,
["attn"] = m.ForTheAttentionOf,
["birthday"] = m.Birthday, ["birthday"] = m.Birthday,
["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null, ["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null,
["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null, ["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null,
@ -414,6 +415,7 @@ namespace Elwig.Helpers.Export {
["zwstid"] = m.ZwstId, ["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr, ["lfbis_nr"] = m.LfbisNr,
["ustid_nr"] = m.UstIdNr, ["ustid_nr"] = m.UstIdNr,
["juridical_pers"] = m.IsJuridicalPerson,
["volllieferant"] = m.IsVollLieferant, ["volllieferant"] = m.IsVollLieferant,
["buchführend"] = m.IsBuchführend, ["buchführend"] = m.IsBuchführend,
["organic"] = m.IsOrganic, ["organic"] = m.IsOrganic,
@ -431,7 +433,7 @@ namespace Elwig.Helpers.Export {
["country"] = m.CountryNum, ["country"] = m.CountryNum,
}, },
["billing_address"] = m.BillingAddress != null ? new JsonObject { ["billing_address"] = m.BillingAddress != null ? new JsonObject {
["name"] = m.BillingAddress.Name, ["name"] = m.BillingAddress.FullName,
["address"] = m.BillingAddress.Address, ["address"] = m.BillingAddress.Address,
["postal_dest"] = m.BillingAddress.PostalDestId, ["postal_dest"] = m.BillingAddress.PostalDestId,
["country"] = m.BillingAddress.CountryNum, ["country"] = m.BillingAddress.CountryNum,
@ -460,11 +462,12 @@ namespace Elwig.Helpers.Export {
return (new Member { return (new Member {
MgNr = mgnr, MgNr = mgnr,
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(), PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
Name = json["name"]!.AsValue().GetValue<string>(),
Prefix = json["prefix"]?.AsValue().GetValue<string>(), Prefix = json["prefix"]?.AsValue().GetValue<string>(),
GivenName = json["given_name"]!.AsValue().GetValue<string>(), GivenName = json["given_name"]?.AsValue().GetValue<string>(),
MiddleName = json["middle_names"]?.AsValue().GetValue<string>(), MiddleName = json["middle_names"]?.AsValue().GetValue<string>(),
FamilyName = json["family_name"]!.AsValue().GetValue<string>(),
Suffix = json["suffix"]?.AsValue().GetValue<string>(), Suffix = json["suffix"]?.AsValue().GetValue<string>(),
ForTheAttentionOf = json["attn"]?.AsValue().GetValue<string>(),
Birthday = json["birthday"]?.AsValue().GetValue<string>(), Birthday = json["birthday"]?.AsValue().GetValue<string>(),
EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(), EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(), ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
@ -473,6 +476,7 @@ namespace Elwig.Helpers.Export {
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(), ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(), LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(), UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(),
IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false,
IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false, IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false, IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
IsOrganic = json["organic"]?.AsValue().GetValue<bool>() ?? false, IsOrganic = json["organic"]?.AsValue().GetValue<bool>() ?? false,
@ -490,7 +494,7 @@ namespace Elwig.Helpers.Export {
Comment = json["comment"]?.AsValue().GetValue<string>(), Comment = json["comment"]?.AsValue().GetValue<string>(),
}, json["billing_address"] is JsonObject a ? new BillingAddr { }, json["billing_address"] is JsonObject a ? new BillingAddr {
MgNr = mgnr, MgNr = mgnr,
Name = a["name"]!.AsValue().GetValue<string>(), FullName = a["name"]!.AsValue().GetValue<string>(),
CountryNum = a["country"]!.AsValue().GetValue<int>(), CountryNum = a["country"]!.AsValue().GetValue<int>(),
PostalDestId = a["postal_dest"]!.AsValue().GetValue<string>(), PostalDestId = a["postal_dest"]!.AsValue().GetValue<string>(),
Address = a["address"]!.AsValue().GetValue<string>(), Address = a["address"]!.AsValue().GetValue<string>(),

View File

@ -197,7 +197,7 @@ namespace Elwig.Helpers {
} }
public static void MailTo(string emailAddress) { public static void MailTo(string emailAddress) {
MailTo(new string[] { emailAddress }); MailTo([emailAddress]);
} }
public static void MailTo(IEnumerable<string> emailAddresses) { public static void MailTo(IEnumerable<string> emailAddresses) {

View File

@ -35,7 +35,7 @@ namespace Elwig.Models.Dtos {
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) { private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT m.mgnr, m.family_name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, p.plz, o.name AS ort, m.address,

View File

@ -50,7 +50,7 @@ namespace Elwig.Models.Dtos {
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) { private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT m.mgnr, m.family_name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision, p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,

View File

@ -0,0 +1,62 @@
using Elwig.Documents;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
public class DeliveryAncmtListData : DataTable<DeliveryAncmtListRow> {
private static readonly (string, string, string?, int?)[] FieldNames = [
("Date", "Datum", null, 20),
("Branch", "Zweigstelle", null, 30),
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("SortId", "Sorte", null, 10),
("Weight", "Gewicht", "kg", 20),
];
public DeliveryAncmtListData(IEnumerable<DeliveryAncmtListRow> rows, List<string> filterNames) :
base(DeliveryAncmtList.Name, DeliveryAncmtList.Name, string.Join(" / ", filterNames), rows, FieldNames) {
}
public static async Task<DeliveryAncmtListData> FromQuery(IQueryable<DeliveryAncmt> query, List<string> filterNames) {
return new((await query
.Include(a => a.Schedule.Branch)
.Include(a => a.Member)
.Include(a => a.Variety)
.AsSplitQuery()
.ToListAsync()).Select(d => new DeliveryAncmtListRow(d)), filterNames);
}
}
public class DeliveryAncmtListRow {
public DateOnly Date;
public string Branch;
public int MgNr;
public string Name1;
public string Name2;
public string AdministrativeName;
public string SortId;
public string Variety;
public int Weight;
public DeliveryAncmtListRow(DeliveryAncmt a) {
var s = a.Schedule;
var m = a.Member;
Date = s.Date;
Branch = s.Branch.Name;
MgNr = m.MgNr;
Name1 = m.AdministrativeName1;
Name2 = m.AdministrativeName2;
AdministrativeName = m.AdministrativeName;
SortId = a.SortId;
Variety = a.Variety.Name;
Weight = a.Weight;
}
}
}

View File

@ -14,16 +14,18 @@ namespace Elwig.Models.Dtos {
("Pos", "Pos.", null, 10), ("Pos", "Pos.", null, 10),
("Date", "Datum", null, 20), ("Date", "Datum", null, 20),
("Time", "Zeit", null, 20), ("Time", "Zeit", null, 20),
("DeliveryBranch", "Zweigstelle", null, 30),
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40), ("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40), ("Name2", "Vorname", null, 40),
("MemberBranch", "Stamm-Zwst.", null, 30),
("SortId", "Sorte", null, 10), ("SortId", "Sorte", null, 10),
("AttrId", "Attr.", null, 15), ("AttrId", "Attr.", null, 15),
("CultId", "Bewirt.", null, 15), ("CultId", "Bewirt.", null, 15),
("QualId", "Qualität", null, 15), ("QualId", "Qualität", null, 15),
("Gradation", "Gradation", "°Oe|°KMW", 40), ("Gradation", "Gradation", "°Oe|°KMW", 40),
("Weight", "Gewicht", "kg", 20), ("Weight", "Gewicht", "kg", 20),
("NetGross", "bto./nto.", null, 20), ("IsNetWeight", "Gerebelt", null, 20),
("HkId", "Herkunft", null, 20), ("HkId", "Herkunft", null, 20),
("Modifiers", "Zu-/Abschläge", null, 40), ("Modifiers", "Zu-/Abschläge", null, 40),
("Comment", "Anmerkung", null, 60), ("Comment", "Anmerkung", null, 60),
@ -35,7 +37,7 @@ namespace Elwig.Models.Dtos {
public static async Task<DeliveryJournalData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) { public static async Task<DeliveryJournalData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
return new((await query return new((await query
.Include(p => p.Delivery.Member) .Include(p => p.Delivery.Member.Branch)
.Include(p => p.Delivery.Branch) .Include(p => p.Delivery.Branch)
.Include(p => p.PartModifiers).ThenInclude(m => m.Modifier) .Include(p => p.PartModifiers).ThenInclude(m => m.Modifier)
.Include(p => p.Variety) .Include(p => p.Variety)
@ -53,10 +55,12 @@ namespace Elwig.Models.Dtos {
public int Pos; public int Pos;
public DateOnly Date; public DateOnly Date;
public TimeOnly? Time; public TimeOnly? Time;
public string DeliveryBranch;
public int MgNr; public int MgNr;
public string Name1; public string Name1;
public string Name2; public string Name2;
public string AdministrativeName; public string AdministrativeName;
public string? MemberBranch;
public string SortId; public string SortId;
public string Variety; public string Variety;
public string? AttrId; public string? AttrId;
@ -71,7 +75,6 @@ namespace Elwig.Models.Dtos {
public double Oe => Gradation.Oe; public double Oe => Gradation.Oe;
public int Weight { get; set; } public int Weight { get; set; }
public bool IsNetWeight; public bool IsNetWeight;
public string NetGross => IsNetWeight ? "n" : "b";
public string? Modifiers; public string? Modifiers;
public string? Comment; public string? Comment;
@ -83,10 +86,12 @@ namespace Elwig.Models.Dtos {
Pos = p.DPNr; Pos = p.DPNr;
Date = d.Date; Date = d.Date;
Time = d.Time; Time = d.Time;
DeliveryBranch = d.Branch.Name;
MgNr = m.MgNr; MgNr = m.MgNr;
Name1 = m.FamilyName; Name1 = m.Name;
Name2 = m.AdministrativeName2; Name2 = m.AdministrativeName2;
AdministrativeName = m.AdministrativeName; AdministrativeName = m.AdministrativeName;
MemberBranch = m.Branch?.Name;
SortId = p.SortId; SortId = p.SortId;
Variety = p.Variety.Name; Variety = p.Variety.Name;

View File

@ -37,7 +37,7 @@ namespace Elwig.Models.Dtos {
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) { private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT m.mgnr, m.family_name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, p.plz, o.name AS ort, m.address,

View File

@ -91,7 +91,7 @@ namespace Elwig.Models.Dtos {
public MemberListRow(Member m, int? areaCom = null) { public MemberListRow(Member m, int? areaCom = null) {
MgNr = m.MgNr; MgNr = m.MgNr;
Name1 = m.FamilyName; Name1 = m.Name;
Name2 = m.AdministrativeName2; Name2 = m.AdministrativeName2;
DefaultKg = m.DefaultKg?.Name; DefaultKg = m.DefaultKg?.Name;
Branch = m.Branch?.Name; Branch = m.Branch?.Name;
@ -100,7 +100,7 @@ namespace Elwig.Models.Dtos {
Plz = m.PostalDest.AtPlz!.Plz; Plz = m.PostalDest.AtPlz!.Plz;
Locality = m.PostalDest.AtPlz!.Ort.Name; Locality = m.PostalDest.AtPlz!.Ort.Name;
if (m.BillingAddress is BillingAddr a) { if (m.BillingAddress is BillingAddr a) {
BillingName = a.Name; BillingName = a.FullName;
BillingAddress = a.Address; BillingAddress = a.Address;
BillingPlz = a.PostalDest.AtPlz!.Plz; BillingPlz = a.PostalDest.AtPlz!.Plz;
BillingLocality = a.PostalDest.AtPlz!.Ort.Name; BillingLocality = a.PostalDest.AtPlz!.Ort.Name;

View File

@ -26,7 +26,7 @@ namespace Elwig.Models.Dtos {
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) { public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
var rows = await table.FromSqlRaw($""" var rows = await table.FromSqlRaw($"""
SELECT m.mgnr, m.family_name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.business_shares, p.plz, o.name AS ort, m.address, m.business_shares,

View File

@ -1,5 +1,6 @@
using Elwig.Helpers; using Elwig.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -39,6 +40,16 @@ namespace Elwig.Models.Entities {
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } = null!; public virtual Member Member { get; private set; } = null!;

View File

@ -8,7 +8,7 @@ namespace Elwig.Models.Entities {
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("name")] [Column("name")]
public required string Name { get; set; } public required string FullName { get; set; }
[Column("country")] [Column("country")]
public int CountryNum { get; set; } public int CountryNum { get; set; }

View File

@ -83,15 +83,15 @@ namespace Elwig.Models.Entities {
get => Utils.DecFromDb(AmountValue, 2); get => Utils.DecFromDb(AmountValue, 2);
} }
[Column("ctime")] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; private set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime")] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; private set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("Year, AvNr, MgNr")] [ForeignKey("Year, AvNr, MgNr")]
public virtual PaymentMember Payment { get; private set; } = null!; public virtual PaymentMember Payment { get; private set; } = null!;

View File

@ -17,7 +17,6 @@ namespace Elwig.Models.Entities {
[Column("date")] [Column("date")]
public required string DateString { get; set; } public required string DateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd"); get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
@ -26,7 +25,6 @@ namespace Elwig.Models.Entities {
[Column("time")] [Column("time")]
public string? TimeString { get; set; } public string? TimeString { get; set; }
[NotMapped] [NotMapped]
public TimeOnly? Time { public TimeOnly? Time {
get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss"); get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
@ -63,6 +61,16 @@ namespace Elwig.Models.Entities {
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } = null!; public virtual Season Season { get; private set; } = null!;
@ -110,7 +118,7 @@ namespace Elwig.Models.Entities {
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
var list = new string?[] { var list = new string?[] {
LsNr, Time?.ToString("HH:mm"), LsNr, Time?.ToString("HH:mm"),
Member.FamilyName, Member.MiddleName, Member.GivenName, Member.BillingAddress?.Name, Member.Name, Member.MiddleName, Member.GivenName, Member.BillingAddress?.FullName,
Comment Comment
}.ToList(); }.ToList();
list.AddRange(Parts.Select(p => p.Comment).Distinct()); list.AddRange(Parts.Select(p => p.Comment).Distinct());

View File

@ -0,0 +1,56 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("delivery_announcement"), PrimaryKey("Year", "DsNr", "MgNr", "SortId")]
public class DeliveryAncmt {
[Column("year")]
public int Year { get; set; }
[Column("dsnr")]
public int DsNr { get; set; }
[Column("mgnr")]
public int MgNr { get; set; }
[Column("sortid")]
public required string SortId { get; set; }
[Column("weight")]
public int Weight { get; set; }
[Column("type")]
public required string Type { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("Year, DsNr")]
public virtual DeliverySchedule Schedule { get; private set; } = null!;
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; } = null!;
[ForeignKey("SortId")]
public virtual WineVar Variety { get; private set; } = null!;
public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore([
Schedule.Description,
Member.Name, Member.MiddleName, Member.GivenName,
Member.BillingAddress?.FullName,
], keywords);
}
}
}

View File

@ -128,6 +128,16 @@ namespace Elwig.Models.Entities {
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[InverseProperty(nameof(DeliveryPartModifier.Part))] [InverseProperty(nameof(DeliveryPartModifier.Part))]
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!; public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;

View File

@ -0,0 +1,71 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models.Entities {
[Table("delivery_schedule"), PrimaryKey("Year", "DsNr")]
public class DeliverySchedule {
[Column("year")]
public int Year { get; set; }
[Column("dsnr")]
public int DsNr { get; set; }
[Column("date")]
public required string DateString { get; set; }
[NotMapped]
public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
set => DateString = value.ToString("yyyy-MM-dd");
}
[Column("zwstid")]
public required string ZwstId { get; set; }
[Column("description")]
public required string Description { get; set; }
[Column("max_weight")]
public int? MaxWeight { get; set; }
[NotMapped]
public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
[NotMapped]
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
[Column("ancmt_from")]
public long? AncmtFromUnix { get; set; }
[NotMapped]
public DateTime? AncmtFrom {
get => AncmtFromUnix != null ? DateTimeOffset.FromUnixTimeSeconds(AncmtFromUnix.Value).LocalDateTime : null;
set => AncmtFromUnix = value != null ? new DateTimeOffset(value.Value).ToUnixTimeSeconds() : null;
}
[Column("ancmt_to")]
public long? AncmtToUnix { get; set; }
[NotMapped]
public DateTime? AncmtTo {
get => AncmtToUnix != null ? DateTimeOffset.FromUnixTimeSeconds(AncmtToUnix.Value).LocalDateTime : null;
set => AncmtToUnix = value != null ? new DateTimeOffset(value.Value).ToUnixTimeSeconds() : null;
}
[ForeignKey("Year")]
public virtual Season Season { get; private set; } = null!;
[ForeignKey("ZwstId")]
public virtual Branch Branch { get; private set; } = null!;
[InverseProperty(nameof(DeliveryScheduleWineVar.Schedule))]
public virtual ICollection<DeliveryScheduleWineVar> Varieties { get; private set; } = null!;
[InverseProperty(nameof(DeliveryAncmt.Schedule))]
public virtual ICollection<DeliveryAncmt> Announcements { get; private set; } = null!;
public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore([Description], keywords);
}
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("delivery_schedule_wine_variety"), PrimaryKey("Year", "DsNr", "SortId")]
public class DeliveryScheduleWineVar {
[Column("year")]
public int Year { get; set; }
[Column("dsnr")]
public int DsNr { get; set; }
[Column("sortid")]
public required string SortId { get; set; }
[Column("priority")]
public int Priority { get; set; }
[ForeignKey("Year, DsNr")]
public virtual DeliverySchedule Schedule { get; private set; } = null!;
[ForeignKey("SortId")]
public virtual WineVar Variety { get; private set; } = null!;
}
}

View File

@ -14,52 +14,45 @@ namespace Elwig.Models.Entities {
[Column("predecessor_mgnr")] [Column("predecessor_mgnr")]
public int? PredecessorMgNr { get; set; } public int? PredecessorMgNr { get; set; }
[Column("name")]
public required string Name { get; set; }
[Column("prefix")] [Column("prefix")]
public string? Prefix { get; set; } public string? Prefix { get; set; }
[Column("given_name")] [Column("given_name")]
public required string GivenName { get; set; } public string? GivenName { get; set; }
[Column("middle_names")] [Column("middle_names")]
public string? MiddleName { get; set; } public string? MiddleName { get; set; }
[NotMapped] [NotMapped]
public string[] MiddleNames { public string[] MiddleNames {
get => (MiddleName != null) ? MiddleName.Split(" ") : []; get => (MiddleName != null) ? MiddleName.Split(" ") : [];
set => MiddleName = (value.Length > 0) ? string.Join(" ", value) : null; set => MiddleName = (value.Length > 0) ? string.Join(" ", value) : null;
} }
[Column("family_name")]
public required string FamilyName { get; set; }
[Column("suffix")] [Column("suffix")]
public string? Suffix { get; set; } public string? Suffix { get; set; }
public string Name => [Column("attn")]
(Prefix != null ? Prefix + " " : "") + public string? ForTheAttentionOf { get; set; }
GivenName + " " +
(MiddleName != null ? MiddleName + " " : "") +
FamilyName +
(Suffix != null ? " " + Suffix : "");
public string ShortName => GivenName + " " + FamilyName; [NotMapped]
public string FullName => IsJuridicalPerson ? Name : string.Join(" ", ((string?[])[Prefix, GivenName, MiddleName, Name, Suffix]).Where(s => !string.IsNullOrWhiteSpace(s)));
public string AdministrativeName => AdministrativeName1 + " " + AdministrativeName2; [NotMapped]
public string ShortName => (!string.IsNullOrWhiteSpace(GivenName) ? $"{GivenName} " : "") + Name;
public string AdministrativeName1 => FamilyName.Replace('ß', 'ẞ').ToUpper(); [NotMapped]
public string AdministrativeName => AdministrativeName1 + (!string.IsNullOrWhiteSpace(AdministrativeName2) ? $" {AdministrativeName2}" : "");
public string AdministrativeName2 => [NotMapped]
(Prefix != null ? Prefix + " " : "") + public string AdministrativeName1 => IsJuridicalPerson ? Name : Name.Replace('ß', 'ẞ').ToUpper();
GivenName + [NotMapped]
(MiddleName != null ? " " + MiddleName : "") + public string? AdministrativeName2 => IsJuridicalPerson ? null : string.Join(" ", ((string?[])[Prefix, GivenName, MiddleName, Suffix]).Where(s => !string.IsNullOrWhiteSpace(s)));
(Suffix != null ? " " + Suffix : "");
[Column("birthday")] [Column("birthday")]
public string? Birthday { get; set; } public string? Birthday { get; set; }
[Column("entry_date")] [Column("entry_date")]
public string? EntryDateString { get; set; } public string? EntryDateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly? EntryDate { public DateOnly? EntryDate {
get => EntryDateString != null ? DateOnly.ParseExact(EntryDateString, "yyyy-MM-dd") : null; get => EntryDateString != null ? DateOnly.ParseExact(EntryDateString, "yyyy-MM-dd") : null;
@ -68,7 +61,6 @@ namespace Elwig.Models.Entities {
[Column("exit_date")] [Column("exit_date")]
public string? ExitDateString { get; set; } public string? ExitDateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly? ExitDate { public DateOnly? ExitDate {
get => ExitDateString != null ? DateOnly.ParseExact(ExitDateString, "yyyy-MM-dd") : null; get => ExitDateString != null ? DateOnly.ParseExact(ExitDateString, "yyyy-MM-dd") : null;
@ -90,6 +82,9 @@ namespace Elwig.Models.Entities {
[Column("ustid_nr")] [Column("ustid_nr")]
public string? UstIdNr { get; set; } public string? UstIdNr { get; set; }
[Column("juridical_pers")]
public bool IsJuridicalPerson { get; set; }
[Column("volllieferant")] [Column("volllieferant")]
public bool IsVollLieferant { get; set; } public bool IsVollLieferant { get; set; }
@ -146,10 +141,19 @@ namespace Elwig.Models.Entities {
[ForeignKey("DefaultKgNr")] [ForeignKey("DefaultKgNr")]
public virtual WbKg? DefaultWbKg { get; private set; } public virtual WbKg? DefaultWbKg { get; private set; }
[NotMapped] [NotMapped]
public AT_Kg? DefaultKg => DefaultWbKg?.AtKg; public AT_Kg? DefaultKg => DefaultWbKg?.AtKg;
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("ZwstId")] [ForeignKey("ZwstId")]
public virtual Branch? Branch { get; private set; } public virtual Branch? Branch { get; private set; }
@ -179,8 +183,8 @@ namespace Elwig.Models.Entities {
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore([ return Utils.GetSearchScore([
FamilyName, MiddleName, GivenName, Name, MiddleName, GivenName,
BillingAddress?.Name, BillingAddress?.FullName,
Comment, Comment,
], keywords); ], keywords);
} }

View File

@ -11,12 +11,31 @@ namespace Elwig.Models.Entities {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("amount")] [Column("mod_abs")]
public long AmountValue { get; set; } public long? ModAbsValue { get; set; }
[NotMapped] [NotMapped]
public decimal Amount { public decimal? ModAbs {
get => Utils.DecFromDb(AmountValue, 2); get => ModAbsValue != null ? Utils.DecFromDb(ModAbsValue.Value, 2) : null;
set => AmountValue = Utils.DecToDb(value, 2); set => ModAbsValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("mod_rel")]
public double? ModRelValue { get; set; }
[NotMapped]
public decimal? ModRel {
get => ModRelValue != null ? (decimal)ModRelValue.Value : null;
set => ModRelValue = value != null ? (double)value.Value : null;
}
[Column("mod_comment")]
public string? ModComment { get; set; }
[Column("amount")]
public long? AmountValue { get; set; }
[NotMapped]
public decimal? Amount {
get => AmountValue != null ? Utils.DecFromDb(AmountValue.Value, 2) : null;
set => AmountValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
} }
[Column("comment")] [Column("comment")]

View File

@ -35,9 +35,9 @@ namespace Elwig.Models.Entities {
public bool TestVariant { get; set; } public bool TestVariant { get; set; }
[Column("calc_time")] [Column("calc_time")]
public int? CalcTimeUnix { get; set; } public long? CalcTimeUnix { get; set; }
[NotMapped] [NotMapped]
public DateTime? CalcTime => CalcTimeUnix != null ? DateTimeOffset.FromUnixTimeSeconds((long)CalcTimeUnix).UtcDateTime.ToLocalTime() : null; public DateTime? CalcTime => CalcTimeUnix != null ? DateTimeOffset.FromUnixTimeSeconds(CalcTimeUnix.Value).LocalDateTime : null;
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
@ -45,6 +45,16 @@ namespace Elwig.Models.Entities {
[Column("data")] [Column("data")]
public required string Data { get; set; } public required string Data { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } = null!; public virtual Season Season { get; private set; } = null!;

View File

@ -2,7 +2,7 @@ using Elwig.Models.Entities;
namespace Elwig.Models { namespace Elwig.Models {
public interface IAddress { public interface IAddress {
string Name { get; } string FullName { get; }
string Address { get; } string Address { get; }
PostalDest PostalDest { get; } PostalDest PostalDest { get; }
} }

View File

@ -0,0 +1,91 @@
-- schema version 25 to 26
CREATE TABLE delivery_schedule (
year INTEGER NOT NULL,
dsnr INTEGER NOT NULL,
date TEXT NOT NULL CHECK (date LIKE year || '-%' AND date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$'),
zwstid TEXT NOT NULL,
description TEXT NOT NULL,
max_weight INTEGER,
ancmt_from INTEGER,
ancmt_to INTEGER,
CONSTRAINT pk_delivery_schedule PRIMARY KEY (year, dsnr),
CONSTRAINT fk_delivery_schedule_season FOREIGN KEY (year) REFERENCES season (year)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_delivery_schedule_branch FOREIGN KEY (zwstid) REFERENCES branch (zwstid)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
CREATE TABLE delivery_schedule_wine_variety (
year INTEGER NOT NULL,
dsnr INTEGER NOT NULL,
sortid TEXT NOT NULL,
priority INTEGER NOT NULL DEFAULT 1,
CONSTRAINT pk_delivery_schedule_wine_variety PRIMARY KEY (year, dsnr, sortid),
CONSTRAINT fk_delivery_schedule_wine_variety_delivery_schedule FOREIGN KEY (year, dsnr) REFERENCES delivery_schedule (year, dsnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_delivery_schedule_wine_variety_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
CREATE TABLE delivery_announcement (
year INTEGER NOT NULL,
dsnr INTEGER NOT NULL,
mgnr INTEGER NOT NULL,
sortid TEXT NOT NULL,
weight INTEGER NOT NULL,
type TEXT NOT NULL,
ctime INTEGER NOT NULL DEFAULT (UNIXEPOCH()),
mtime INTEGER NOT NULL DEFAULT (UNIXEPOCH()),
CONSTRAINT pk_delivery_announcement PRIMARY KEY (year, dsnr, mgnr, sortid),
CONSTRAINT fk_delivery_announcement_delivery_schedule FOREIGN KEY (year, dsnr) REFERENCES delivery_schedule (year, dsnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_delivery_announcement_member FOREIGN KEY (mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_delivery_announcement_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
CREATE TRIGGER t_delivery_announcement_i_ctime
AFTER INSERT ON delivery_announcement FOR EACH ROW
WHEN NEW.ctime != UNIXEPOCH()
BEGIN
UPDATE delivery_announcement SET ctime = UNIXEPOCH() WHERE (year, dsnr, mgnr, sortid) = (NEW.year, NEW.dsnr, NEW.mgnr, NEW.sortid);
END;
CREATE TRIGGER t_delivery_announcement_u_ctime
BEFORE UPDATE ON delivery_announcement FOR EACH ROW
WHEN OLD.ctime != NEW.ctime
BEGIN
SELECT RAISE(ABORT, 'It is not allowed to change ctime');
END;
CREATE TRIGGER t_delivery_announcement_i_mtime
AFTER INSERT ON delivery_announcement FOR EACH ROW
WHEN NEW.mtime != UNIXEPOCH()
BEGIN
UPDATE delivery_announcement SET mtime = UNIXEPOCH() WHERE (year, dsnr, mgnr, sortid) = (NEW.year, NEW.dsnr, NEW.mgnr, NEW.sortid);
END;
CREATE TRIGGER t_delivery_announcement_u_mtime
AFTER UPDATE ON delivery_announcement FOR EACH ROW
WHEN NEW.mtime != UNIXEPOCH()
BEGIN
UPDATE delivery_announcement SET mtime = UNIXEPOCH() WHERE (year, dsnr, mgnr, sortid) = (NEW.year, NEW.dsnr, NEW.mgnr, NEW.sortid);
END;

View File

@ -0,0 +1,48 @@
-- schema version 26 to 27
PRAGMA writable_schema = ON;
ALTER TABLE member RENAME COLUMN family_name TO name;
ALTER TABLE member ADD COLUMN attn TEXT DEFAULT NULL;
ALTER TABLE member ADD COLUMN juridical_pers INTEGER NOT NULL CHECK (juridical_pers IN (TRUE, FALSE)) DEFAULT FALSE;
UPDATE sqlite_schema SET sql = REPLACE(sql, 'given_name TEXT NOT NULL', 'given_name TEXT DEFAULT NULL')
WHERE type = 'table' AND name = 'member';
DROP VIEW v_bki_member;
CREATE VIEW v_bki_member AS
SELECT s.year, m.mgnr, m.lfbis_nr, m.name,
(COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '')) AS other_names,
a.name AS billing_name, COALESCE(a.address, m.address) AS address,
COALESCE(a.country, m.country) AS country, COALESCE(a.postal_dest, m.postal_dest) AS postal_dest,
SUM(IIF(c.year_from <= s.year AND (c.year_to IS NULL OR c.year_to >= s.year), c.area, 0)) AS area
FROM season s, member m
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
LEFT JOIN area_commitment c ON c.mgnr = m.mgnr
GROUP BY s.year, m.mgnr;
DROP VIEW v_bki_delivery;
CREATE VIEW v_bki_delivery AS
SELECT m.lfbis_nr, m.name, m.other_names, m.billing_name,
m.address, plz.plz, IIF(INSTR(o.name, ',') = 0, o.name, SUBSTR(o.name, 1, INSTR(o.name, ',') - 1)) AS ort,
d.date, d.weight, v.type, v.sortid, d.qualid, d.year, d.hkid, d.kmw, d.oe,
m.area
FROM v_delivery d
JOIN v_bki_member m ON (m.year, m.mgnr) = (d.year, d.mgnr)
JOIN postal_dest pd ON (pd.country, pd.id) = (m.country, m.postal_dest)
LEFT JOIN AT_plz_dest ap ON (ap.country, ap.id) = (pd.country, pd.id)
LEFT JOIN AT_plz plz ON plz.plz = ap.plz
LEFT JOIN AT_ort o ON o.okz = ap.okz
JOIN wine_variety v ON v.sortid = d.sortid
ORDER BY d.date, d.time;
PRAGMA schema_version = 2601;
PRAGMA writable_schema = OFF;
UPDATE member
SET name = a.name, juridical_pers = TRUE
FROM member_billing_address a
WHERE a.mgnr = member.mgnr AND member.name = '';
DELETE FROM member_billing_address
WHERE mgnr IN (SELECT mgnr FROM member WHERE name = member_billing_address.name)

View File

@ -0,0 +1,13 @@
-- schema version 27 to 28
ALTER TABLE payment_custom ADD COLUMN mod_abs INTEGER DEFAULT NULL;
ALTER TABLE payment_custom ADD COLUMN mod_rel REAL DEFAULT NULL;
ALTER TABLE payment_custom ADD COLUMN mod_comment TEXT DEFAULT NULL;
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(REPLACE(sql, 'amount INTEGER NOT NULL', 'amount INTEGER DEFAULT NULL'), 'comment TEXT', 'comment TEXT DEFAULT NULL')
WHERE type = 'table' AND name = 'payment_custom';
PRAGMA schema_version = 2701;
PRAGMA writable_schema = OFF;

View File

@ -0,0 +1,183 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Microsoft.Win32;
using System.Net.Http;
using System.Windows.Input;
using System.Windows;
using System;
namespace Elwig.Services {
public static class DeliveryAncmtService {
public enum ExportSubject {
FromSelectedSchedule,
};
public static void InitInputs(this DeliveryAncmtAdminViewModel vm) {
if (vm.SelectedDeliverySchedule is DeliverySchedule s)
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, s.Year, s.DsNr);
}
public static void ClearInputs(this DeliveryAncmtAdminViewModel vm) {
}
public static async Task FillInputs(this DeliveryAncmtAdminViewModel vm, DeliveryAncmt a) {
vm.MgNr = a.MgNr;
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr);
vm.SortId = a.SortId;
vm.Weight = a.Weight;
}
public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<DeliveryAncmt> deliveryAncmtQuery = ctx.DeliveryAnnouncements;
if (vm.SelectedDeliverySchedule is DeliverySchedule s) {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == s.Year && a.DsNr == s.DsNr);
filterNames.Add($"{s.Date:dd.MM.yyyy} {s.Branch.Name} {s.Description}");
} else {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == vm.FilterSeason);
filterNames.Add($"{vm.FilterSeason}");
}
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterMgNr = new List<int>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
if (e.ToLower() is "r" or "rot") {
filterVar.AddRange(var.Values.Where(v => v.IsRed).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Rotweinsorten");
} else if (e.ToLower() is "w" or "weiß" or "weiss") {
filterVar.AddRange(var.Values.Where(v => v.IsWhite).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Weißweinsorten");
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper());
filter.RemoveAt(i--);
filterNames.Add(var[e.ToUpper()].Name);
} else if (e.Length == 3 && e[0] == '!' && var.ContainsKey(e[1..].ToUpper())) {
filterNotVar.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (e.All(char.IsAsciiDigit) && mgnr.TryGetValue(e, out var member)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);
filterNames.Add(member.AdministrativeName);
} else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
filter[i] = e[1..^1];
} else if (e.Length <= 2) {
filter.RemoveAt(i--);
}
}
if (filterMgNr.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterMgNr.Contains(a.MgNr));
if (filterVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterVar.Contains(a.SortId));
if (filterNotVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => !filterNotVar.Contains(a.SortId));
}
return (filterNames, deliveryAncmtQuery, filter);
}
public static async Task<(int, int, int, string)> UpdateDeliveryAncmt(this DeliveryAncmtAdminViewModel vm, int? oldYear, int? oldDsNr, int? oldMgNr, string? oldSortId, string? oldType) {
int year = vm.DeliverySchedule!.Year;
int dsnr = vm.DeliverySchedule!.DsNr;
int newMgNr = vm.MgNr!.Value;
string newSortId = vm.SortId!;
using (var ctx = new AppDbContext()) {
var a = new DeliveryAncmt {
Year = oldYear ?? year,
DsNr = oldDsNr ?? dsnr,
MgNr = oldMgNr ?? newMgNr,
SortId = oldSortId ?? newSortId,
Weight = vm.Weight!.Value,
Type = oldType ?? "manual",
};
if (oldDsNr != null) {
ctx.Update(a);
} else {
ctx.Add(a);
}
await ctx.SaveChangesAsync();
if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) {
await ctx.Database.ExecuteSqlRawAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = '{newSortId}' WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, '{a.SortId}')");
}
}
await App.HintContextChange();
return (year, dsnr, newMgNr, newSortId);
}
public static async Task GenerateDeliveryAncmtList(this DeliveryAncmtAdminViewModel vm, ExportSubject subject, ExportMode mode) {
using var ctx = new AppDbContext();
IQueryable<DeliveryAncmt> query;
List<string> filterNames = [];
if (subject == ExportSubject.FromSelectedSchedule) {
var s = vm.SelectedDeliverySchedule;
if (s == null) return;
query = ctx.DeliveryAnnouncements
.Where(a => a.Year == s.Year && a.DsNr == s.DsNr);
filterNames.Add($"{s.Date:dd.MM.yyyy} {s.Branch.Name} {s.Description}");
} else {
throw new ArgumentException("Invalid value for ExportSubject");
}
query = query
.OrderBy(a => a.Schedule.DateString)
.ThenBy(a => a.Schedule.Branch.Name)
.ThenBy(a => a.Schedule.Description)
.ThenBy(a => a.Member.Name)
.ThenBy(a => a.Member.GivenName)
.ThenBy(a => a.Member.MgNr);
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryAncmtList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryAncmtList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
}
}

View File

@ -0,0 +1,183 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using LinqKit;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Services {
public static class DeliveryScheduleService {
public static void InitInputs(this DeliveryScheduleAdminViewModel vm) {
if (vm.BranchSource.Count() == 1)
vm.Branch = vm.BranchSource.First();
vm.AncmtFromTimeString = "00:00";
vm.AncmtToTimeString = "23:59";
}
public static void ClearInputs(this DeliveryScheduleAdminViewModel vm) {
}
public static async Task FillInputs(this DeliveryScheduleAdminViewModel vm, DeliverySchedule s) {
vm.Date = s.Date;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, s.ZwstId);
vm.Description = s.Description;
vm.MaxWeight = s.MaxWeight;
vm.MainVarieties.Clear();
foreach (var v in s.Varieties.Where(v => v.Priority == 1)) {
vm.MainVarieties.Add((WineVar)ControlUtils.GetItemFromSourceWithPk(vm.MainVarietiesSource, v.SortId)!);
}
vm.OtherVarieties.Clear();
foreach (var v in s.Varieties.Where(v => v.Priority != 1)) {
vm.OtherVarieties.Add((WineVar)ControlUtils.GetItemFromSourceWithPk(vm.OtherVarietiesSource, v.SortId)!);
}
vm.AncmtFrom = s.AncmtFrom;
vm.AncmtTo = s.AncmtTo?.AddSeconds(-1);
}
public static async Task<(List<string>, IQueryable<DeliverySchedule>, List<string>)> GetFilters(this DeliveryScheduleAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<DeliverySchedule> deliveryScheduleQuery = ctx.DeliverySchedules;
if (vm.FilterSeason != null) {
deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.Year == vm.FilterSeason);
filterNames.Add($"{vm.FilterSeason}");
}
if (vm.FilterOnlyUpcoming) {
deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0);
filterNames.Add($"ab {Utils.Today:dd.MM.yyyy}");
}
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterZwst = new List<string>();
var filterDate = new List<(string?, string?)>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var zwst = await ctx.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
if (e.ToLower() is "r" or "rot") {
filterVar.AddRange(var.Values.Where(v => v.IsRed).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Rotweinsorten");
} else if (e.ToLower() is "w" or "weiß" or "weiss") {
filterVar.AddRange(var.Values.Where(v => v.IsWhite).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Weißweinsorten");
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper());
filter.RemoveAt(i--);
filterNames.Add(var[e.ToUpper()].Name);
} else if (e.Length == 3 && e[0] == '!' && var.ContainsKey(e[1..].ToUpper())) {
filterNotVar.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (zwst.ContainsKey(e.ToLower())) {
var b = zwst[e.ToLower()];
filterZwst.Add(b.ZwstId);
filter.RemoveAt(i--);
filterNames.Add($"Zweigstelle {b.Name}");
} else if (DateOnly.TryParse(e, out var date)) {
var s = date.ToString("yyyy-MM-dd");
filterDate.Add((s, s));
filter.RemoveAt(i--);
if (filterNames.Contains($"{vm.FilterSeason}") && vm.FilterSeason == date.Year)
filterNames.Remove($"{vm.FilterSeason}");
filterNames.Add(date.ToString("dd.MM.yyyy"));
} else if (Utils.DateFromToRegex.IsMatch(e)) {
var parts = e.Split("-");
if (parts.Length == 1) {
// single date
var dParts = parts[0].Split('.');
var s = $"{dParts[2]}-{dParts[1].PadLeft(2, '0')}-{dParts[0].PadLeft(2, '0')}";
filterDate.Add((s, s));
filter.RemoveAt(i--);
var n = string.Join('.', s.Split('-').Reverse());
if (dParts[2] == "") {
filterNames.Remove($"{vm.FilterSeason}");
filterNames.Add(n + $"{vm.FilterSeason}");
} else {
if ($"{vm.FilterSeason}" == dParts[2])
filterNames.Remove($"{vm.FilterSeason}");
filterNames.Add(n);
}
} else if (parts.Length == 2) {
// from/to date
var d1Parts = parts[0].Split('.');
var d2Parts = parts[1].Split('.');
var s1 = d1Parts.Length < 2 ? null : $"{d1Parts.ElementAtOrDefault(2)}-{d1Parts[1].PadLeft(2, '0')}-{d1Parts[0].PadLeft(2, '0')}";
var s2 = d2Parts.Length < 2 ? null : $"{d2Parts.ElementAtOrDefault(2)}-{d2Parts[1].PadLeft(2, '0')}-{d2Parts[0].PadLeft(2, '0')}";
filterDate.Add((s1, s2));
filter.RemoveAt(i--);
var n1 = s1 == null ? null : string.Join('.', s1.Split('-').Reverse());
var n2 = s2 == null ? null : string.Join('.', s2.Split('-').Reverse());
if (n1 != null && n2 != null) {
filterNames.Add($"{n1}{n2}");
} else if (n1 != null) {
filterNames.Add($"ab dem {n1}");
} else if (n2 != null) {
filterNames.Add($"bis zum {n2}");
}
}
} else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
filter[i] = e[1..^1];
} else if (e.Length <= 2) {
filter.RemoveAt(i--);
}
}
if (filterDate.Count > 0) {
var pr = PredicateBuilder.New<DeliverySchedule>(false);
foreach (var (d1, d2) in filterDate)
pr.Or(p => (d1 == null || d1.CompareTo(p.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(p.DateString.Substring(10 - d2.Length)) >= 0));
deliveryScheduleQuery = deliveryScheduleQuery.Where(pr);
}
if (filterVar.Count > 0) deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.Varieties.Any(v => filterVar.Contains(v.SortId)));
if (filterNotVar.Count > 0) deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.Varieties.All(v => !filterNotVar.Contains(v.SortId)));
if (filterZwst.Count > 0) deliveryScheduleQuery = deliveryScheduleQuery.Where(s => filterZwst.Contains(s.Branch.ZwstId));
}
return (filterNames, deliveryScheduleQuery, filter);
}
public static async Task UpdateDeliverySchedule(this DeliveryScheduleAdminViewModel vm, int? oldYear, int? oldDsNr) {
int year = vm.Date!.Value.Year;
using (var ctx = new AppDbContext()) {
var s = new DeliverySchedule {
Year = oldYear ?? year,
DsNr = oldDsNr ?? await ctx.NextDsNr(year),
DateString = $"{vm.Date:yyyy-MM-dd}",
ZwstId = vm.Branch!.ZwstId,
Description = vm.Description,
MaxWeight = vm.MaxWeight,
AncmtFrom = vm.AncmtFrom,
AncmtTo = vm.AncmtTo?.AddMinutes(1),
};
if (oldDsNr != null) {
ctx.Update(s);
} else {
ctx.Add(s);
}
ctx.UpdateDeliveryScheduleWineVarieties(s, (await ctx.DeliveryScheduleWineVarieties
.Where(v => v.Year == s.Year && v.DsNr == s.DsNr)
.Select(v => new { v.Variety, v.Priority })
.ToListAsync())
.Select(v => (v.Variety, v.Priority))
.ToList(), vm.MainVarieties.Select(v => (v, 1)).Union(vm.OtherVarieties.Select(v => (v, 2))).ToList());
await ctx.SaveChangesAsync();
}
await App.HintContextChange();
}
}
}

View File

@ -55,11 +55,13 @@ namespace Elwig.Services {
vm.IsMemberSelected = true; vm.IsMemberSelected = true;
vm.MgNrString = $"{m.MgNr}"; vm.MgNrString = $"{m.MgNr}";
vm.PredecessorMgNrString = $"{m.PredecessorMgNr}"; vm.PredecessorMgNrString = $"{m.PredecessorMgNr}";
vm.IsJuridicalPerson = m.IsJuridicalPerson;
vm.EnableMemberReferenceButton = m.PredecessorMgNr != null; vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
vm.Prefix = m.Prefix; vm.Prefix = m.Prefix;
vm.GivenName = m.GivenName; vm.GivenName = m.GivenName;
vm.FamilyName = m.FamilyName; vm.Name = m.Name;
vm.Suffix = m.Suffix; vm.Suffix = m.Suffix;
vm.ForTheAttentionOf = m.ForTheAttentionOf;
vm.Birthday = (m.Birthday != null) ? string.Join(".", m.Birthday.Split("-").Reverse()) : null; vm.Birthday = (m.Birthday != null) ? string.Join(".", m.Birthday.Split("-").Reverse()) : null;
if (m.Birthday?.Length == 10) { if (m.Birthday?.Length == 10) {
vm.Age = Utils.GetAge(DateOnly.ParseExact(m.Birthday, "yyyy-MM-dd")).ToString(); vm.Age = Utils.GetAge(DateOnly.ParseExact(m.Birthday, "yyyy-MM-dd")).ToString();
@ -109,7 +111,7 @@ namespace Elwig.Services {
var billingAddr = m.BillingAddress; var billingAddr = m.BillingAddress;
if (billingAddr != null) { if (billingAddr != null) {
vm.BillingName = billingAddr.Name; vm.BillingName = billingAddr.FullName;
vm.BillingAddress = billingAddr.Address; vm.BillingAddress = billingAddr.Address;
if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) { if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
vm.BillingPlzString = $"{b.Plz}"; vm.BillingPlzString = $"{b.Plz}";
@ -150,8 +152,12 @@ namespace Elwig.Services {
vm.StatusDeliveriesThisSeasonToolTip = d2Grid; vm.StatusDeliveriesThisSeasonToolTip = d2Grid;
var c = m.ActiveAreaCommitments(ctx, Utils.CurrentLastSeason); var c = m.ActiveAreaCommitments(ctx, Utils.CurrentLastSeason);
var s = await ctx.Seasons.FindAsync(await ctx.Seasons.MaxAsync(s => s.Year)); int maxKgPerHa = 10_000;
var (text, grid) = await AreaComService.GenerateToolTip(c, s?.MaxKgPerHa ?? 10_000); try {
var s = await ctx.Seasons.FindAsync(await ctx.Seasons.MaxAsync(s => s.Year));
if (s != null) maxKgPerHa = s.MaxKgPerHa;
} catch { }
var (text, grid) = await AreaComService.GenerateToolTip(c, maxKgPerHa);
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}"; vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitment = text; vm.StatusAreaCommitment = text;
vm.StatusAreaCommitmentToolTip = grid; vm.StatusAreaCommitmentToolTip = grid;
@ -392,14 +398,14 @@ namespace Elwig.Services {
} else if (vm.MemberListOrderByName) { } else if (vm.MemberListOrderByName) {
query = query query = query
.OrderBy(m => m.Branch!.Name) .OrderBy(m => m.Branch!.Name)
.ThenBy(m => m.FamilyName) .ThenBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr); .ThenBy(m => m.MgNr);
} else if (vm.MemberListOrderByOrt) { } else if (vm.MemberListOrderByOrt) {
query = query query = query
.OrderBy(m => m.Branch!.Name) .OrderBy(m => m.Branch!.Name)
.ThenBy(m => m.DefaultWbKg!.AtKg.Name) .ThenBy(m => m.DefaultWbKg!.AtKg.Name)
.ThenBy(m => m.FamilyName) .ThenBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr); .ThenBy(m => m.MgNr);
} }
@ -497,10 +503,12 @@ namespace Elwig.Services {
var m = new Member { var m = new Member {
MgNr = oldMgNr ?? newMgNr, MgNr = oldMgNr ?? newMgNr,
PredecessorMgNr = vm.PredecessorMgNr, PredecessorMgNr = vm.PredecessorMgNr,
Prefix = string.IsNullOrEmpty(vm.Prefix) ? null : vm.Prefix, IsJuridicalPerson = vm.IsJuridicalPerson,
GivenName = vm.GivenName!, Prefix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Prefix) ? null : vm.Prefix,
FamilyName = vm.FamilyName!, GivenName = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.GivenName) ? null : vm.GivenName,
Suffix = string.IsNullOrEmpty(vm.Suffix) ? null : vm.Suffix, 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.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
IsDeceased = vm.IsDeceased, IsDeceased = vm.IsDeceased,
CountryNum = 40, // Austria AT AUT CountryNum = 40, // Austria AT AUT
@ -540,7 +548,7 @@ namespace Elwig.Services {
var p = vm.BillingOrt; var p = vm.BillingOrt;
ctx.Add(new BillingAddr { ctx.Add(new BillingAddr {
MgNr = m.MgNr, MgNr = m.MgNr,
Name = vm.BillingName, FullName = vm.BillingName,
Address = vm.BillingAddress ?? "", Address = vm.BillingAddress ?? "",
CountryNum = p.CountryNum, CountryNum = p.CountryNum,
PostalDestId = p.Id, PostalDestId = p.Id,

View File

@ -0,0 +1,70 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Elwig.ViewModels {
public partial class DeliveryAncmtAdminViewModel : ObservableObject {
[ObservableProperty]
private string? _searchQuery = "";
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
[ObservableProperty]
private bool _filterOnlyUpcoming;
[ObservableProperty]
private string? _filterSeasonString;
public int? FilterSeason {
get => int.TryParse(FilterSeasonString, out var year) ? year : null;
set => FilterSeasonString = $"{value}";
}
[ObservableProperty]
private DeliveryAncmt? _selectedDeliveryAncmt;
[ObservableProperty]
private IEnumerable<DeliveryAncmt> _deliveryAncmts = [];
[ObservableProperty]
private DeliverySchedule? _selectedDeliverySchedule;
[ObservableProperty]
private IEnumerable<DeliverySchedule> _deliverySchedules = [];
[ObservableProperty]
private bool _enableSearchInputs = true;
[ObservableProperty]
private string? _mgNrString;
public int? MgNr {
get => int.TryParse(MgNrString, out var mgnr) ? mgnr : null;
set => MgNrString = $"{value}";
}
[ObservableProperty]
private Member? _member;
[ObservableProperty]
private IEnumerable<Member> _memberSource = [];
[ObservableProperty]
private string? _memberAddress;
[ObservableProperty]
private DeliverySchedule? _deliverySchedule;
[ObservableProperty]
private IEnumerable<DeliverySchedule> _deliveryScheduleSource = [];
[ObservableProperty]
private string? _sortId;
[ObservableProperty]
private WineVar? _wineVariety;
[ObservableProperty]
private IEnumerable<WineVar> _wineVarietySource = [];
[ObservableProperty]
private string? _weightString;
public int? Weight {
get => int.TryParse(WeightString, out var w) ? w : null;
set => WeightString = $"{value}";
}
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
}
}

View File

@ -0,0 +1,101 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
namespace Elwig.ViewModels {
public partial class DeliveryScheduleAdminViewModel : ObservableObject {
[ObservableProperty]
private string? _searchQuery = "";
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
[ObservableProperty]
private bool _filterOnlyUpcoming;
[ObservableProperty]
private string? _filterSeasonString;
public int? FilterSeason {
get => int.TryParse(FilterSeasonString, out var year) ? year : null;
set => FilterSeasonString = $"{value}";
}
[ObservableProperty]
private DeliverySchedule? _selectedDeliverySchedule;
[ObservableProperty]
private IEnumerable<DeliverySchedule> _deliverySchedules = [];
[ObservableProperty]
private bool _enableSearchInputs = true;
[ObservableProperty]
private string _dateString = "";
public DateOnly? Date {
get => DateOnly.TryParseExact(DateString, "dd.MM.yyyy", out var d) ? d : null;
set => DateString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private Branch? _branch;
[ObservableProperty]
private IEnumerable<Branch> _branchSource = [];
[ObservableProperty]
private string _description = "";
[ObservableProperty]
private string? _maxWeightString = "";
public int? MaxWeight {
get => int.TryParse(MaxWeightString, out var w) ? w : null;
set => MaxWeightString = $"{value}";
}
public ObservableCollection<WineVar> MainVarieties { get; set; } = [];
[ObservableProperty]
private IEnumerable<WineVar> _mainVarietiesSource = [];
public ObservableCollection<WineVar> OtherVarieties { get; set; } = [];
[ObservableProperty]
private IEnumerable<WineVar> _otherVarietiesSource = [];
[ObservableProperty]
private string _ancmtFromDateString = "";
public DateOnly? AncmtFromDate {
get => DateOnly.TryParseExact(AncmtFromDateString, "dd.MM.yyyy", out var d) ? d : null;
set => AncmtFromDateString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string _ancmtFromTimeString = "";
public TimeOnly? AncmtFromTime {
get => TimeOnly.TryParseExact(AncmtFromTimeString, "HH:mm", out var t) ? t : null;
set => AncmtFromTimeString = $"{value:HH:mm}";
}
public DateTime? AncmtFrom {
get => AncmtFromDate != null && AncmtFromTime != null ? AncmtFromDate.Value.ToDateTime(AncmtFromTime.Value) : null;
set {
AncmtFromDateString = $"{value:dd.MM.yyyy}";
AncmtFromTimeString = $"{value:HH:mm}";
}
}
[ObservableProperty]
private string _ancmtToDateString = "";
public DateOnly? AncmtToDate {
get => DateOnly.TryParseExact(AncmtToDateString, "dd.MM.yyyy", out var d) ? d : null;
set => AncmtToDateString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string _ancmtToTimeString = "";
public TimeOnly? AncmtToTime {
get => TimeOnly.TryParseExact(AncmtToTimeString, "HH:mm", out var t) ? t : null;
set => AncmtToTimeString = $"{value:HH:mm}";
}
public DateTime? AncmtTo {
get => AncmtToDate != null && AncmtToTime != null ? AncmtToDate.Value.ToDateTime(AncmtToTime.Value) : null;
set {
AncmtToDateString = $"{value:dd.MM.yyyy}";
AncmtToTimeString = $"{value:HH:mm}";
}
}
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
}
}

View File

@ -38,7 +38,7 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _enableSearchInputs = true; private bool _enableSearchInputs = true;
[ObservableProperty] [ObservableProperty]
public IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ]; private IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ];
[ObservableProperty] [ObservableProperty]
private bool _memberListOrderByMgNr; private bool _memberListOrderByMgNr;
@ -54,14 +54,18 @@ namespace Elwig.ViewModels {
private string? _predecessorMgNrString; private string? _predecessorMgNrString;
public int? PredecessorMgNr => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null; public int? PredecessorMgNr => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null;
[ObservableProperty] [ObservableProperty]
private bool _isJuridicalPerson;
[ObservableProperty]
private string? _prefix; private string? _prefix;
[ObservableProperty] [ObservableProperty]
private string? _givenName; private string? _givenName;
[ObservableProperty] [ObservableProperty]
private string? _familyName; private string? _name;
[ObservableProperty] [ObservableProperty]
private string? _suffix; private string? _suffix;
[ObservableProperty] [ObservableProperty]
private string? _forTheAttentionOf;
[ObservableProperty]
private string? _birthday; private string? _birthday;
[ObservableProperty] [ObservableProperty]
private string? _age; private string? _age;
@ -176,5 +180,9 @@ namespace Elwig.ViewModels {
private Visibility _controlButtonsVisibility = Visibility.Visible; private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty] [ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden; private Visibility _editingButtonsVisibility = Visibility.Hidden;
[ObservableProperty]
private Visibility _personalNameVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _juridicalNameVisibility = Visibility.Hidden;
} }
} }

View File

@ -159,7 +159,10 @@ namespace Elwig.Windows {
protected void ValidateRequiredInputs() { protected void ValidateRequiredInputs() {
foreach (var input in RequiredInputs) { foreach (var input in RequiredInputs) {
if (input is TextBox tb && tb.Text.Length == 0) { if (input is Control c && c.Visibility != Visibility.Visible) {
ControlUtils.ClearInputState(input);
Valid[input] = true;
} else if (input is TextBox tb && tb.Text.Length == 0) {
ControlUtils.SetInputInvalid(input); ControlUtils.SetInputInvalid(input);
Valid[input] = false; 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.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {

View File

@ -1,4 +1,5 @@
<local:AdministrationWindow x:Class="Elwig.Windows.AreaComAdminWindow" <local:AdministrationWindow
x:Class="Elwig.Windows.AreaComAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@ -255,7 +256,7 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="2" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel> <StatusBar.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Grid> <Grid>

View File

@ -117,7 +117,7 @@ namespace Elwig.Windows {
FinishInputFilling(); FinishInputFilling();
} }
private async void InitInputs() { private async Task InitInputs() {
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
await ViewModel.InitInputs(); await ViewModel.InitInputs();
@ -159,14 +159,14 @@ namespace Elwig.Windows {
NewAreaCommitmentButton_Click(null, null); NewAreaCommitmentButton_Click(null, null);
} }
private void NewAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) { private async void NewAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true; IsCreating = true;
AreaCommitmentList.IsEnabled = false; AreaCommitmentList.IsEnabled = false;
AreaCommitmentList.SelectedItem = null; AreaCommitmentList.SelectedItem = null;
HideAreaCommitmentNewEditDeleteButtons(); HideAreaCommitmentNewEditDeleteButtons();
ShowAreaCommitmentSaveResetCancelButtons(); ShowAreaCommitmentSaveResetCancelButtons();
UnlockInputs(); UnlockInputs();
InitInputs(); await InitInputs();
LockSearchInputs(); LockSearchInputs();
} }
@ -197,17 +197,25 @@ namespace Elwig.Windows {
} }
private async void DeleteAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) { private async void DeleteAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem; if (AreaCommitmentList.SelectedItem is not AreaCom a)
if (a == null) return; return;
var r = MessageBox.Show( var r = MessageBox.Show(
$"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?", $"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
"Flächenbindung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); "Flächenbindung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) { if (r == MessageBoxResult.OK) {
using var ctx = new AppDbContext(); Mouse.OverrideCursor = Cursors.AppStarting;
ctx.Remove(a); try {
await ctx.SaveChangesAsync(); using (var ctx = new AppDbContext()) {
await RefreshList(); ctx.Remove(a);
await ctx.SaveChangesAsync();
}
await App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Flächenbindung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
} }
@ -246,11 +254,11 @@ namespace Elwig.Windows {
AreaCommitmentResetButton_Click(null, null); AreaCommitmentResetButton_Click(null, null);
} }
private void AreaCommitmentResetButton_Click(object? sender, RoutedEventArgs? evt) { private async void AreaCommitmentResetButton_Click(object? sender, RoutedEventArgs? evt) {
if (IsEditing) { if (IsEditing) {
RefreshInputs(); RefreshInputs();
} else if (IsCreating) { } else if (IsCreating) {
InitInputs(); await InitInputs();
} }
UpdateButtons(); UpdateButtons();
} }

View File

@ -605,7 +605,7 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="2" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel> <StatusBar.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Grid> <Grid>

View File

@ -434,7 +434,7 @@ namespace Elwig.Windows {
.Where(m => m.IsActive || !IsCreating) .Where(m => m.IsActive || !IsCreating)
.Include(m => m.PostalDest.AtPlz!.Ort) .Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ToListAsync()); .ToListAsync());
ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync()); ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
@ -764,7 +764,7 @@ namespace Elwig.Windows {
.Where(m => m.IsActive || !ViewModel.IsReceipt) .Where(m => m.IsActive || !ViewModel.IsReceipt)
.Include(m => m.PostalDest.AtPlz!.Ort) .Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ToListAsync()); .ToListAsync());
if (DeliveryList.SelectedItem is not Delivery d) { if (DeliveryList.SelectedItem is not Delivery d) {
@ -810,7 +810,7 @@ namespace Elwig.Windows {
.Where(m => m.IsActive || !ViewModel.IsReceipt) .Where(m => m.IsActive || !ViewModel.IsReceipt)
.Include(m => m.PostalDest.AtPlz!.Ort) .Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ToListAsync()); .ToListAsync());
IsCreating = true; IsCreating = true;
@ -1243,7 +1243,7 @@ namespace Elwig.Windows {
private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) { private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
if (WineVarietyInput.SelectedItem is WineVar s) if (WineVarietyInput.SelectedItem is WineVar s)
ViewModel.SortId = s.SortId; ViewModel.SortId = s.SortId;
} }
private void UpdateWineQualityLevels() { private void UpdateWineQualityLevels() {

View File

@ -0,0 +1,322 @@
<local:AdministrationWindow
x:Class="Elwig.Windows.DeliveryAncmtAdminWindow"
AutomationProperties.AutomationId="DeliveryAncmtWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Traubenanmeldungen - Elwig" Height="700" Width="900" MinWidth="600" MinHeight="400"
Loaded="Window_Loaded">
<Window.DataContext>
<vm:DeliveryAncmtAdminViewModel/>
</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="ctrl:CheckComboBox">
<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.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="250"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*" MinWidth="300"/>
</Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Anmeldeliste">
<MenuItem x:Name="Menu_DeliveryAncmtList_SaveSelected" Header="...von ausgewähltem Leseplan speichern... (Excel)"
Click="Menu_DeliveryAncmtList_SaveSelected_Click"/>
<MenuItem x:Name="Menu_DeliveryAncmtList_ShowSelected" Header="...von ausgewähltem Leseplan anzeigen (PDF)"
Click="Menu_DeliveryAncmtList_ShowSelected_Click" InputGestureText="Strg+P"/>
<MenuItem x:Name="Menu_DeliveryAncmtList_SavePdfSelected" Header="...von ausgewähltem Leseplan speichern... (PDF)"
Click="Menu_DeliveryAncmtList_SavePdfSelected_Click"/>
<MenuItem x:Name="Menu_DeliveryAncmtList_PrintSelected" Header="...von ausgewähltem Leseplan drucken"
Click="Menu_DeliveryAncmtList_PrintSelected_Click" InputGestureText="Strg+Shift+P"/>
</MenuItem>
</Menu>
<Grid Grid.Row="1" Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="1*" MinHeight="100"/>
<RowDefinition Height="5"/>
<RowDefinition Height="2*" MinHeight="100"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding EnableSearchInputs}"
Grid.ColumnSpan="3" Margin="5,5,174,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
<Bold>Strg+F</Bold><LineBreak/><LineBreak/>
Traubenanmeldungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
<LineBreak/>
Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/>
<Bold>Rot/Weiß</Bold>: z.B. r, Rot, w, weiß, ...<LineBreak/>
<Bold>Mitglied</Bold>: z.B. 1234, 987, ...
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<ctrl:IntegerUpDown x:Name="SeasonInput" Text="{Binding FilterSeasonString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding EnableSearchInputs}"
Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1900" Maximum="9999"
Margin="5,5,113,0" VerticalAlignment="Top" HorizontalAlignment="Right"
TextChanged="SeasonInput_TextChanged"/>
<CheckBox x:Name="OnlyUpcomingInput" Content="Nur zukünftige" IsChecked="{Binding FilterOnlyUpcoming, Mode=TwoWay}" IsEnabled="{Binding EnableSearchInputs}"
HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="3"
Checked="OnlyUpcomingInput_Changed" Unchecked="OnlyUpcomingInput_Changed"/>
<ListBox x:Name="DeliveryScheduleList" SelectedItem="{Binding SelectedDeliverySchedule, Mode=TwoWay}" ItemsSource="{Binding DeliverySchedules, Mode=TwoWay}"
Grid.Row="1" Grid.ColumnSpan="3" Margin="5,0,5,5" VerticalAlignment="Stretch" IsEnabled="{Binding EnableSearchInputs}"
SelectionChanged="DeliveryScheduleList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Date, StringFormat='dd.MM.'}" Width="32"/>
<TextBlock Text="{Binding ZwstId}" Width="25" TextAlignment="Center"/>
<TextBlock Text="{Binding Description}" Width="200"/>
<TextBlock TextAlignment="Right">
<TextBlock Text="{Binding AnnouncedWeight, StringFormat='{}{0:N0}'}" Width="42" TextAlignment="Right"/> kg
/ <TextBlock Text="{Binding MaxWeight, StringFormat='{}{0:N0}'}" Width="42" TextAlignment="Right"/> kg
(<TextBlock Text="{Binding Percent, StringFormat='{}{0:N0}'}" Width="20" TextAlignment="Right"/>%)
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Grid.Row="2" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<DataGrid x:Name="DeliveryAncmtList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
ItemsSource="{Binding DeliveryAncmts, Mode=TwoWay}" SelectedItem="{Binding SelectedDeliveryAncmt, Mode=TwoWay}"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="DeliveryAncmtList_SelectionChanged"
Margin="5,5,5,0" Grid.Row="3" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr, StringFormat='{}{0} '}" Width="50">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
<DataGridTextColumn Header="Sorte" Binding="{Binding SortId}" Width="50">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="75">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewDeliveryAncmtButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="4"
Click="NewDeliveryAncmtButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditDeliveryAncmtButton" Content="Bearbeiten" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="4"
Click="EditDeliveryAncmtButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteDeliveryAncmtButton" Content="Löschen" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="4"
Click="DeleteDeliveryAncmtButton_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="4"
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="4"
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="4"
Click="CancelButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Esc</TextBlock>
</Button.ToolTip>
</Button>
</Grid>
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<GroupBox Header="Mitglied" Grid.Row="0" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Text="{Binding MgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="MemberInput" SelectedItem="{Binding Member, Mode=TwoWay}" ItemsSource="{Binding MemberSource, Mode=TwoWay}"
Grid.Column="1" Margin="53,10,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Button x:Name="MemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="MemberReferenceButton_Click"/>
<Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/>
<TextBox x:Name="MemberAddressField" Text="{Binding MemberAddress}"
Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22"
IsReadOnly="True" IsTabStop="False"/>
</Grid>
</GroupBox>
<GroupBox Header="Anmeldung" Grid.Row="1" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Leseplan:" Margin="10,10,0,0" Grid.Column="0"/>
<ComboBox x:Name="DeliveryScheduleInput" SelectedItem="{Binding DeliverySchedule, Mode=TwoWay}" ItemsSource="{Binding DeliveryScheduleSource, Mode=TwoWay}"
Grid.Column="1" Margin="0,10,10,10"
TextSearch.TextPath="Name"
SelectionChanged="DeliveryScheduleInput_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Date, StringFormat='dd.MM.'}" Width="35"/>
<TextBlock Text="{Binding ZwstId}" Width="30" TextAlignment="Center"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Content="Sorte:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="SortIdInput" Text="{Binding SortId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="36" Grid.Row="1" Grid.Column="1" Margin="0,40,0,0" HorizontalAlignment="Left"
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="WineVarietyInput" SelectedItem="{Binding WineVariety, Mode=TwoWay}" ItemsSource="{Binding WineVarietySource, Mode=TwoWay}"
Grid.Column="1" Margin="41,40,10,10"
TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp">
<ComboBox.ItemTemplateSelector>
<ctrl:WineVarietyTemplateSelector/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
<Label Content="Gewicht:" Margin="10,70,0,0" Grid.Column="0"/>
<ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Margin="0,70,10,10" Width="61" HorizontalAlignment="Left"
TextChanged="WeightInput_TextChanged" KeyUp="Input_KeyUp"/>
</Grid>
</GroupBox>
<Button x:Name="DeliveryScheduleButton" Content="Leseplanung"
Grid.Row="2" Margin="10,10,10,10" Width="120" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="DeliveryScheduleButton_Click"/>
</Grid>
<StatusBar Grid.Row="2" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
</StatusBarItem>
</StatusBar>
</Grid>
</local:AdministrationWindow>

View File

@ -0,0 +1,465 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using System.Linq;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
namespace Elwig.Windows {
public partial class DeliveryAncmtAdminWindow : AdministrationWindow {
public DeliveryAncmtAdminViewModel ViewModel => (DeliveryAncmtAdminViewModel)DataContext;
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
private readonly RoutedCommand CtrlP = new("CtrlP", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAncmtAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
public DeliveryAncmtAdminWindow() {
InitializeComponent();
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryAncmtList_ShowSelected_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryAncmtList_PrintSelected_Click));
ExemptInputs = [
SearchInput, SeasonInput, OnlyUpcomingInput, DeliveryScheduleList, DeliveryAncmtList,
];
RequiredInputs = [
MgNrInput, MemberInput, DeliveryScheduleInput, SortIdInput, WineVarietyInput, WeightInput,
];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason;
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
ViewModel.FilterOnlyUpcoming = true;
LockInputs();
}
private void Input_KeyUp(object sender, KeyEventArgs evt) {
if (sender is not Control ctrl) return;
if (evt.Key != Key.Enter) return;
if (ctrl == MgNrInput || ctrl == MemberInput) {
SortIdInput.Focus();
SortIdInput.SelectAll();
} else if (ctrl == SortIdInput || ctrl == WineVarietyInput) {
WeightInput.Focus();
WeightInput.SelectAll();
} else if (ctrl == WeightInput) {
SaveButton_Click(null, null);
}
}
private async void Menu_DeliveryAncmtList_SaveSelected_Click(object sender, RoutedEventArgs evt) {
if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
return;
await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.SaveList);
}
private async void Menu_DeliveryAncmtList_ShowSelected_Click(object sender, RoutedEventArgs evt) {
if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
return;
await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.Show);
}
private async void Menu_DeliveryAncmtList_SavePdfSelected_Click(object sender, RoutedEventArgs evt) {
if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
return;
await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.SavePdf);
}
private async void Menu_DeliveryAncmtList_PrintSelected_Click(object sender, RoutedEventArgs evt) {
if (DeliveryScheduleList.SelectedItem is not DeliverySchedule s)
return;
await ViewModel.GenerateDeliveryAncmtList(DeliveryAncmtService.ExportSubject.FromSelectedSchedule, ExportMode.Print);
}
private async Task RefreshDeliveryScheduleList() {
using var ctx = new AppDbContext();
var deliverySchedules = await ctx.DeliverySchedules
.Where(s => s.Year == ViewModel.FilterSeason)
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
.Include(s => s.Branch)
.Include(s => s.Announcements)
.OrderBy(s => s.DateString)
.ThenBy(s => s.Branch.Name)
.ThenBy(s => s.Description)
.ToListAsync();
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules, DeliveryScheduleList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
ControlUtils.RenewItemsSource(DeliveryScheduleInput, deliverySchedules, DeliveryScheduleInput_SelectionChanged);
}
private async Task RefreshList(bool updateSort = false) {
using var ctx = new AppDbContext();
var (_, deliveryAncmtQuery, filter) = await ViewModel.GetFilters(ctx);
var deliveryAncmts = await deliveryAncmtQuery
.Include(a => a.Member.BillingAddress)
.Include(a => a.Schedule)
.AsSplitQuery()
.ToListAsync();
if (filter.Count > 0 && deliveryAncmts.Count > 0) {
var dict = deliveryAncmts.AsParallel()
.ToDictionary(a => a, a => a.SearchScore(filter))
.OrderByDescending(a => a.Value)
.ThenBy(a => a.Key.Member.Name)
.ThenBy(a => a.Key.Member.GivenName)
.ThenBy(a => a.Key.Member.MgNr);
var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
deliveryAncmts = dict
.Where(a => a.Value > threshold)
.Select(a => a.Key)
.ToList();
} else {
deliveryAncmts = deliveryAncmts
.OrderBy(a => a.Member.Name)
.ThenBy(a => a.Member.GivenName)
.ThenBy(a => a.Member.MgNr)
.ToList();
}
ControlUtils.RenewItemsSource(DeliveryAncmtList, deliveryAncmts,
DeliveryAncmtList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
if (updateSort && DeliveryAncmtList.SelectedItem != null)
DeliveryAncmtList.ScrollIntoView(DeliveryAncmtList.SelectedItem);
}
private async Task RefreshInputs(bool validate = false) {
ClearInputStates();
if (ViewModel.SelectedDeliveryAncmt is DeliveryAncmt a) {
EditDeliveryAncmtButton.IsEnabled = true;
DeleteDeliveryAncmtButton.IsEnabled = true;
await FillInputs(a);
} else {
EditDeliveryAncmtButton.IsEnabled = false;
DeleteDeliveryAncmtButton.IsEnabled = false;
ClearOriginalValues();
ClearDefaultValues();
ClearInputs(validate);
ClearInputStates();
}
GC.Collect();
}
private void InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.InitInputs();
ValidateRequiredInputs();
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
.Where(m => m.IsActive || !IsCreating)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.Name)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr)
.ToListAsync());
ControlUtils.RenewItemsSource(WineVarietyInput, await ctx.WineVarieties.OrderBy(v => v.Name).ToListAsync());
await RefreshDeliveryScheduleList();
await RefreshList();
}
private async void DeliveryAncmtList_SelectionChanged(object sender, RoutedEventArgs evt) {
await RefreshInputs();
}
private async void DeliveryScheduleList_SelectionChanged(object sender, RoutedEventArgs evt) {
await RefreshList();
if (DeliveryScheduleList.SelectedItem is DeliverySchedule s) {
Menu_DeliveryAncmtList_SaveSelected.IsEnabled = !IsEditing && !IsCreating;
Menu_DeliveryAncmtList_ShowSelected.IsEnabled = !IsEditing && !IsCreating;
Menu_DeliveryAncmtList_SavePdfSelected.IsEnabled = !IsEditing && !IsCreating;
Menu_DeliveryAncmtList_PrintSelected.IsEnabled = !IsEditing && !IsCreating;
} else {
Menu_DeliveryAncmtList_SaveSelected.IsEnabled = false;
Menu_DeliveryAncmtList_ShowSelected.IsEnabled = false;
Menu_DeliveryAncmtList_SavePdfSelected.IsEnabled = false;
Menu_DeliveryAncmtList_PrintSelected.IsEnabled = false;
}
}
private void DeliveryScheduleInput_SelectionChanged(object sender, RoutedEventArgs evt) {
}
private async void OnlyUpcomingInput_Changed(object sender, RoutedEventArgs evt) {
await RefreshDeliveryScheduleList();
}
private async void SearchInput_TextChanged(object sender, TextChangedEventArgs evt) {
await RefreshList(true);
}
private async void SeasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (ViewModel.FilterSeason == null) return;
ViewModel.FilterOnlyUpcoming = false;
await RefreshDeliveryScheduleList();
await RefreshList();
}
private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
var text = MgNrInput.Text;
var caret = MgNrInput.CaretIndex;
ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
MgNrInput.Text = text;
MgNrInput.CaretIndex = caret;
}
private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
}
private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = MemberInput.SelectedItem as Member;
ViewModel.MgNr = m?.MgNr;
ViewModel.MemberAddress = m?.FullAddress;
}
protected override void ShortcutNew() {
if (!NewDeliveryAncmtButton.IsEnabled || NewDeliveryAncmtButton.Visibility != Visibility.Visible)
return;
NewDeliveryAncmtButton_Click(null, null);
}
private async void NewDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true;
DeliveryAncmtList.IsEnabled = false;
ViewModel.SelectedDeliveryAncmt = null;
using var ctx = new AppDbContext();
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
.Where(m => m.IsActive || !IsCreating)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.Name)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr)
.ToListAsync());
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
InitInputs();
ViewModel.EnableSearchInputs = false;
}
protected override void ShortcutEdit() {
if (!EditDeliveryAncmtButton.IsEnabled || EditDeliveryAncmtButton.Visibility != Visibility.Visible)
return;
EditDeliveryAncmtButton_Click(null, null);
}
private void EditDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliveryAncmt == null) return;
IsEditing = true;
DeliveryAncmtList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
ViewModel.EnableSearchInputs = false;
}
protected override void ShortcutDelete() {
if (!DeleteDeliveryAncmtButton.IsEnabled || DeleteDeliveryAncmtButton.Visibility != Visibility.Visible)
return;
DeleteDeliveryAncmtButton_Click(null, null);
}
private async void DeleteDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliveryAncmt is not DeliveryAncmt a)
return;
var r = MessageBox.Show(
$"Soll die Traubenanmeldung wirklich unwiderruflich gelöscht werden?",
"Traubenanmeldung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using (var ctx = new AppDbContext()) {
ctx.Remove(a);
await ctx.SaveChangesAsync();
}
await App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Traubenanmeldung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
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.AppStarting;
int year = -1, dsnr = -1, mgnr = -1;
string? sortid = null;
try {
var s = ViewModel.SelectedDeliveryAncmt;
(year, dsnr, mgnr, sortid) = await ViewModel.UpdateDeliveryAncmt(s?.Year, s?.DsNr, s?.MgNr, s?.SortId, s?.Type);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Traubenanmeldung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
IsEditing = false;
IsCreating = false;
DeliveryAncmtList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
ViewModel.EnableSearchInputs = true;
FinishInputFilling();
await RefreshList();
await RefreshInputs();
ViewModel.SearchQuery = "";
if (sortid != null)
ControlUtils.SelectItemWithPk(DeliveryAncmtList, year, dsnr, mgnr, sortid);
}
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) {
await RefreshInputs();
} else if (IsCreating) {
ClearInputs();
InitInputs();
}
UpdateButtons();
}
private async void CancelButton_Click(object? sender, RoutedEventArgs? evt) {
IsEditing = false;
IsCreating = false;
DeliveryAncmtList.IsEnabled = true;
using var ctx = new AppDbContext();
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
.Where(m => m.IsActive || !IsCreating)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.OrderBy(m => m.Name)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr)
.ToListAsync());
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
await RefreshInputs();
LockInputs();
ViewModel.EnableSearchInputs = true;
}
private async Task FillInputs(DeliveryAncmt a) {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.FillInputs(a);
FinishInputFilling();
}
private void UpdateWineVariety(bool valid) {
if (valid) {
ViewModel.SortId = ViewModel.SortId![0..2];
ControlUtils.SelectItemWithPk(WineVarietyInput, ViewModel.SortId);
} else {
WineVarietyInput.SelectedItem = null;
}
}
private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
UpdateWineVariety(InputTextChanged((TextBox)sender, Validator.CheckSortId));
}
private void SortIdInput_LostFocus(object sender, RoutedEventArgs evt) {
UpdateWineVariety(InputLostFocus((TextBox)sender, Validator.CheckSortId));
}
private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
if (WineVarietyInput.SelectedItem is WineVar s)
ViewModel.SortId = s.SortId;
}
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
base.ClearInputs(validate);
}
protected override void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch;
}
private void FocusSearchInput(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) {
SearchInput.Focus();
SearchInput.SelectAll();
}
}
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() {
NewDeliveryAncmtButton.IsEnabled = true;
EditDeliveryAncmtButton.IsEnabled = ViewModel.SelectedDeliveryAncmt != null;
DeleteDeliveryAncmtButton.IsEnabled = ViewModel.SelectedDeliveryAncmt != null;
ViewModel.ControlButtonsVisibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewDeliveryAncmtButton.IsEnabled = false;
EditDeliveryAncmtButton.IsEnabled = false;
DeleteDeliveryAncmtButton.IsEnabled = false;
ViewModel.ControlButtonsVisibility = Visibility.Hidden;
}
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckInteger((TextBox)sender, false, 5));
}
private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (MemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
private void DeliveryScheduleButton_Click(object sender, RoutedEventArgs evt) {
App.FocusDeliverySchedule();
}
}
}

View File

@ -0,0 +1,284 @@
<local:AdministrationWindow
x:Class="Elwig.Windows.DeliveryScheduleAdminWindow"
AutomationProperties.AutomationId="DeliveryScheduleAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Leseplanung - Elwig" Height="600" Width="850" MinHeight="450" MinWidth="800"
Loaded="Window_Loaded">
<Window.DataContext>
<vm:DeliveryScheduleAdminViewModel/>
</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="ctrl:CheckComboBox">
<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.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="350"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*" MinWidth="400"/>
</Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
</Menu>
<Grid Grid.Row="1" Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding EnableSearchInputs}"
Grid.ColumnSpan="3" Margin="5,5,174,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
<Bold>Strg+F</Bold><LineBreak/><LineBreak/>
Lesepläne filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
<LineBreak/>
Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/>
<Bold>Rot/Weiß</Bold>: z.B. r, Rot, w, weiß, ...<LineBreak/>
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
<Bold>Beschreibung</Bold>: z.B. Bio, ...
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<ctrl:IntegerUpDown x:Name="SeasonInput" Text="{Binding FilterSeasonString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding EnableSearchInputs}"
Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1900" Maximum="9999"
Margin="5,5,113,0" VerticalAlignment="Top" HorizontalAlignment="Right"
TextChanged="SeasonInput_TextChanged"/>
<CheckBox x:Name="OnlyUpcomingInput" Content="Nur zukünftige" IsChecked="{Binding FilterOnlyUpcoming, Mode=TwoWay}" IsEnabled="{Binding EnableSearchInputs}"
HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="3"
Checked="OnlyUpcomingInput_Changed" Unchecked="OnlyUpcomingInput_Changed"/>
<DataGrid x:Name="DeliveryScheduleList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
ItemsSource="{Binding DeliverySchedules, Mode=TwoWay}" SelectedItem="{Binding SelectedDeliverySchedule, Mode=TwoWay}"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="DeliveryScheduleList_SelectionChanged"
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns>
<DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat='dd.MM.yy'}" Width="70">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Z." Binding="{Binding ZwstId}" Width="25">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="200"/>
<DataGridTextColumn Header="Max. Gew." Binding="{Binding MaxWeight, StringFormat='{}{0:N0} kg'}" Width="80">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewDeliveryScheduleButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="NewDeliveryScheduleButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditDeliveryScheduleButton" 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="EditDeliveryScheduleButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteDeliveryScheduleButton" 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="DeleteDeliveryScheduleButton_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" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="1.5*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Header="Leseplan" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Datum/Zwst.:" Margin="10,10,0,10"/>
<TextBox x:Name="DateInput" Text="{Binding DateString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,10" Width="77" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name"
Margin="82,10,10,10" Width="150" Grid.Column="1" HorizontalAlignment="Left"/>
<Label Content="Beschreibung:" Margin="10,40,0,10"/>
<TextBox x:Name="DescriptionInput" Text="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,10" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<Label Content="Max. Gewicht:" Margin="10,70,0,10"/>
<ctrl:UnitTextBox x:Name="MaxWeightInput" Unit="kg" Text="{Binding MaxWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,10" Grid.Column="1" Width="68" HorizontalAlignment="Left"
TextChanged="MaxWeightInput_TextChanged"/>
<Label Content="Sorten:" Margin="10,100,0,10"/>
<ctrl:CheckComboBox x:Name="MainWineVarietiesInput" SelectedItems="{Binding MainVarieties}" ItemsSource="{Binding MainVarietiesSource, Mode=TwoWay}"
Grid.Column="1" Margin="0,100,10,10"
Delimiter=", " AllItemsSelectedContent="Alle Sorten" ListDisplayMemberPath="SortId" TextSearch.TextPath="Name">
<ctrl:CheckComboBox.ItemTemplateSelector>
<ctrl:WineVarietyTemplateSelector/>
</ctrl:CheckComboBox.ItemTemplateSelector>
</ctrl:CheckComboBox>
<Label Content="Weitere Sorten:" Margin="10,130,0,10"/>
<ctrl:CheckComboBox x:Name="OtherWineVarietiesInput" SelectedItems="{Binding OtherVarieties}" ItemsSource="{Binding OtherVarietiesSource, Mode=TwoWay}"
Grid.Column="1" Margin="0,130,10,10"
Delimiter=", " AllItemsSelectedContent="Alle Sorten" ListDisplayMemberPath="SortId" TextSearch.TextPath="Name">
<ctrl:CheckComboBox.ItemTemplateSelector>
<ctrl:WineVarietyTemplateSelector/>
</ctrl:CheckComboBox.ItemTemplateSelector>
</ctrl:CheckComboBox>
</Grid>
</GroupBox>
<GroupBox Header="Anmeldungen" Margin="5,5,5,5" Grid.Row="1" Width="249" Height="98"
VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="ab frühestens:" Margin="10,10,0,10"/>
<TextBox x:Name="AncmtFromDateInput" Text="{Binding AncmtFromDateString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="77" Margin="0,10,10,10" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<TextBox x:Name="AncmtFromTimeInput" Text="{Binding AncmtFromTimeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="44" Margin="83,10,10,10" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="TimeInput_TextChanged" LostFocus="TimeInput_LostFocus"/>
<Label Content="bis spätestens:" Margin="10,40,0,10"/>
<TextBox x:Name="AncmtToDateInput" Text="{Binding AncmtToDateString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="77" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<TextBox x:Name="AncmtToTimeInput" Text="{Binding AncmtToTimeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="44" Margin="83,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="TimeInput_TextChanged" LostFocus="TimeInput_LostFocus"/>
</Grid>
</GroupBox>
</Grid>
<StatusBar Grid.Row="2" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
</StatusBarItem>
</StatusBar>
</Grid>
</local:AdministrationWindow>

View File

@ -0,0 +1,311 @@
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 DeliveryScheduleAdminWindow : AdministrationWindow {
public DeliveryScheduleAdminViewModel ViewModel => (DeliveryScheduleAdminViewModel)DataContext;
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryScheduleAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
public DeliveryScheduleAdminWindow() {
InitializeComponent();
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
ExemptInputs = [
SearchInput, SeasonInput, OnlyUpcomingInput, DeliveryScheduleList,
];
RequiredInputs = [
DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput,
];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason;
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
ViewModel.FilterOnlyUpcoming = true;
LockInputs();
}
private async Task RefreshList(bool updateSort = false) {
using var ctx = new AppDbContext();
var (_, deliveryScheduleQuery, filter) = await ViewModel.GetFilters(ctx);
var deliverySchedules = await deliveryScheduleQuery
.Include(s => s.Varieties)
.Include(s => s.Branch)
.AsSplitQuery()
.ToListAsync();
if (filter.Count > 0 && deliverySchedules.Count > 0) {
var dict = deliverySchedules.AsParallel()
.ToDictionary(s => s, s => s.SearchScore(filter))
.OrderByDescending(a => a.Value)
.ThenBy(a => a.Key.DateString)
.ThenBy(a => a.Key.Branch.Name)
.ThenBy(a => a.Key.Description);
var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
deliverySchedules = dict
.Where(a => a.Value > threshold)
.Select(a => a.Key)
.ToList();
} else {
deliverySchedules = deliverySchedules
.OrderBy(s => s.DateString)
.ThenBy(s => s.Branch.Name)
.ThenBy(s => s.Description)
.ToList();
}
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules,
DeliveryScheduleList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
if (updateSort && DeliveryScheduleList.SelectedItem != null)
DeliveryScheduleList.ScrollIntoView(DeliveryScheduleList.SelectedItem);
}
private async Task RefreshInputs(bool validate = false) {
ClearInputStates();
if (ViewModel.SelectedDeliverySchedule is DeliverySchedule s) {
EditDeliveryScheduleButton.IsEnabled = true;
DeleteDeliveryScheduleButton.IsEnabled = true;
await FillInputs(s);
} else {
EditDeliveryScheduleButton.IsEnabled = false;
DeleteDeliveryScheduleButton.IsEnabled = false;
ClearOriginalValues();
ClearDefaultValues();
ClearInputs(validate);
ClearInputStates();
}
GC.Collect();
}
private void InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.InitInputs();
ValidateRequiredInputs();
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
var varieties = await ctx.WineVarieties.OrderBy(v => v.Name).ToListAsync();
ControlUtils.RenewItemsSource(MainWineVarietiesInput, varieties);
ControlUtils.RenewItemsSource(OtherWineVarietiesInput, varieties);
await RefreshList();
}
private async void DeliveryScheduleList_SelectionChanged(object sender, RoutedEventArgs evt) {
await RefreshInputs();
}
private async void OnlyUpcomingInput_Changed(object sender, RoutedEventArgs evt) {
await RefreshList();
}
private async void SearchInput_TextChanged(object sender, TextChangedEventArgs evt) {
await RefreshList(true);
}
private async void SeasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (ViewModel.FilterSeason == null) return;
ViewModel.FilterOnlyUpcoming = false;
await RefreshList();
}
protected override void ShortcutNew() {
if (!NewDeliveryScheduleButton.IsEnabled || NewDeliveryScheduleButton.Visibility != Visibility.Visible)
return;
NewDeliveryScheduleButton_Click(null, null);
}
private void NewDeliveryScheduleButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true;
DeliveryScheduleList.IsEnabled = false;
ViewModel.SelectedDeliverySchedule = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
InitInputs();
ViewModel.EnableSearchInputs = false;
}
protected override void ShortcutEdit() {
if (!EditDeliveryScheduleButton.IsEnabled || EditDeliveryScheduleButton.Visibility != Visibility.Visible)
return;
EditDeliveryScheduleButton_Click(null, null);
}
private void EditDeliveryScheduleButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliverySchedule == null) return;
IsEditing = true;
DeliveryScheduleList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
ViewModel.EnableSearchInputs = false;
}
protected override void ShortcutDelete() {
if (!DeleteDeliveryScheduleButton.IsEnabled || DeleteDeliveryScheduleButton.Visibility != Visibility.Visible)
return;
DeleteDeliveryScheduleButton_Click(null, null);
}
private async void DeleteDeliveryScheduleButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliverySchedule is not DeliverySchedule s)
return;
var r = MessageBox.Show(
$"Soll der Leseplan \"{s.Description}\" vom {s.Date:dd.MM.yyyy} wirklich unwiderruflich gelöscht werden?",
"Leseplan löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using (var ctx = new AppDbContext()) {
ctx.Remove(s);
await ctx.SaveChangesAsync();
}
await App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
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.AppStarting;
try {
await ViewModel.UpdateDeliverySchedule(ViewModel.SelectedDeliverySchedule?.Year, ViewModel.SelectedDeliverySchedule?.DsNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
IsEditing = false;
IsCreating = false;
DeliveryScheduleList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
ViewModel.EnableSearchInputs = true;
FinishInputFilling();
await RefreshList();
await RefreshInputs();
ViewModel.SearchQuery = "";
}
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) {
await RefreshInputs();
} else if (IsCreating) {
ClearInputs();
InitInputs();
}
UpdateButtons();
}
private async void CancelButton_Click(object? sender, RoutedEventArgs? evt) {
IsEditing = false;
IsCreating = false;
DeliveryScheduleList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
await RefreshInputs();
LockInputs();
ViewModel.EnableSearchInputs = true;
}
private async Task FillInputs(DeliverySchedule s) {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.FillInputs(s);
FinishInputFilling();
}
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
base.ClearInputs(validate);
}
protected override void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch;
}
private void FocusSearchInput(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) {
SearchInput.Focus();
SearchInput.SelectAll();
}
}
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() {
NewDeliveryScheduleButton.IsEnabled = true;
EditDeliveryScheduleButton.IsEnabled = ViewModel.SelectedDeliverySchedule != null;
DeleteDeliveryScheduleButton.IsEnabled = ViewModel.SelectedDeliverySchedule != null;
ViewModel.ControlButtonsVisibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewDeliveryScheduleButton.IsEnabled = false;
EditDeliveryScheduleButton.IsEnabled = false;
DeleteDeliveryScheduleButton.IsEnabled = false;
ViewModel.ControlButtonsVisibility = Visibility.Hidden;
}
private new void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
base.DateInput_TextChanged(sender, evt);
if (ViewModel.Date is DateOnly date) {
ViewModel.AncmtToDate = date.AddDays(-2);
}
}
private void MaxWeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckInteger((TextBox)sender, false, 6));
}
}
}

View File

@ -166,7 +166,7 @@ namespace Elwig.Windows {
} }
ControlUtils.RenewItemsSource(MemberCustomInput, await ctx.Members ControlUtils.RenewItemsSource(MemberCustomInput, await ctx.Members
.Where(m => m.IsActive) .Where(m => m.IsActive)
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.Include(m => m.Branch) .Include(m => m.Branch)
.Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.DefaultWbKg!.AtKg)
@ -438,7 +438,7 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} else if (OrderNameInput.IsChecked == true) { } else if (OrderNameInput.IsChecked == true) {
recipients = recipients recipients = recipients
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToList(); .ToList();
@ -447,7 +447,7 @@ namespace Elwig.Windows {
recipients = recipients recipients = recipients
.OrderBy(m => m.BillingAddress?.PostalDest.AtPlz?.Plz ?? m.PostalDest.AtPlz?.Plz) .OrderBy(m => m.BillingAddress?.PostalDest.AtPlz?.Plz ?? m.PostalDest.AtPlz?.Plz)
.ThenBy(m => m.BillingAddress?.PostalDest.AtPlz?.Ort.Name ?? m.PostalDest.AtPlz?.Ort.Name) .ThenBy(m => m.BillingAddress?.PostalDest.AtPlz?.Ort.Name ?? m.PostalDest.AtPlz?.Ort.Name)
.ThenBy(m => m.FamilyName) .ThenBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToList(); .ToList();
@ -455,7 +455,7 @@ namespace Elwig.Windows {
recipients = recipients recipients = recipients
.OrderBy(m => m.PostalDest.AtPlz?.Plz) .OrderBy(m => m.PostalDest.AtPlz?.Plz)
.ThenBy(m => m.PostalDest.AtPlz?.Ort.Name) .ThenBy(m => m.PostalDest.AtPlz?.Ort.Name)
.ThenBy(m => m.FamilyName) .ThenBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToList(); .ToList();

View File

@ -64,7 +64,7 @@
Margin="205,210,0,0"/> Margin="205,210,0,0"/>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click" <Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
Margin="0,250,205,0"/> Margin="0,250,205,0"/>
<Button x:Name="RegistrationButton" Content="Anmeldungen" IsEnabled="False" <Button x:Name="DeliveryAncmtButton" Content="Anmeldungen" Click="DeliveryAncmtButton_Click"
Margin="205,250,0,0"/> Margin="205,250,0,0"/>
<Button x:Name="DownloadButton" Click="DownloadButton_Click" <Button x:Name="DownloadButton" Click="DownloadButton_Click"

View File

@ -213,6 +213,10 @@ namespace Elwig.Windows {
w.Show(); w.Show();
} }
private void DeliveryAncmtButton_Click(object sender, RoutedEventArgs evt) {
App.FocusDeliveryAncmt();
}
private void BaseDataButton_Click(object sender, RoutedEventArgs evt) { private void BaseDataButton_Click(object sender, RoutedEventArgs evt) {
App.FocusBaseData(); App.FocusBaseData();
} }

View File

@ -199,7 +199,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="140"/> <DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="140"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/> <DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40"> <DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
@ -208,7 +208,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Rechnungsadresse" Binding="{Binding BillingAddress.Name}" Width="200"/> <DataGridTextColumn Header="Rechnungsadresse" Binding="{Binding BillingAddress.FullName}" Width="200"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
@ -285,6 +285,9 @@
<TextBox x:Name="MgNrInput" Text="{Binding MgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="MgNrInput" Text="{Binding MgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/> TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/>
<CheckBox x:Name="JuridicalPersonInput" Content="Juristische Person" IsChecked="{Binding IsJuridicalPerson}"
Checked="JuridicalPersonInput_Changed" Unchecked="JuridicalPersonInput_Changed"
Margin="53,15,0,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/> <Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/>
<TextBox x:Name="PredecessorMgNrInput" Text="{Binding PredecessorMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="PredecessorMgNrInput" Text="{Binding PredecessorMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
@ -294,27 +297,33 @@
Margin="53,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" IsEnabled="{Binding EnableMemberReferenceButton}" ToolTip="Zu Vorgänger springen" Margin="53,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" IsEnabled="{Binding EnableMemberReferenceButton}" ToolTip="Zu Vorgänger springen"
Click="MemberReferenceButton_Click"/> Click="MemberReferenceButton_Click"/>
<Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/> <Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2" Visibility="{Binding PersonalNameVisibility}"/>
<TextBox x:Name="PrefixInput" Text="{Binding Prefix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="PrefixInput" Text="{Binding Prefix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PersonalNameVisibility}"
Margin="0,40,10,0" Grid.Column="3" Margin="0,40,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Name:" Margin="10,40,0,0" Grid.Column="0" Visibility="{Binding JuridicalNameVisibility}"/>
<TextBox x:Name="GivenNameInput" Text="{Binding GivenName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0" Visibility="{Binding PersonalNameVisibility}"/>
<TextBox x:Name="GivenNameInput" Text="{Binding GivenName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PersonalNameVisibility}"
Margin="0,40,0,0" Grid.Column="1" Margin="0,40,0,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0" Visibility="{Binding PersonalNameVisibility}"/>
<TextBox x:Name="FamilyNameInput" Text="{Binding FamilyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="NameInput" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,0,0" Grid.Column="1" Margin="0,70,0,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2"/> <Label Content="Zu Handen:" Margin="10,70,0,0" Grid.Column="0" Visibility="{Binding JuridicalNameVisibility}"/>
<TextBox x:Name="SuffixInput" Text="{Binding Suffix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="AttnInput" Text="{Binding ForTheAttentionOf, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding JuridicalNameVisibility}"
Margin="0,70,10,0" Grid.Column="1" Grid.ColumnSpan="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2" Visibility="{Binding PersonalNameVisibility}"/>
<TextBox x:Name="SuffixInput" Text="{Binding Suffix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PersonalNameVisibility}"
Margin="0,70,10,0" Grid.Column="3" Margin="0,70,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/> <Label x:Name="BirthdayLabel" Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="BirthdayInput" Text="{Binding Birthday, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="BirthdayInput" Text="{Binding Birthday, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left" Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="PartialDateInput_TextChanged" LostFocus="PartialDateInput_LostFocus"/> TextChanged="PartialDateInput_TextChanged" LostFocus="PartialDateInput_LostFocus"/>
@ -637,7 +646,7 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="2" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel> <StatusBar.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<Grid> <Grid>

View File

@ -41,7 +41,7 @@ namespace Elwig.Windows {
SearchInput, ActiveMemberInput, MemberList, SearchInput, ActiveMemberInput, MemberList,
]; ];
RequiredInputs = [ RequiredInputs = [
MgNrInput, GivenNameInput, FamilyNameInput, MgNrInput, GivenNameInput, NameInput,
AddressInput, PlzInput, OrtInput, BillingOrtInput, AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput BusinessSharesInput, BranchInput, DefaultKgInput
]; ];
@ -130,8 +130,9 @@ namespace Elwig.Windows {
var dict = members.AsParallel() var dict = members.AsParallel()
.ToDictionary(m => m, m => m.SearchScore(filter)) .ToDictionary(m => m, m => m.SearchScore(filter))
.OrderByDescending(a => a.Value) .OrderByDescending(a => a.Value)
.ThenBy(a => a.Key.FamilyName) .ThenBy(a => a.Key.Name)
.ThenBy(a => a.Key.GivenName); .ThenBy(a => a.Key.GivenName)
.ThenBy(a => a.Key.MgNr);
var threshold = dict.Select(a => a.Value).Max() * 3 / 4; var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
members = dict members = dict
.Where(a => a.Value > threshold) .Where(a => a.Value > threshold)
@ -139,8 +140,9 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} else { } else {
members = members members = members
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr)
.ToList(); .ToList();
} }
@ -167,7 +169,7 @@ namespace Elwig.Windows {
GC.Collect(); GC.Collect();
} }
private async void InitInputs() { private async Task InitInputs() {
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
await ViewModel.InitInputs(); await ViewModel.InitInputs();
@ -301,7 +303,7 @@ namespace Elwig.Windows {
NewMemberButton_Click(null, null); NewMemberButton_Click(null, null);
} }
private void NewMemberButton_Click(object? sender, RoutedEventArgs? evt) { private async void NewMemberButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true; IsCreating = true;
MemberList.IsEnabled = false; MemberList.IsEnabled = false;
ViewModel.SelectedMember = null; ViewModel.SelectedMember = null;
@ -311,7 +313,7 @@ namespace Elwig.Windows {
ShowSaveResetCancelButtons(); ShowSaveResetCancelButtons();
UnlockInputs(); UnlockInputs();
UpdateContactInfoVisibility(true); UpdateContactInfoVisibility(true);
InitInputs(); await InitInputs();
ViewModel.EnableSearchInputs = false; ViewModel.EnableSearchInputs = false;
} }
@ -425,7 +427,7 @@ namespace Elwig.Windows {
await RefreshInputs(); await RefreshInputs();
} else if (IsCreating) { } else if (IsCreating) {
ClearInputs(); ClearInputs();
InitInputs(); await InitInputs();
} }
UpdateButtons(); UpdateButtons();
} }
@ -726,7 +728,7 @@ namespace Elwig.Windows {
private async void PredecessorMgNrInput_LostFocus(object sender, RoutedEventArgs evt) { private async void PredecessorMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
var valid = InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr); var valid = InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr);
if (valid && ViewModel.PredecessorMgNr is int mgnr && (IsEditing || IsCreating)) { if (valid && ViewModel.PredecessorMgNr is int mgnr && (IsEditing || IsCreating)) {
if (MemberList.SelectedItem is not Member m || m.MgNr == mgnr) if (ViewModel.MgNr == mgnr)
return; return;
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var areaComs = await ctx.AreaCommitments var areaComs = await ctx.AreaCommitments
@ -736,11 +738,16 @@ namespace Elwig.Windows {
return; return;
var oldMember = (await ctx.Members.FindAsync(mgnr))!; var oldMember = (await ctx.Members.FindAsync(mgnr))!;
var d = new AreaComDialog(oldMember.AdministrativeName, m.AdministrativeName, areaComs.Count, areaComs.Sum(c => c.Area)); var newName = $"{ViewModel.Name?.Replace('ß', 'ẞ').ToUpper()} " +
$"{ViewModel.Prefix}{(!string.IsNullOrEmpty(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrEmpty(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrEmpty(ViewModel.Suffix) ? " " : "")}";
var d = new AreaComDialog(oldMember.AdministrativeName, newName, areaComs.Count, areaComs.Sum(c => c.Area));
if (d.ShowDialog() != true) if (d.ShowDialog() != true)
return; return;
ViewModel.TransferPredecessorAreaComs = d.SuccessorSeason; ViewModel.TransferPredecessorAreaComs = d.SuccessorSeason;
SetOriginalValue(PredecessorMgNrInput, -1); // hack to allow user to save if (IsEditing)
SetOriginalValue(PredecessorMgNrInput, -1); // hack to allow user to save
UpdateButtons(); UpdateButtons();
} }
} }
@ -775,7 +782,7 @@ namespace Elwig.Windows {
if (ViewModel.SelectedMember is not Member m) return; if (ViewModel.SelectedMember is not Member m) return;
var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" + var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
//$"CustomerNumber={m.LfbisNr}&" + //$"CustomerNumber={m.LfbisNr}&" +
$"Name={(m.BillingAddress?.Name ?? m.Name).Replace(' ', '+')}&" + $"Name={(m.BillingAddress?.FullName ?? m.FullName).Replace(' ', '+')}&" +
$"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}"; $"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}";
Process.Start(new ProcessStartInfo(url) { Process.Start(new ProcessStartInfo(url) {
UseShellExecute = true, UseShellExecute = true,
@ -804,5 +811,26 @@ namespace Elwig.Windows {
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);
} }
private void JuridicalPersonInput_Changed(object sender, RoutedEventArgs evt) {
CheckBox_Changed(sender, evt);
if (ViewModel.IsJuridicalPerson) {
ViewModel.PersonalNameVisibility = Visibility.Hidden;
ViewModel.JuridicalNameVisibility = Visibility.Visible;
NameInput.Margin = new(0, 40, 10, 0);
NameInput.SetValue(Grid.ColumnSpanProperty, 3);
BirthdayLabel.Content = "Gründung:";
DeceasedInput.Content = "Aufgelöst";
} else {
ViewModel.JuridicalNameVisibility = Visibility.Hidden;
ViewModel.PersonalNameVisibility = Visibility.Visible;
NameInput.Margin = new(0, 70, 0, 0);
NameInput.SetValue(Grid.ColumnSpanProperty, 1);
BirthdayLabel.Content = "Geburtstag:";
DeceasedInput.Content = "Verstorben";
}
ValidateRequiredInputs();
UpdateButtons();
}
} }
} }

View File

@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Auszahlung anpassen - Elwig" Height="600" Width="1000" MinHeight="400" MinWidth="850"> Title="Auszahlung anpassen - Elwig" Height="600" Width="1000" MinHeight="560" MinWidth="850">
<Window.Resources> <Window.Resources>
<ctrl:UnitConverter x:Key="CurrencyConverter" Precision="2" Unit="€"/> <ctrl:UnitConverter x:Key="CurrencyConverter" Precision="2" Unit="€"/>
<ctrl:UnitConverter x:Key="WeightConverter" Precision="0" Unit="kg"/> <ctrl:UnitConverter x:Key="WeightConverter" Precision="0" Unit="kg"/>
@ -74,7 +74,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="100"/> <DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="100"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="90"/> <DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="90"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0:N0} '}" Width="35"> <DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0:N0} '}" Width="35">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
@ -83,6 +83,20 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Abs." Binding="{Binding ModAbs, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Rel." Binding="{Binding ModRel, StringFormat='{}{0:P2} '}" Width="65">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Ü.-/U.-Lfrg." Binding="{Binding OverUnder, Converter={StaticResource WeightConverter},StringFormat='{}{0} '}" Width="70"> <DataGridTextColumn Header="Ü.-/U.-Lfrg." Binding="{Binding OverUnder, Converter={StaticResource WeightConverter},StringFormat='{}{0} '}" Width="70">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
@ -118,7 +132,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Weitere" Binding="{Binding Custom, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65"> <DataGridTextColumn Header="Weitere" Binding="{Binding CustomAmount, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -139,7 +153,7 @@
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/> <GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2" Grid.Row="1"> <Grid Grid.Column="2" Grid.Row="1">
<GroupBox Header="Benutzerdefinierte Zu-/Abschläge" Margin="5,10,10,10" Height="180" Width="365" <GroupBox Header="Benutzerdefinierte Zu-/Abschläge" Margin="5,10,10,10" Height="270" Width="365"
VerticalAlignment="Top" HorizontalAlignment="Left"> VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid> <Grid>
<Label Content="Mitglied:" Margin="10,10,10,10"/> <Label Content="Mitglied:" Margin="10,10,10,10"/>
@ -153,12 +167,23 @@
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen" Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="MemberReferenceButton_Click"/> Click="MemberReferenceButton_Click"/>
<Label Content="Betrag:" Margin="10,40,10,10"/> <Label Content="Vor MwSt." Margin="70,37.5,10,10"/>
<ctrl:UnitTextBox x:Name="CustomAmountInput" Width="80" Margin="70,40,10,10" Unit="€" <Label Content="Betrag:" Margin="10,60,10,10"/>
<ctrl:UnitTextBox x:Name="CustomModAbsInput" Width="80" Margin="70,60,10,10" Unit="€"
TextChanged="CustomModAbsInput_TextChanged"/>
<ctrl:UnitTextBox x:Name="CustomModRelInput" Width="60" Margin="155,60,10,10" Unit="%"
TextChanged="CustomModRelInput_TextChanged"/>
<Label Content="Freitext:" Margin="10,90,10,10"/>
<TextBox x:Name="CustomModCommentInput" Margin="70,90,10,10"/>
<Label Content="Nach MwSt." Margin="70,117.5,10,10"/>
<Label Content="Betrag:" Margin="10,140,10,10"/>
<ctrl:UnitTextBox x:Name="CustomAmountInput" Width="80" Margin="70,140,10,10" Unit="€"
TextChanged="CustomAmountInput_TextChanged"/> TextChanged="CustomAmountInput_TextChanged"/>
<Label Content="Freitext:" Margin="10,70,10,10"/> <Label Content="Freitext:" Margin="10,170,10,10"/>
<TextBox x:Name="CustomCommentInput" Margin="70,70,10,10"/> <TextBox x:Name="CustomCommentInput" Margin="70,170,10,10"/>
<Button x:Name="SaveCustomButton" Content="Speichern" Margin="0,0,125,10" Width="120" <Button x:Name="SaveCustomButton" Content="Speichern" Margin="0,0,125,10" Width="120"
HorizontalAlignment="Center" VerticalAlignment="Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom"

View File

@ -45,12 +45,12 @@ namespace Elwig.Windows {
var members = await ctx.Members var members = await ctx.Members
.Select(m => new { .Select(m => new {
m.MgNr, m.MgNr,
m.FamilyName, m.Name,
m.GivenName, m.GivenName,
m.BusinessShares, m.BusinessShares,
m.IsActive, m.IsActive,
}) })
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToListAsync(); .ToListAsync();
@ -70,7 +70,7 @@ namespace Elwig.Windows {
var list = members var list = members
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.IsActive, m.IsActive,
BusinessShares = m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0), BusinessShares = m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0),
DeliveryObligation = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerBusinessShare, DeliveryObligation = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerBusinessShare,
@ -78,7 +78,7 @@ namespace Elwig.Windows {
Adjust = history.TryGetValue(m.MgNr, out int v2) ? (int?)v2 : null, Adjust = history.TryGetValue(m.MgNr, out int v2) ? (int?)v2 : null,
}) })
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.BusinessShares,
Weight = weight.GetValueOrDefault(m.MgNr, 0), Weight = weight.GetValueOrDefault(m.MgNr, 0),
OverUnder = weight.TryGetValue(m.MgNr, out int v1) ? OverUnder = weight.TryGetValue(m.MgNr, out int v1) ?
@ -89,7 +89,7 @@ namespace Elwig.Windows {
AdjustAmount = m.Adjust * -season.BusinessShareValue, AdjustAmount = m.Adjust * -season.BusinessShareValue,
}) })
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder, m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.OverUnder != null && m.OverUnder < 0 ? PenaltyBs = m.OverUnder != null && m.OverUnder < 0 ?
(season.PenaltyPerKg * m.OverUnder ?? 0) + (season.PenaltyPerKg * m.OverUnder ?? 0) +
@ -103,34 +103,36 @@ namespace Elwig.Windows {
}).Sum() : (decimal?)null, }).Sum() : (decimal?)null,
m.Adjust, m.Adjust,
m.AdjustAmount, m.AdjustAmount,
Custom = CustomPayments!.GetValueOrDefault(m.MgNr, null)?.Amount, Custom = CustomPayments!.GetValueOrDefault(m.MgNr, null),
}) })
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder, m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.PenaltyBs == null || m.PenaltyBs == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyBs, 2), PenaltyBs = m.PenaltyBs == null || m.PenaltyBs == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyBs, 2),
PenaltyAc = m.PenaltyAc == null ? (decimal?)null : Math.Round((decimal)m.PenaltyAc, 2), PenaltyAc = m.PenaltyAc == null ? (decimal?)null : Math.Round((decimal)m.PenaltyAc, 2),
m.Adjust, m.Adjust,
AdjustAmount = m.AdjustAmount == null ? (decimal?)null : Math.Round((decimal)m.AdjustAmount, 2), AdjustAmount = m.AdjustAmount == null ? (decimal?)null : Math.Round((decimal)m.AdjustAmount, 2),
m.Custom CustomAmount = m.Custom?.Amount,
ModAbs = m.Custom?.ModAbs,
ModRel = m.Custom?.ModRel,
}) })
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder, m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.Custom, m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
Total = (m.PenaltyBs ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.Custom ?? 0), Total = (m.PenaltyBs ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.CustomAmount ?? 0),
}) })
.Select(m => new { .Select(m => new {
m.MgNr, m.FamilyName, m.GivenName, m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder, m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.Custom, m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
m.Total, m.Total,
Background = m.Weight == 0 ? Brushes.Orange : m.Weight / 2 < -m.Total ? Brushes.Red : Brushes.White, Background = m.Weight == 0 ? Brushes.Orange : m.Weight / 2 < -m.Total ? Brushes.Red : Brushes.White,
Foreground = m.Total == 0 ? Brushes.Gray : Brushes.Black, Foreground = m.Total == 0 ? Brushes.Gray : Brushes.Black,
}) })
.Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyBs != null || m.PenaltyAc != null || m.Custom != null) .Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyBs != null || m.PenaltyAc != null || m.CustomAmount != null || m.ModAbs != null || m.ModRel != null)
.OrderByDescending(m => m.OverUnder ?? 0) .OrderByDescending(m => m.OverUnder ?? 0)
.ThenBy(m => m.FamilyName) .ThenBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToList(); .ToList();
@ -141,12 +143,12 @@ namespace Elwig.Windows {
PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyBs != null && r.PenaltyBs != 0)} Mg. / {list.Sum(r => r.PenaltyBs):N2} {sym}"; PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyBs != null && r.PenaltyBs != 0)} Mg. / {list.Sum(r => r.PenaltyBs):N2} {sym}";
PenaltyAreaCommitments.Text = $"{list.Count(r => r.PenaltyAc != null && r.PenaltyAc != 0)} Mg. / {list.Sum(r => r.PenaltyAc):N2} {sym}"; PenaltyAreaCommitments.Text = $"{list.Count(r => r.PenaltyAc != null && r.PenaltyAc != 0)} Mg. / {list.Sum(r => r.PenaltyAc):N2} {sym}";
AutoBusinessShareAdjustment.Text = $"{list.Count(r => r.Adjust > 0)} Mg. / {list.Sum(r => r.Adjust)} GA / {list.Sum(r => r.AdjustAmount):N2} {sym}"; AutoBusinessShareAdjustment.Text = $"{list.Count(r => r.Adjust > 0)} Mg. / {list.Sum(r => r.Adjust)} GA / {list.Sum(r => r.AdjustAmount):N2} {sym}";
CustomModifiers.Text = $"{list.Count(r => r.Custom != null)} Mg. / {list.Sum(r => r.Custom):N2} {sym}"; CustomModifiers.Text = $"{list.Count(r => r.CustomAmount != null)} Mg. / {list.Sum(r => r.CustomAmount):N2} {sym}";
TotalModifiers.Text = $"{list.Count(r => r.Total != 0)} Mg. / {list.Sum(r => r.Total):N2} {sym}"; TotalModifiers.Text = $"{list.Count(r => r.Total != 0)} Mg. / {list.Sum(r => r.Total):N2} {sym}";
NonDeliveries.Text = $"{list.Count(r => r.Weight == 0):N0}"; NonDeliveries.Text = $"{list.Count(r => r.Weight == 0):N0}";
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
.OrderBy(m => m.FamilyName) .OrderBy(m => m.Name)
.ThenBy(m => m.GivenName) .ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr) .ThenBy(m => m.MgNr)
.ToListAsync()); .ToListAsync());
@ -239,13 +241,22 @@ namespace Elwig.Windows {
MemberList.ScrollIntoView(MemberList.SelectedItem); MemberList.ScrollIntoView(MemberList.SelectedItem);
if (CustomPayments?.TryGetValue(m.MgNr, out var p) == true) { if (CustomPayments?.TryGetValue(m.MgNr, out var p) == true) {
CustomModAbsInput.Text = $"{p.ModAbs:N2}";
CustomModRelInput.Text = $"{p.ModRel * 100:N2}";
CustomModCommentInput.Text = p.ModComment ?? "";
CustomAmountInput.Text = $"{p.Amount:N2}"; CustomAmountInput.Text = $"{p.Amount:N2}";
CustomCommentInput.Text = p.Comment ?? ""; CustomCommentInput.Text = p.Comment ?? "";
} else { } else {
CustomModAbsInput.Text = "";
CustomModRelInput.Text = "";
CustomModCommentInput.Text = "";
CustomAmountInput.Text = ""; CustomAmountInput.Text = "";
CustomCommentInput.Text = ""; CustomCommentInput.Text = "";
} }
} else { } else {
CustomModAbsInput.Text = "";
CustomModRelInput.Text = "";
CustomModCommentInput.Text = "";
CustomAmountInput.Text = ""; CustomAmountInput.Text = "";
CustomCommentInput.Text = ""; CustomCommentInput.Text = "";
} }
@ -256,6 +267,14 @@ namespace Elwig.Windows {
App.FocusMember(m.MgNr); App.FocusMember(m.MgNr);
} }
private void CustomModAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckDecimal((TextBox)sender, false, 4, 2, true);
}
private void CustomModRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckDecimal((TextBox)sender, false, 3, 2, true);
}
private void CustomAmountInput_TextChanged(object sender, TextChangedEventArgs evt) { private void CustomAmountInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckDecimal((TextBox)sender, false, 4, 2, true); Validator.CheckDecimal((TextBox)sender, false, 4, 2, true);
} }
@ -268,12 +287,19 @@ namespace Elwig.Windows {
if (CustomPayments?.TryGetValue(m.MgNr, out var p) == true) { if (CustomPayments?.TryGetValue(m.MgNr, out var p) == true) {
ctx.Remove(p); ctx.Remove(p);
} }
if (sender == SaveCustomButton && decimal.TryParse(CustomAmountInput.Text, out var num)) { if (sender == SaveCustomButton) {
var modAbs = decimal.TryParse(CustomModAbsInput.Text, out var n1) ? (decimal?)n1 : null;
var modRel = decimal.TryParse(CustomModRelInput.Text, out var n2) ? (decimal?)n2 / 100 : null;
var amount = decimal.TryParse(CustomAmountInput.Text, out var n3) ? (decimal?)n3 : null;
var modText = CustomModCommentInput.Text.Trim();
var text = CustomCommentInput.Text.Trim(); var text = CustomCommentInput.Text.Trim();
ctx.Add(new PaymentCustom { ctx.Add(new PaymentCustom {
MgNr = m.MgNr, MgNr = m.MgNr,
Year = Year, Year = Year,
Amount = num, ModAbs = modAbs,
ModRel = modRel,
ModComment = modText == "" ? null : modText,
Amount = amount,
Comment = text == "" ? null : text, Comment = text == "" ? null : text,
}); });
} }

View File

@ -128,6 +128,12 @@
HorizontalAlignment="Left" VerticalAlignment="Top" HorizontalAlignment="Left" VerticalAlignment="Top"
TextChanged="WeightModifierInput_TextChanged" LostFocus="WeightModifierInput_LostFocus"/> TextChanged="WeightModifierInput_TextChanged" LostFocus="WeightModifierInput_LostFocus"/>
<TextBlock x:Name="MatzenNote" Grid.ColumnSpan="2" Margin="20,170,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
Ob ein Mitglied den Treuebonus<LineBreak/>
bekommt ist in der Buchungsliste<LineBreak/>
(Spalte Zuschlag) ersichtlich.
</TextBlock>
<Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/> <Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/>
<CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen" <CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen"
Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"

View File

@ -44,6 +44,11 @@ namespace Elwig.Windows {
if (!App.Config.Debug) { if (!App.Config.Debug) {
DataInput.Visibility = Visibility.Hidden; DataInput.Visibility = Visibility.Hidden;
} }
if (App.Client.IsMatzen) {
ConsiderCustomInput.Content = ConsiderCustomInput.Content.ToString()?.Replace("Benutzerdefinierte", "Bentz.def.").Replace("Mitglied", "Mg.") + " (inkl. Treuebonus)";
} else {
MatzenNote.Visibility = Visibility.Hidden;
}
} }
protected override async Task OnRenewContext(AppDbContext ctx) { protected override async Task OnRenewContext(AppDbContext ctx) {

View File

@ -0,0 +1,30 @@
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Dtos;
namespace Tests.DocumentTests {
[TestFixture]
public class DeliveryAncmtListTest {
[Test]
public async Task Test_01_AllAnnouncements2020() {
using var ctx = new AppDbContext();
var filter = "01.10.2020 Matzen GV Kabinettaktion";
var data = await DeliveryAncmtListData.FromQuery(ctx.DeliveryAnnouncements.Where(a => a.Year == 2020 && a.DsNr == 1), [filter]);
using var doc = new DeliveryAncmtList(filter, data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
Assert.That(text, Contains.Substring("Anmeldeliste"));
Assert.That(text, Contains.Substring("01.10.2020 Matzen GV Kabinettaktion"));
Assert.That(table, Is.EqualTo(new string[][] {
["01.10.2020", "101 MUSTERMANN Max", "Grüner Veltliner", "5 000"],
["01.10.2020", "102 WEINBAUER Wernhardt", "Grüner Veltliner", "10 000"],
["01.10.2020", "103 MUSTERBAUER Matthäus", "Grüner Veltliner", "8 000"],
["01.10.2020", "104 WINZER Waltraud", "Grüner Veltliner", "2 000"],
["Gesamt:", "Anmeldungen: 4", "25 000"],
}));
});
}
}
}

View File

@ -21,7 +21,7 @@ namespace Tests.DocumentTests {
public static string[][] ExtractTable(string text) { public static string[][] ExtractTable(string text) {
return text.Split('\n') return text.Split('\n')
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).Where(c => c.Length > 0).ToArray()) .Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).Where(c => c.Length > 0).ToArray())
.Where(row => row.Length > 3) .Where(row => row.Length >= 3)
.Skip(1) .Skip(1)
.ToArray(); .ToArray();
} }

View File

@ -36,7 +36,7 @@ namespace Tests.E2ETests {
Window.FindElement(By.WpfId("MgNrInput")).SendKeys("9999"); Window.FindElement(By.WpfId("MgNrInput")).SendKeys("9999");
Window.FindElement(By.WpfId("GivenNameInput")).SendKeys("Norbert"); Window.FindElement(By.WpfId("GivenNameInput")).SendKeys("Norbert");
Window.FindElement(By.WpfId("FamilyNameInput")).SendKeys("Neuling"); Window.FindElement(By.WpfId("NameInput")).SendKeys("Neuling");
Window.FindElement(By.WpfId("PrefixInput")).SendKeys("Ing."); Window.FindElement(By.WpfId("PrefixInput")).SendKeys("Ing.");
Window.FindElement(By.WpfId("SuffixInput")).SendKeys("jun."); Window.FindElement(By.WpfId("SuffixInput")).SendKeys("jun.");
Window.FindElement(By.WpfId("BirthdayInput")).SendKeys("1987"); Window.FindElement(By.WpfId("BirthdayInput")).SendKeys("1987");

View File

@ -1,5 +1,6 @@
-- deletes for DocumentTests -- deletes for DocumentTests
DELETE FROM delivery_schedule;
DELETE FROM payment_variant; DELETE FROM payment_variant;
DELETE FROM delivery; DELETE FROM delivery;
DELETE FROM season; DELETE FROM season;

View File

@ -9,6 +9,18 @@ INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lo
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL); (2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO delivery_schedule (year, dsnr, date, zwstid, description, max_weight, ancmt_from, ancmt_to) VALUES
(2020, 1, '2020-10-01', 'X', 'GV Kabinettaktion', 100000, NULL, NULL);
INSERT INTO delivery_schedule_wine_variety (year, dsnr, sortid, priority) VALUES
(2020, 1, 'GV', 1);
INSERT INTO delivery_announcement (year, dsnr, mgnr, sortid, weight, type) VALUES
(2020, 1, 101, 'GV', 5000, 'manual'),
(2020, 1, 102, 'GV', 10000, 'manual'),
(2020, 1, 103, 'GV', 8000, 'manual'),
(2020, 1, 104, 'GV', 2000, 'manual');
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE), (2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE); (2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);

View File

@ -66,7 +66,7 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
(15216, 2), (15216, 2),
(15224, 2); (15224, 2);
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ), (101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ),
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'), (102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'),
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ), (103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ),

View File

@ -1 +1 @@
curl --fail -s -L "https://elwig.at/files/create.sql?v=25" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql" curl --fail -s -L "https://elwig.at/files/create.sql?v=28" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"