Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
39956cbcbd | |||
84d8d0cecb | |||
fe7f9d675b | |||
9d1ee8638c | |||
cd40075702 | |||
204dfe8745 | |||
f97dfc3c72 | |||
d3839c288a | |||
2764a0ca21 | |||
48970de652 | |||
367c3ac357 | |||
4d89a17e80 | |||
f52c11b91e | |||
f48c6a02cb | |||
025ff08d84 | |||
b091bd0ec3 | |||
804a17911c | |||
170cfda37e | |||
2d737e2780 | |||
2333077aa5 | |||
9127cd3f03 | |||
036a0dc978 | |||
7749f6ab45 | |||
cf05a0c658 | |||
5567d9f25a | |||
4403754ada | |||
8db6007264 | |||
944270744a | |||
10ee1d6548 | |||
db4de5b5fe | |||
f69d2809f3 | |||
1c2e0baa68 | |||
39f93da0ba | |||
91717f8efb | |||
7786fb421a |
124
CHANGELOG.md
124
CHANGELOG.md
@ -3,6 +3,130 @@ Changelog
|
||||
=========
|
||||
|
||||
|
||||
[v0.10.3][v0.10.3] (2024-08-21) {#v0.10.3}
|
||||
------------------------------------------
|
||||
|
||||
### Behobene Fehler {#v0.10.3-bugfixes}
|
||||
|
||||
* Datum für _Anmeldungen bis_ im Leseplan-Fenster (`DeliveryScheduleAdminWindow`) ab jetzt änderbar. (84d8d0cecb)
|
||||
|
||||
### Sonstiges {#v0.10.3-misc}
|
||||
|
||||
* Wochentag bei Leseplan-Liste im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`) anzeigen. (fe7f9d675b)
|
||||
|
||||
[v0.10.3]: https://git.necronda.net/winzer/elwig/releases/tag/v0.10.3
|
||||
|
||||
|
||||
|
||||
|
||||
[v0.10.2][v0.10.2] (2024-08-16) {#v0.10.2}
|
||||
------------------------------------------
|
||||
|
||||
### Behobene Fehler {#v0.10.2-bugfixes}
|
||||
|
||||
* Beim Hochladen der Mitgliederdaten sind Flächenbindungen nicht mitexportiert worden. (204dfe8745)
|
||||
* Beim Importieren von älteren Exportdaten kam es zu einem Fehler. (cd40075702)
|
||||
|
||||
[v0.10.2]: https://git.necronda.net/winzer/elwig/releases/tag/v0.10.2
|
||||
|
||||
|
||||
|
||||
|
||||
[v0.10.1][v0.10.1] (2024-08-14) {#v0.10.1}
|
||||
------------------------------------------
|
||||
|
||||
### Neue Funktionen {#v0.10.1-features}
|
||||
|
||||
* In der Mitglieder-Liste (PDF und Excel) werden ggf. gefilterte Flächenbindungen angegeben. (d3839c288a)
|
||||
|
||||
### Behobene Fehler {#v0.10.1-bugfixes}
|
||||
|
||||
* Fehler beim Berechnen...
|
||||
* falls in Saison keine Lieferungen vorhanden sind. (48970de652)
|
||||
* falls der Vorname eines Mitgliedes nicht gesetzt ist. (2764a0ca21)
|
||||
|
||||
[v0.10.1]: https://git.necronda.net/winzer/elwig/releases/tag/v0.10.1
|
||||
|
||||
|
||||
|
||||
|
||||
[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}
|
||||
---------------------------------------
|
||||
|
||||
### Behobene Fehler {#v0.9.2-bugfixes}
|
||||
|
||||
* Verhalten beim Schließen des Haupt-Fensters (`MainWindow`) nicht immer richtig. (8db6007264)
|
||||
* Im Mitglieder-Fenster (`MemberAdminWindow`) führt beim Erstellen eines Mitglied das Eingeben einer Tel.-Nr. zu einem Fehler. (4403754ada)
|
||||
|
||||
[v0.9.2]: https://git.necronda.net/winzer/elwig/releases/tag/v0.9.2
|
||||
|
||||
|
||||
|
||||
|
||||
[v0.9.1][v0.9.1] (2024-08-01) {#v0.9.1}
|
||||
---------------------------------------
|
||||
|
||||
### Neue Funktionen {#v0.9.1-features}
|
||||
|
||||
* Im Rundschreiben-Fenster (`MailWindow`) können E-Mails ohne Anhänge verschickt werden. (f69d2809f3)
|
||||
* Im Mitglieder-Fenster (`MemberAdminWindow`) und Lieferungen-Fenster (`DeliveryAdminWindow`) kann das/die momentan ausgewählte Mitglied/Lieferung alleine exportiert werden. (db4de5b5fe)
|
||||
* Exporte für Mitglieder enthalten nun auch deren Flächenbindungen. (10ee1d6548)
|
||||
|
||||
### Behobene Fehler {#v0.9.1-bugfixes}
|
||||
|
||||
* Sortierung der Wiegen-Knöpfe im Übernahme-Fenster (`DeliveryAdminWindow`). (7786fb421a)
|
||||
|
||||
### Sonstiges {#v0.9.1-misc}
|
||||
|
||||
* Deutsche Fehlermeldung beim Hochladen/Herunterladen im Haupt-Fenster (`MainWindow`). (91717f8efb)
|
||||
* Waagen-Fehler _Brutto negativ_ implementiert. (39f93da0ba)
|
||||
|
||||
[v0.9.1]: https://git.necronda.net/winzer/elwig/releases/tag/v0.9.1
|
||||
|
||||
|
||||
|
||||
|
||||
[v0.9.0][v0.9.0] (2024-07-28) {#v0.9.0}
|
||||
---------------------------------------
|
||||
|
||||
|
@ -34,18 +34,7 @@ namespace Elwig {
|
||||
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
||||
|
||||
public static Config Config { get; private set; } = new(ConfigPath);
|
||||
public static int VersionMajor { get; private set; }
|
||||
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 Version Version { get; private set; } = new();
|
||||
|
||||
public static int BranchNum { get; private set; }
|
||||
public static string ZwstId { get; private set; }
|
||||
@ -110,7 +99,7 @@ namespace Elwig {
|
||||
}
|
||||
|
||||
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 {
|
||||
await AppDbUpdater.CheckDb();
|
||||
@ -231,7 +220,7 @@ namespace Elwig {
|
||||
public static async Task CheckForUpdates(bool showAlert = false) {
|
||||
if (Config.UpdateUrl == null) return;
|
||||
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(() => {
|
||||
var d = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size);
|
||||
if (d.ShowDialog() == true) {
|
||||
@ -303,6 +292,14 @@ namespace Elwig {
|
||||
return w;
|
||||
}
|
||||
|
||||
public static DeliveryAncmtAdminWindow FocusDeliveryAncmt() {
|
||||
return FocusWindow<DeliveryAncmtAdminWindow>(() => new());
|
||||
}
|
||||
|
||||
public static DeliveryScheduleAdminWindow FocusDeliverySchedule() {
|
||||
return FocusWindow<DeliveryScheduleAdminWindow>(() => new());
|
||||
}
|
||||
|
||||
public static PaymentVariantsWindow FocusPaymentVariants(int year) {
|
||||
return FocusWindow<PaymentVariantsWindow>(() => new(year), w => w.Year == year);
|
||||
}
|
||||
|
@ -20,6 +20,12 @@ namespace Elwig.Controls {
|
||||
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 string AllItemsSelectedContent {
|
||||
get => (string)GetValue(AllItemsSelectedContentProperty);
|
||||
@ -116,7 +122,7 @@ namespace Elwig.Controls {
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
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) {
|
||||
_textBox.Text = AllItemsSelectedContent;
|
||||
AllItemsSelected = true;
|
||||
|
@ -32,7 +32,7 @@ namespace Elwig.Documents {
|
||||
get {
|
||||
IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member;
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,15 +33,21 @@ namespace Elwig.Documents {
|
||||
bool considerCustomModifiers,
|
||||
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;
|
||||
ShowDateAndLocation = true;
|
||||
Data = data;
|
||||
Payment = p;
|
||||
Credit = p.Credit;
|
||||
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;
|
||||
if (mod != null) {
|
||||
if (CustomPayment?.ModComment != null) {
|
||||
MemberModifier = CustomPayment.ModComment;
|
||||
} else if (mod != null) {
|
||||
MemberModifier = $"{mod.Name} ({mod.ValueStr})";
|
||||
} else {
|
||||
MemberModifier = "Sonstige Zu-/Abschläge";
|
||||
@ -88,8 +94,5 @@ namespace Elwig.Documents {
|
||||
.Where(u => u.Item3 != 0)
|
||||
.ToList();
|
||||
}
|
||||
if (considerCustomModifiers) {
|
||||
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -157,9 +157,9 @@
|
||||
@Raw(FormatRow($"Autom. Nachz. von GA ({Model.MemberAutoBusinessShares})", Model.MemberAutoBusinessSharesAmount, add: true));
|
||||
penalty += Model.MemberAutoBusinessSharesAmount;
|
||||
}
|
||||
@if (Model.CustomPayment != null) {
|
||||
@Raw(FormatRow(Model.CustomPayment.Comment ?? (Model.CustomPayment.Amount < 0 ? "Weitere Abzüge" : "Weitere Zuschläge"), Model.CustomPayment.Amount, add: true));
|
||||
penalty += Model.CustomPayment.Amount;
|
||||
@if (Model.CustomPayment?.Amount != null) {
|
||||
@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.Value;
|
||||
}
|
||||
|
||||
@if (Model.Credit == null) {
|
||||
|
21
Elwig/Documents/DeliveryAncmtList.cs
Normal file
21
Elwig/Documents/DeliveryAncmtList.cs
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
46
Elwig/Documents/DeliveryAncmtList.cshtml
Normal file
46
Elwig/Documents/DeliveryAncmtList.cshtml
Normal 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>
|
13
Elwig/Documents/DeliveryAncmtList.css
Normal file
13
Elwig/Documents/DeliveryAncmtList.css
Normal 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;
|
||||
}
|
@ -90,6 +90,8 @@ namespace Elwig.Documents {
|
||||
name = "WineQualityStatistics";
|
||||
} else if (this is PaymentVariantSummary) {
|
||||
name = "PaymentVariantSummary";
|
||||
} else if (this is DeliveryAncmtList) {
|
||||
name = "DeliveryAncmtList";
|
||||
} else {
|
||||
throw new InvalidOperationException("Invalid document object");
|
||||
}
|
||||
@ -156,7 +158,7 @@ namespace Elwig.Documents {
|
||||
|
||||
public void Show() {
|
||||
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) {
|
||||
|
@ -2,7 +2,7 @@ using Elwig.Models.Entities;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
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 = "";
|
||||
}
|
||||
}
|
||||
|
@ -18,21 +18,31 @@
|
||||
<tbody>
|
||||
<tr class="sectionheading"><th colspan="6">Persönliche Daten</th></tr>
|
||||
<tr>
|
||||
<th class="small">Titel (vorangestellt)</th>
|
||||
<th class="small">Vorname</th>
|
||||
<th colspan="3" class="small">Nachname</th>
|
||||
<th class="small">Titel (nachgestellt)</th>
|
||||
@if (Model.Member.IsJuridicalPerson) {
|
||||
<th colspan="3" class="small">Name</th>
|
||||
<th colspan="3" class="small">Zu Handen</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>
|
||||
<td class="large">@Model.Member.Prefix</td>
|
||||
<td class="large">@Model.Member.GivenName @Model.Member.MiddleName</td>
|
||||
<td class="large" colspan="3">@Model.Member.FamilyName</td>
|
||||
<td class="large">@Model.Member.Suffix</td>
|
||||
@if (Model.Member.IsJuridicalPerson) {
|
||||
<td colspan="3" class="large">@Model.Member.Name</td>
|
||||
<td colspan="3" class="large">@Model.Member.ForTheAttentionOf</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>
|
||||
<th>Mitglieds-Nr.:</th>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -50,7 +60,7 @@
|
||||
<tr class="sectionheading"><th colspan="6">Rechnungsadresse (optional)</th></tr>
|
||||
<tr>
|
||||
<th>Name:</th>
|
||||
<td colspan="5">@Model.Member.BillingAddress?.Name</td>
|
||||
<td colspan="5">@Model.Member.BillingAddress?.FullName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Adresse:</th>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Elwig.Models.Dtos;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class MemberList : Document {
|
||||
@ -9,9 +10,17 @@ namespace Elwig.Documents {
|
||||
public string Filter;
|
||||
public IEnumerable<MemberListRow> Members;
|
||||
|
||||
public string[] AreaComFilters;
|
||||
public bool FilterAreaComs => AreaComFilters.Length > 0;
|
||||
|
||||
public MemberList(string filter, IEnumerable<MemberListRow> members) : base(Name) {
|
||||
Filter = filter;
|
||||
Members = members;
|
||||
AreaComFilters = [..members
|
||||
.SelectMany(m => m.AreaCommitmentsFiltered)
|
||||
.Select(c => c.VtrgId)
|
||||
.Distinct()
|
||||
.Order()];
|
||||
}
|
||||
|
||||
public MemberList(string filter, MemberListData data) :
|
||||
|
@ -8,30 +8,61 @@
|
||||
<h2>@Model.Filter</h2>
|
||||
<table class="members">
|
||||
<colgroup>
|
||||
<col style="width: 8mm;"/>
|
||||
<col style="width: 42mm;"/>
|
||||
<col style="width: 40mm;"/>
|
||||
<col style="width: 8mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
@if (Model.AreaComFilters.Length > 1) {
|
||||
<col style="width: 38mm;"/>
|
||||
} else {
|
||||
<col style="width: 42mm;"/>
|
||||
}
|
||||
@if (Model.AreaComFilters.Length > 1) {
|
||||
<col style="width: 36mm;"/>
|
||||
} else {
|
||||
<col style="width: 40mm;"/>
|
||||
}
|
||||
<col style="width: 8mm;"/>
|
||||
@if (Model.AreaComFilters.Length > 1) {
|
||||
<col style="width: 18mm;"/>
|
||||
} else {
|
||||
<col style="width: 20mm;"/>
|
||||
}
|
||||
<col style="width: 12mm;"/>
|
||||
<col style="width: 5mm;" />
|
||||
<col style="width: 18mm;"/>
|
||||
<col style="width: 5mm;"/>
|
||||
@if (Model.AreaComFilters.Length > 1) {
|
||||
<col style="width: 16mm;"/>
|
||||
} else {
|
||||
<col style="width: 18mm;"/>
|
||||
}
|
||||
<col style="width: 12mm;"/>
|
||||
@if (Model.AreaComFilters.Length > 1) {
|
||||
<col style="width: 12mm;"/>
|
||||
}
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">Nr.</th>
|
||||
<th rowspan="2" style="text-align: left;">Name</th>
|
||||
<th rowspan="2" style="text-align: left;">Adresse</th>
|
||||
<th rowspan="2">PLZ</th>
|
||||
<th rowspan="2" style="text-align: left;">Ort</th>
|
||||
<th rowspan="2">Betr.-Nr.</th>
|
||||
<th rowspan="2">GA</th>
|
||||
<th rowspan="2" style="text-align: left;">Stamm-KG</th>
|
||||
<th>Geb. Fl.</th>
|
||||
@{
|
||||
var headerSpan = Model.FilterAreaComs ? 3 : 2;
|
||||
}
|
||||
<th rowspan="@headerSpan">Nr.</th>
|
||||
<th rowspan="@headerSpan" style="text-align: left;">Name</th>
|
||||
<th rowspan="@headerSpan" style="text-align: left;">Adresse</th>
|
||||
<th rowspan="@headerSpan">PLZ</th>
|
||||
<th rowspan="@headerSpan" style="text-align: left;">Ort</th>
|
||||
<th rowspan="@headerSpan">Betr.-Nr.</th>
|
||||
<th rowspan="@headerSpan">GA</th>
|
||||
<th rowspan="@headerSpan" style="text-align: left;">Stamm-KG</th>
|
||||
<th colspan="@(Model.FilterAreaComs ? Model.AreaComFilters.Length : 1)">Geb. Fl.</th>
|
||||
</tr>
|
||||
@if (Model.FilterAreaComs) {
|
||||
<tr>
|
||||
@foreach (var vtrgId in Model.AreaComFilters) {
|
||||
<th>@vtrgId</th>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th class="unit">[m²]</th>
|
||||
@for (int i = 0; i < Math.Max(Model.AreaComFilters.Length, 1); i++) {
|
||||
<th class="unit">[m²]</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="small">
|
||||
@ -40,22 +71,28 @@
|
||||
}
|
||||
@foreach (var m in Model.Members) {
|
||||
if (lastBranch != null && m.Branch != lastBranch) {
|
||||
<tr class="spacing"><td colspan="9"></td></tr>
|
||||
<tr class="spacing"><td colspan="@(8 + Math.Max(Model.AreaComFilters.Length, 1))"></td></tr>
|
||||
<tr class="header">
|
||||
<th colspan="9">@m.Branch</th>
|
||||
<th colspan="@(8 + Math.Max(Model.AreaComFilters.Length, 1))">@m.Branch</th>
|
||||
</tr>
|
||||
lastBranch = m.Branch;
|
||||
}
|
||||
<tr>
|
||||
<td class="number" rowspan="@(m.BillingName != null ? 2 : 1)">@m.MgNr</td>
|
||||
<td>@m.Name1.Replace('ß', 'ẞ').ToUpper() @m.Name2</td>
|
||||
<td>@m.AdminName1 @m.Name2</td>
|
||||
<td>@m.Address</td>
|
||||
<td>@m.Plz</td>
|
||||
<td class="tiny">@m.Locality</td>
|
||||
<td>@m.LfbisNr</td>
|
||||
<td class="number">@m.BusinessShares</td>
|
||||
<td class="tiny">@m.DefaultKg</td>
|
||||
<td class="number">@($"{m.AreaCommitment:N0}")</td>
|
||||
@if (Model.AreaComFilters.Length > 0) {
|
||||
foreach (var v in Model.AreaComFilters) {
|
||||
<td class="number">@($"{m.AreaCommitmentsFiltered.FirstOrDefault(c => c.VtrgId == v).Area:N0}")</td>
|
||||
}
|
||||
} else {
|
||||
<td class="number">@($"{m.AreaCommitment:N0}")</td>
|
||||
}
|
||||
</tr>
|
||||
if (m.BillingName != null) {
|
||||
<tr>
|
||||
|
@ -20,9 +20,10 @@
|
||||
@{
|
||||
//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 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 sum1 = sum2 - modifiers;
|
||||
var sum1 = sum2 - deliveryModifiers - memberModifiers;
|
||||
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 vat = Model.Variant.Credits.Sum(p => p.VatAmount);
|
||||
@ -49,7 +50,7 @@
|
||||
<td class="center">@(Model.BillingData.ConsiderContractPenalties ? "Ja" : "Nein")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="overflow: visible;">Nto./bto.-Zuschl:</th>
|
||||
<th style="overflow: visible;">Rebel-Zuschl.:</th>
|
||||
<td colspan="3" class="center">
|
||||
@($"{Utils.GetSign(Model.BillingData.NetWeightModifier)}{Math.Abs(Model.BillingData.NetWeightModifier) * 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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Zu-/Abschläge (Lieferungen):</th>
|
||||
<td class="number">@Utils.GetSign(modifiers)</td>
|
||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(modifiers):N2}")</td>
|
||||
<th colspan="2">Zu-/Abschläge (Mitglieder):</th>
|
||||
<td class="number">@Utils.GetSign(memberModifiers)</td>
|
||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(memberModifiers):N2}")</td>
|
||||
<th class="lborder">Lieferungen:</th>
|
||||
<td colspan="2" class="number">@($"{Model.DeliveryNum:N0}")</td>
|
||||
</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>
|
||||
<th colspan="2">Gesamtsumme:</th>
|
||||
<td class="number tborder"></td>
|
||||
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{sum2:N2}")</td>
|
||||
<th class="lborder">Teillieferungen:</th>
|
||||
<td colspan="2" class="number">@($"{Model.DeliveryPartNum:N0}")</td>
|
||||
<th class="lborder"></th>
|
||||
<td colspan="2"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Bisher ausgezahlt:</th>
|
||||
<td class="number">@Utils.GetSign(payed)</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 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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Mehrwertsteuer:</th>
|
||||
<td class="number">@Utils.GetSign(vat)</td>
|
||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
|
||||
<th colspan="2">Nettosumme:</th>
|
||||
<td class="number tborder"></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 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>
|
||||
</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 colspan="2">Mehrwertsteuer:</th>
|
||||
<td class="number">@Utils.GetSign(vat)</td>
|
||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
|
||||
@{
|
||||
var gebRows = Model.Data.Rows
|
||||
.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")
|
||||
</td>
|
||||
</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>
|
||||
<th colspan="2">Abzüge (Strafen/Pönalen, GA, ...):</th>
|
||||
<td class="number">@Utils.GetSign(totalMods)</td>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>0.9.0</Version>
|
||||
<Version>0.10.3</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
|
@ -51,6 +51,9 @@ namespace Elwig.Helpers {
|
||||
public DbSet<MemberHistory> MemberHistory { get; private set; }
|
||||
public DbSet<AreaCom> AreaCommitments { 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<Delivery> Deliveries { get; private set; }
|
||||
public DbSet<DeliveryPart> DeliveryParts { get; private set; }
|
||||
@ -236,6 +239,13 @@ namespace Elwig.Helpers {
|
||||
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) {
|
||||
return await WineQualityLevels
|
||||
.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) {
|
||||
var ownCnx = cnx == null;
|
||||
cnx ??= await ConnectAsync();
|
||||
|
@ -9,11 +9,11 @@ namespace Elwig.Helpers {
|
||||
public static class AppDbUpdater {
|
||||
|
||||
// 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;
|
||||
|
||||
public static async Task<string> CheckDb() {
|
||||
public static async Task<Version> CheckDb() {
|
||||
using var cnx = AppDbContext.Connect();
|
||||
|
||||
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);
|
||||
|
||||
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
||||
var major = userVers >> 24;
|
||||
var minor = (userVers >> 16) & 0xFF;
|
||||
var patch = userVers & 0xFFFF;
|
||||
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)(userVers & 0xFFFF));
|
||||
|
||||
if (App.VersionMajor > major ||
|
||||
(App.VersionMajor == major && App.VersionMinor > minor) ||
|
||||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
|
||||
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
|
||||
if (App.Version > v) {
|
||||
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | App.Version.Build;
|
||||
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) {
|
||||
|
@ -157,13 +157,15 @@ namespace Elwig.Helpers.Billing {
|
||||
lastMgNr = mgnr;
|
||||
}
|
||||
|
||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||
UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year};
|
||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||
ON CONFLICT DO UPDATE
|
||||
SET discr = excluded.discr, value = value + excluded.value;
|
||||
""");
|
||||
await AppDbContext.ExecuteBatch(cnx, $"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
|
||||
if (inserts.Count > 0) {
|
||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||
ON CONFLICT DO UPDATE
|
||||
SET discr = excluded.discr, value = value + excluded.value;
|
||||
""");
|
||||
}
|
||||
|
||||
if (!avoidUnderDlvrs) {
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
|
@ -29,6 +29,8 @@ namespace Elwig.Helpers.Billing {
|
||||
await CalculatePrices(cnx);
|
||||
if (Data.ConsiderDelieryModifiers) {
|
||||
await CalculateDeliveryModifiers(cnx);
|
||||
}
|
||||
if (Data.ConsiderCustomModifiers) {
|
||||
await CalculateMemberModifiers(cnx);
|
||||
}
|
||||
await tx.CommitAsync();
|
||||
@ -104,18 +106,21 @@ namespace Elwig.Helpers.Billing {
|
||||
if (App.Client.IsMatzen) {
|
||||
var lastYears = 3;
|
||||
var multiplier = 0.50;
|
||||
var includePredecessor = true;
|
||||
var modName = "Treue%";
|
||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||
SELECT c.year, {AvNr}, s.mgnr, 0,
|
||||
ROUND(s.sum * COALESCE(m.abs, 0)),
|
||||
COALESCE(m.rel, 0)
|
||||
FROM (SELECT {Year} AS year, mgnr,
|
||||
ROUND(AVG(sum) * {multiplier}) AS baseline,
|
||||
COUNT(*) = {lastYears} AND MIN(sum) > 0 AS allowed
|
||||
FROM v_stat_member
|
||||
WHERE year > {Year} - {lastYears}
|
||||
GROUP BY mgnr
|
||||
FROM (SELECT {Year} AS year, m.mgnr,
|
||||
ROUND(AVG(COALESCE(a.sum, b.sum)) * {multiplier}) AS baseline,
|
||||
COUNT(*) = {lastYears} AND MIN(COALESCE(a.sum, b.sum)) > 0 AS allowed
|
||||
FROM member m
|
||||
LEFT JOIN v_stat_member a ON a.mgnr = m.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
|
||||
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}'
|
||||
@ -125,6 +130,16 @@ namespace Elwig.Helpers.Billing {
|
||||
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) {
|
||||
|
@ -87,7 +87,7 @@ namespace Elwig.Helpers {
|
||||
SmtpPassword = config["smtp:password"];
|
||||
SmtpFrom = config["smtp:from"];
|
||||
|
||||
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key);
|
||||
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key).Order();
|
||||
ScaleList.Clear();
|
||||
Scales = ScaleList;
|
||||
foreach (var s in scales) {
|
||||
|
@ -29,7 +29,7 @@ namespace Elwig.Helpers.Export {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using var cmd = cnx.CreateCommand();
|
||||
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
|
||||
FROM v_bki_delivery
|
||||
WHERE year = {year}
|
||||
|
@ -83,7 +83,7 @@ namespace Elwig.Helpers.Export {
|
||||
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
|
||||
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt>
|
||||
<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) {
|
||||
var full = ShowAddresses == AddressMode.Full;
|
||||
|
@ -39,6 +39,7 @@ namespace Elwig.Helpers.Export {
|
||||
Dictionary<int, int> currentDids;
|
||||
Dictionary<string, int> currentLsNrs;
|
||||
Dictionary<int, List<WbRd>> currentWbRde;
|
||||
Dictionary<int, AT_Kg> kgs;
|
||||
|
||||
using (var ctx = new AppDbContext()) {
|
||||
branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
|
||||
@ -50,6 +51,7 @@ namespace Elwig.Helpers.Export {
|
||||
currentWbRde = await ctx.WbRde
|
||||
.GroupBy(r => r.KgNr)
|
||||
.ToDictionaryAsync(g => g.Key, g => g.ToList());
|
||||
kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
|
||||
}
|
||||
|
||||
var data = new List<(
|
||||
@ -101,9 +103,9 @@ namespace Elwig.Helpers.Export {
|
||||
string? line;
|
||||
while ((line = await reader.ReadLineAsync()) != null) {
|
||||
var obj = JsonNode.Parse(line)!.AsObject();
|
||||
var (m, b, telNrs, emailAddrs) = obj.ToMember();
|
||||
var (m, b, telNrs, emailAddrs) = obj.ToMember(kgs);
|
||||
r.Members.Add(m);
|
||||
if (b != null) data[^1].BillingAddresses.Add(b);
|
||||
if (b != null) r.BillingAddresses.Add(b);
|
||||
r.TelephoneNumbers.AddRange(telNrs);
|
||||
r.EmailAddresses.AddRange(emailAddrs);
|
||||
}
|
||||
@ -115,9 +117,12 @@ namespace Elwig.Helpers.Export {
|
||||
string? line;
|
||||
while ((line = await reader.ReadLineAsync()) != null) {
|
||||
var obj = JsonNode.Parse(line)!.AsObject();
|
||||
var (areaCom, wbrd) = obj.ToAreaCom(currentWbRde);
|
||||
var (areaCom, wbrd) = obj.ToAreaCom(kgs, currentWbRde);
|
||||
r.AreaCommitments.Add(areaCom);
|
||||
if (wbrd != null) r.Riede.Add(wbrd);
|
||||
if (wbrd != null) {
|
||||
currentWbRde[wbrd.KgNr].Add(wbrd);
|
||||
r.Riede.Add(wbrd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,6 +141,7 @@ namespace Elwig.Helpers.Export {
|
||||
}
|
||||
|
||||
var importedMembers = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
|
||||
var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
|
||||
var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
|
||||
|
||||
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers), meta) in data.Zip(metaData)) {
|
||||
@ -159,6 +165,12 @@ namespace Elwig.Helpers.Export {
|
||||
if (duplicateMgNrs.Count > 0)
|
||||
importDuplicateMembers = ImportQuestion(branch.Name, device, "Mitglieder", true, duplicateMgNrs.Count);
|
||||
|
||||
var fbnrs = areaCommitments.Select(c => c.FbNr).ToList();
|
||||
var duplicateFbNrs = await ctx.AreaCommitments
|
||||
.Where(c => fbnrs.Contains(c.FbNr))
|
||||
.Select(c => c.FbNr)
|
||||
.ToListAsync();
|
||||
|
||||
var lsnrs = deliveries.Select(d => d.LsNr).ToList();
|
||||
var duplicateLsNrs = await ctx.Deliveries
|
||||
.Where(d => lsnrs.Contains(d.LsNr))
|
||||
@ -199,18 +211,27 @@ namespace Elwig.Helpers.Export {
|
||||
ctx.AddRange(billingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
|
||||
ctx.AddRange(telephoneNumbers.Where(n => duplicateMgNrs.Contains(n.MgNr)));
|
||||
ctx.AddRange(emailAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
|
||||
ctx.UpdateRange(areaCommitments.Where(c => duplicateMgNrs.Contains(c.MgNr) && duplicateFbNrs.Contains(c.FbNr)));
|
||||
ctx.AddRange(areaCommitments.Where(c => duplicateMgNrs.Contains(c.MgNr) && !duplicateFbNrs.Contains(c.FbNr)));
|
||||
}
|
||||
if (importNewMembers) {
|
||||
ctx.AddRange(members.Where(m => !duplicateMgNrs.Contains(m.MgNr)));
|
||||
ctx.AddRange(billingAddresses.Where(a => !duplicateMgNrs.Contains(a.MgNr)));
|
||||
ctx.AddRange(telephoneNumbers.Where(n => !duplicateMgNrs.Contains(n.MgNr)));
|
||||
ctx.AddRange(emailAddresses.Where(a => !duplicateMgNrs.Contains(a.MgNr)));
|
||||
ctx.UpdateRange(areaCommitments.Where(c => !duplicateMgNrs.Contains(c.MgNr) && duplicateFbNrs.Contains(c.FbNr)));
|
||||
ctx.AddRange(areaCommitments.Where(c => !duplicateMgNrs.Contains(c.MgNr) && !duplicateFbNrs.Contains(c.FbNr)));
|
||||
}
|
||||
if (members.Count > 0) {
|
||||
var n = importNewMembers ? members.Count - duplicateMgNrs.Count : 0;
|
||||
var o = importDuplicateMembers ? duplicateMgNrs.Count : 0;
|
||||
importedMembers.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, members.Count - n - o, meta.MemberFilters));
|
||||
}
|
||||
if (areaCommitments.Count > 0) {
|
||||
ctx.AddRange(riede);
|
||||
var imported = areaCommitments.Where(c => (importNewMembers && !duplicateMgNrs.Contains(c.MgNr)) || (importDuplicateMembers && duplicateMgNrs.Contains(c.MgNr))).ToList();
|
||||
importedAreaComs.Add((meta.FileName, meta.ZwstId, meta.Device, imported.Count, areaCommitments.Count - imported.Count, meta.AreaComFilters));
|
||||
}
|
||||
|
||||
if (allowedDuplicateLsNrs.Count > 0) {
|
||||
var dids = deliveries
|
||||
@ -265,6 +286,12 @@ namespace Elwig.Helpers.Export {
|
||||
$" ({d.New} neu, {d.Overwritten} überschrieben, {d.NotImported} nicht importiert)\n" +
|
||||
$" Zweigstelle: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" +
|
||||
$" Filter: {d.Filters}"),
|
||||
$"Flächenbindungen: {importedAreaComs.Sum(d => d.Imported)}",
|
||||
..importedAreaComs.Select(d =>
|
||||
$" {d.FileName} ({d.Imported})\n" +
|
||||
$" ({d.Imported} importiert, {d.NotImported} nicht importiert)\n" +
|
||||
$" Zweigstelle: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" +
|
||||
$" Filter: {d.Filters}"),
|
||||
$"Lieferungen: {importedDeliveries.Sum(d => d.New + d.Overwritten)}",
|
||||
..importedDeliveries.Select(d =>
|
||||
$" {d.FileName} ({d.New + d.Overwritten})\n" +
|
||||
@ -298,6 +325,13 @@ namespace Elwig.Helpers.Export {
|
||||
}.Export(filename);
|
||||
}
|
||||
|
||||
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<string> filters) {
|
||||
return new ElwigExport {
|
||||
Members = (members, filters),
|
||||
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
|
||||
}.Export(filename);
|
||||
}
|
||||
|
||||
public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
|
||||
return new ElwigExport {
|
||||
Deliveries = (deliveries, filters)
|
||||
@ -373,11 +407,12 @@ namespace Elwig.Helpers.Export {
|
||||
return new JsonObject {
|
||||
["mgnr"] = m.MgNr,
|
||||
["predecessor_mgnr"] = m.PredecessorMgNr,
|
||||
["name"] = m.Name,
|
||||
["prefix"] = m.Prefix,
|
||||
["given_name"] = m.GivenName,
|
||||
["middle_names"] = m.MiddleName,
|
||||
["family_name"] = m.FamilyName,
|
||||
["suffix"] = m.Suffix,
|
||||
["attn"] = m.ForTheAttentionOf,
|
||||
["birthday"] = m.Birthday,
|
||||
["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null,
|
||||
["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null,
|
||||
@ -386,6 +421,7 @@ namespace Elwig.Helpers.Export {
|
||||
["zwstid"] = m.ZwstId,
|
||||
["lfbis_nr"] = m.LfbisNr,
|
||||
["ustid_nr"] = m.UstIdNr,
|
||||
["juridical_pers"] = m.IsJuridicalPerson,
|
||||
["volllieferant"] = m.IsVollLieferant,
|
||||
["buchführend"] = m.IsBuchführend,
|
||||
["organic"] = m.IsOrganic,
|
||||
@ -403,7 +439,7 @@ namespace Elwig.Helpers.Export {
|
||||
["country"] = m.CountryNum,
|
||||
},
|
||||
["billing_address"] = m.BillingAddress != null ? new JsonObject {
|
||||
["name"] = m.BillingAddress.Name,
|
||||
["name"] = m.BillingAddress.FullName,
|
||||
["address"] = m.BillingAddress.Address,
|
||||
["postal_dest"] = m.BillingAddress.PostalDestId,
|
||||
["country"] = m.BillingAddress.CountryNum,
|
||||
@ -427,16 +463,21 @@ namespace Elwig.Helpers.Export {
|
||||
};
|
||||
}
|
||||
|
||||
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>) ToMember(this JsonNode json) {
|
||||
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) {
|
||||
var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
|
||||
var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
|
||||
if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) {
|
||||
throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
|
||||
}
|
||||
return (new Member {
|
||||
MgNr = mgnr,
|
||||
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
|
||||
Name = json["name"]?.AsValue().GetValue<string>() ?? json["family_name"]!.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>(),
|
||||
FamilyName = json["family_name"]!.AsValue().GetValue<string>(),
|
||||
Suffix = json["suffix"]?.AsValue().GetValue<string>(),
|
||||
ForTheAttentionOf = json["attn"]?.AsValue().GetValue<string>(),
|
||||
Birthday = json["birthday"]?.AsValue().GetValue<string>(),
|
||||
EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
|
||||
ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
|
||||
@ -445,6 +486,7 @@ namespace Elwig.Helpers.Export {
|
||||
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
|
||||
LfbisNr = json["lfbis_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,
|
||||
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
|
||||
IsOrganic = json["organic"]?.AsValue().GetValue<bool>() ?? false,
|
||||
@ -456,13 +498,13 @@ namespace Elwig.Helpers.Export {
|
||||
CountryNum = json["address"]!["country"]!.AsValue().GetValue<int>(),
|
||||
PostalDestId = json["address"]!["postal_dest"]!.AsValue().GetValue<string>(),
|
||||
Address = json["address"]!["address"]!.AsValue().GetValue<string>(),
|
||||
DefaultKgNr = json["default_kgnr"]?.AsValue().GetValue<int>(),
|
||||
DefaultKgNr = kgnr,
|
||||
ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false,
|
||||
ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false,
|
||||
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||
}, json["billing_address"] is JsonObject a ? new BillingAddr {
|
||||
MgNr = mgnr,
|
||||
Name = a["name"]!.AsValue().GetValue<string>(),
|
||||
FullName = a["name"]!.AsValue().GetValue<string>(),
|
||||
CountryNum = a["country"]!.AsValue().GetValue<int>(),
|
||||
PostalDestId = a["postal_dest"]!.AsValue().GetValue<string>(),
|
||||
Address = a["address"]!.AsValue().GetValue<string>(),
|
||||
@ -496,13 +538,13 @@ namespace Elwig.Helpers.Export {
|
||||
};
|
||||
}
|
||||
|
||||
public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, Dictionary<int, List<WbRd>> riede) {
|
||||
public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
|
||||
var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
|
||||
var ried = json["ried"]?.AsValue().GetValue<string>();
|
||||
WbRd? rd = null;
|
||||
bool newRd = false;
|
||||
if (ried != null) {
|
||||
var rde = riede[kgnr] ?? throw new ArgumentException($"KG {kgnr:00000} is no WbKg");
|
||||
var rde = riede[kgnr] ?? throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
|
||||
rd = rde.FirstOrDefault(r => r.Name == ried);
|
||||
if (rd == null) {
|
||||
newRd = true;
|
||||
|
@ -197,7 +197,7 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
|
||||
public static void MailTo(string emailAddress) {
|
||||
MailTo(new string[] { emailAddress });
|
||||
MailTo([emailAddress]);
|
||||
}
|
||||
|
||||
public static void MailTo(IEnumerable<string> emailAddresses) {
|
||||
|
@ -38,12 +38,12 @@ namespace Elwig.Helpers.Weighing {
|
||||
}
|
||||
|
||||
var error = line[1..3];
|
||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||
if (error[0] == '0') {
|
||||
if (error[1] != '0') {
|
||||
throw new IOException($"Invalid response from scale (error code {error})");
|
||||
}
|
||||
} else if (error[0] == '1') {
|
||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||
switch (error[1]) {
|
||||
case '1': msg = "Allgemeiner Waagenfehler"; break;
|
||||
case '2': msg = "Waage in Überlast"; break;
|
||||
@ -53,8 +53,12 @@ namespace Elwig.Helpers.Weighing {
|
||||
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
|
||||
}
|
||||
throw new IOException($"Waagenfehler {error}: {msg}");
|
||||
} else if (error[0] == '2') {
|
||||
switch (error[1]) {
|
||||
case '0': msg = "Brutto negativ"; break;
|
||||
}
|
||||
throw new IOException($"Fehler {error}: {msg}");
|
||||
} else if (error[0] == '3') {
|
||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||
switch (error[1]) {
|
||||
case '1': msg = "Übertragunsfehler"; break;
|
||||
case '2': msg = "Ungültiger Befehl"; break;
|
||||
|
@ -35,7 +35,7 @@ namespace Elwig.Models.Dtos {
|
||||
|
||||
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
|
||||
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.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||
p.plz, o.name AS ort, m.address,
|
||||
@ -54,7 +54,7 @@ namespace Elwig.Models.Dtos {
|
||||
public class AreaComUnderDeliveryRow {
|
||||
public int MgNr;
|
||||
public string Name1;
|
||||
public string Name2;
|
||||
public string? Name2;
|
||||
public string Address;
|
||||
public int Plz;
|
||||
public string Locality;
|
||||
@ -88,7 +88,7 @@ namespace Elwig.Models.Dtos {
|
||||
[Column("name_1")]
|
||||
public required string Name1 { get; set; }
|
||||
[Column("name_2")]
|
||||
public required string Name2 { get; set; }
|
||||
public string? Name2 { get; set; }
|
||||
[Column("address")]
|
||||
public required string Address { get; set; }
|
||||
[Column("plz")]
|
||||
|
@ -50,7 +50,7 @@ namespace Elwig.Models.Dtos {
|
||||
|
||||
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
|
||||
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.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||
p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
|
||||
@ -79,7 +79,7 @@ namespace Elwig.Models.Dtos {
|
||||
public class CreditNoteRow {
|
||||
public int MgNr;
|
||||
public string Name1;
|
||||
public string Name2;
|
||||
public string? Name2;
|
||||
public string Address;
|
||||
public int Plz;
|
||||
public string Locality;
|
||||
@ -144,7 +144,7 @@ namespace Elwig.Models.Dtos {
|
||||
[Column("name_1")]
|
||||
public required string Name1 { get; set; }
|
||||
[Column("name_2")]
|
||||
public required string Name2 { get; set; }
|
||||
public string? Name2 { get; set; }
|
||||
[Column("address")]
|
||||
public required string Address { get; set; }
|
||||
[Column("plz")]
|
||||
|
62
Elwig/Models/Dtos/DeliveryAncmtListData.cs
Normal file
62
Elwig/Models/Dtos/DeliveryAncmtListData.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,16 +14,18 @@ namespace Elwig.Models.Dtos {
|
||||
("Pos", "Pos.", null, 10),
|
||||
("Date", "Datum", null, 20),
|
||||
("Time", "Zeit", null, 20),
|
||||
("DeliveryBranch", "Zweigstelle", null, 30),
|
||||
("MgNr", "MgNr.", null, 12),
|
||||
("Name1", "Name", null, 40),
|
||||
("Name2", "Vorname", null, 40),
|
||||
("MemberBranch", "Stamm-Zwst.", null, 30),
|
||||
("SortId", "Sorte", null, 10),
|
||||
("AttrId", "Attr.", null, 15),
|
||||
("CultId", "Bewirt.", null, 15),
|
||||
("QualId", "Qualität", null, 15),
|
||||
("Gradation", "Gradation", "°Oe|°KMW", 40),
|
||||
("Weight", "Gewicht", "kg", 20),
|
||||
("NetGross", "bto./nto.", null, 20),
|
||||
("IsNetWeight", "Gerebelt", null, 20),
|
||||
("HkId", "Herkunft", null, 20),
|
||||
("Modifiers", "Zu-/Abschläge", null, 40),
|
||||
("Comment", "Anmerkung", null, 60),
|
||||
@ -35,7 +37,7 @@ namespace Elwig.Models.Dtos {
|
||||
|
||||
public static async Task<DeliveryJournalData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||
return new((await query
|
||||
.Include(p => p.Delivery.Member)
|
||||
.Include(p => p.Delivery.Member.Branch)
|
||||
.Include(p => p.Delivery.Branch)
|
||||
.Include(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||
.Include(p => p.Variety)
|
||||
@ -53,10 +55,12 @@ namespace Elwig.Models.Dtos {
|
||||
public int Pos;
|
||||
public DateOnly Date;
|
||||
public TimeOnly? Time;
|
||||
public string DeliveryBranch;
|
||||
public int MgNr;
|
||||
public string Name1;
|
||||
public string Name2;
|
||||
public string? Name2;
|
||||
public string AdministrativeName;
|
||||
public string? MemberBranch;
|
||||
public string SortId;
|
||||
public string Variety;
|
||||
public string? AttrId;
|
||||
@ -71,7 +75,6 @@ namespace Elwig.Models.Dtos {
|
||||
public double Oe => Gradation.Oe;
|
||||
public int Weight { get; set; }
|
||||
public bool IsNetWeight;
|
||||
public string NetGross => IsNetWeight ? "n" : "b";
|
||||
public string? Modifiers;
|
||||
public string? Comment;
|
||||
|
||||
@ -83,10 +86,12 @@ namespace Elwig.Models.Dtos {
|
||||
Pos = p.DPNr;
|
||||
Date = d.Date;
|
||||
Time = d.Time;
|
||||
DeliveryBranch = d.Branch.Name;
|
||||
MgNr = m.MgNr;
|
||||
Name1 = m.FamilyName;
|
||||
Name1 = m.Name;
|
||||
Name2 = m.AdministrativeName2;
|
||||
AdministrativeName = m.AdministrativeName;
|
||||
MemberBranch = m.Branch?.Name;
|
||||
|
||||
SortId = p.SortId;
|
||||
Variety = p.Variety.Name;
|
||||
|
@ -37,7 +37,7 @@ namespace Elwig.Models.Dtos {
|
||||
|
||||
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
||||
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.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||
p.plz, o.name AS ort, m.address,
|
||||
@ -72,7 +72,7 @@ namespace Elwig.Models.Dtos {
|
||||
public class MemberDeliveryPerVariantRow {
|
||||
public int MgNr;
|
||||
public string Name1;
|
||||
public string Name2;
|
||||
public string? Name2;
|
||||
public string Address;
|
||||
public int Plz;
|
||||
public string Locality;
|
||||
@ -104,7 +104,7 @@ namespace Elwig.Models.Dtos {
|
||||
[Column("name_1")]
|
||||
public required string Name1 { get; set; }
|
||||
[Column("name_2")]
|
||||
public required string Name2 { get; set; }
|
||||
public string? Name2 { get; set; }
|
||||
[Column("address")]
|
||||
public required string Address { get; set; }
|
||||
[Column("plz")]
|
||||
|
@ -31,6 +31,7 @@ namespace Elwig.Models.Dtos {
|
||||
("EntryDate", "Eintritt", null, 20),
|
||||
("ExitDate", "Austritt", null, 20),
|
||||
("AreaCommitment", "geb. Fläche", "m²", 20),
|
||||
("AreaCommitmentsFiltered", "geb. Fläche", "Vtrg.|m²", 30),
|
||||
("UstIdNr", "UID", null, 25),
|
||||
("Iban", "IBAN", null, 45),
|
||||
("Bic", "BIC", null, 30),
|
||||
@ -45,8 +46,8 @@ namespace Elwig.Models.Dtos {
|
||||
base(MemberList.Name, MemberList.Name, string.Join(" / ", filterNames), rows, FieldNames) {
|
||||
}
|
||||
|
||||
public static async Task<MemberListData> FromQuery(IQueryable<Member> query, List<string> filterNames) {
|
||||
var areaCom = await query.ToDictionaryAsync(m => m.MgNr, m => Utils.ActiveAreaCommitments(m.AreaCommitments).Sum(c => c.Area));
|
||||
public static async Task<MemberListData> FromQuery(IQueryable<Member> query, List<string> filterNames, IEnumerable<string> filterAreaCom) {
|
||||
var areaComs = await query.ToDictionaryAsync(m => m.MgNr, m => Utils.ActiveAreaCommitments(m.AreaCommitments));
|
||||
return new((await query
|
||||
.Include(m => m.DefaultWbKg!.AtKg)
|
||||
.Include(m => m.Branch)
|
||||
@ -55,13 +56,17 @@ namespace Elwig.Models.Dtos {
|
||||
.Include(m => m.TelephoneNumbers)
|
||||
.Include(m => m.EmailAddresses)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync()).Select(m => new MemberListRow(m, areaCom[m.MgNr])), filterNames);
|
||||
.ToListAsync()).Select(m => new MemberListRow(m,
|
||||
areaComs[m.MgNr].Sum(c => c.Area),
|
||||
areaComs[m.MgNr].Where(c => filterAreaCom.Contains(c.VtrgId)).GroupBy(c => c.VtrgId).ToDictionary(g => g.Key, g => g.Sum(c => c.Area)))),
|
||||
filterNames);
|
||||
}
|
||||
}
|
||||
|
||||
public class MemberListRow {
|
||||
public int MgNr;
|
||||
public string? Name1;
|
||||
public string? AdminName1;
|
||||
public string? Name2;
|
||||
public string? DefaultKg;
|
||||
public string? Branch;
|
||||
@ -78,6 +83,7 @@ namespace Elwig.Models.Dtos {
|
||||
public string? Iban;
|
||||
public string? Bic;
|
||||
public int? AreaCommitment;
|
||||
public (string VtrgId, int Area)[] AreaCommitmentsFiltered;
|
||||
public bool IsBuchführend;
|
||||
public bool IsOrganic;
|
||||
public bool IsActive;
|
||||
@ -89,9 +95,10 @@ namespace Elwig.Models.Dtos {
|
||||
public string? AdditionalContact;
|
||||
public string? Comment;
|
||||
|
||||
public MemberListRow(Member m, int? areaCom = null) {
|
||||
public MemberListRow(Member m, int? areaCom = null, Dictionary<string, int>? filtered = null) {
|
||||
MgNr = m.MgNr;
|
||||
Name1 = m.FamilyName;
|
||||
Name1 = m.Name;
|
||||
AdminName1 = m.AdministrativeName1;
|
||||
Name2 = m.AdministrativeName2;
|
||||
DefaultKg = m.DefaultKg?.Name;
|
||||
Branch = m.Branch?.Name;
|
||||
@ -100,7 +107,7 @@ namespace Elwig.Models.Dtos {
|
||||
Plz = m.PostalDest.AtPlz!.Plz;
|
||||
Locality = m.PostalDest.AtPlz!.Ort.Name;
|
||||
if (m.BillingAddress is BillingAddr a) {
|
||||
BillingName = a.Name;
|
||||
BillingName = a.FullName;
|
||||
BillingAddress = a.Address;
|
||||
BillingPlz = a.PostalDest.AtPlz!.Plz;
|
||||
BillingLocality = a.PostalDest.AtPlz!.Ort.Name;
|
||||
@ -125,6 +132,7 @@ namespace Elwig.Models.Dtos {
|
||||
.Concat(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).Except([EmailAddress])));
|
||||
Comment = m.Comment;
|
||||
AreaCommitment = areaCom == 0 ? null : areaCom;
|
||||
AreaCommitmentsFiltered = filtered != null ? filtered.OrderBy(f => f.Key).Select(f => (f.Key, f.Value)).ToArray() : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace Elwig.Models.Dtos {
|
||||
|
||||
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
|
||||
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.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||
p.plz, o.name AS ort, m.address, m.business_shares,
|
||||
@ -53,7 +53,7 @@ namespace Elwig.Models.Dtos {
|
||||
[Column("name_1")]
|
||||
public required string Name1 { get; set; }
|
||||
[Column("name_2")]
|
||||
public required string Name2 { get; set; }
|
||||
public string? Name2 { get; set; }
|
||||
[Column("address")]
|
||||
public required string Address { get; set; }
|
||||
[Column("plz")]
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Elwig.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
@ -39,6 +40,16 @@ namespace Elwig.Models.Entities {
|
||||
[Column("comment")]
|
||||
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")]
|
||||
public virtual Member Member { get; private set; } = null!;
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace Elwig.Models.Entities {
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
public required string Name { get; set; }
|
||||
public required string FullName { get; set; }
|
||||
|
||||
[Column("country")]
|
||||
public int CountryNum { get; set; }
|
||||
|
@ -83,15 +83,15 @@ namespace Elwig.Models.Entities {
|
||||
get => Utils.DecFromDb(AmountValue, 2);
|
||||
}
|
||||
|
||||
[Column("ctime")]
|
||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public long CTime { get; private set; }
|
||||
[NotMapped]
|
||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||
|
||||
[Column("mtime")]
|
||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public long MTime { get; private set; }
|
||||
[NotMapped]
|
||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||
|
||||
[ForeignKey("Year, AvNr, MgNr")]
|
||||
public virtual PaymentMember Payment { get; private set; } = null!;
|
||||
|
@ -17,7 +17,6 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[Column("date")]
|
||||
public required string DateString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly Date {
|
||||
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
|
||||
@ -26,7 +25,6 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[Column("time")]
|
||||
public string? TimeString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public TimeOnly? Time {
|
||||
get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
|
||||
@ -63,6 +61,16 @@ namespace Elwig.Models.Entities {
|
||||
[Column("comment")]
|
||||
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")]
|
||||
public virtual Season Season { get; private set; } = null!;
|
||||
|
||||
@ -110,7 +118,7 @@ namespace Elwig.Models.Entities {
|
||||
public int SearchScore(IEnumerable<string> keywords) {
|
||||
var list = new string?[] {
|
||||
LsNr, Time?.ToString("HH:mm"),
|
||||
Member.FamilyName, Member.MiddleName, Member.GivenName, Member.BillingAddress?.Name,
|
||||
Member.Name, Member.MiddleName, Member.GivenName, Member.BillingAddress?.FullName,
|
||||
Comment
|
||||
}.ToList();
|
||||
list.AddRange(Parts.Select(p => p.Comment).Distinct());
|
||||
|
56
Elwig/Models/Entities/DeliveryAncmt.cs
Normal file
56
Elwig/Models/Entities/DeliveryAncmt.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -128,6 +128,16 @@ namespace Elwig.Models.Entities {
|
||||
[Column("comment")]
|
||||
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))]
|
||||
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
|
||||
|
||||
|
71
Elwig/Models/Entities/DeliverySchedule.cs
Normal file
71
Elwig/Models/Entities/DeliverySchedule.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
26
Elwig/Models/Entities/DeliveryScheduleWineVar.cs
Normal file
26
Elwig/Models/Entities/DeliveryScheduleWineVar.cs
Normal 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!;
|
||||
}
|
||||
}
|
@ -14,52 +14,45 @@ namespace Elwig.Models.Entities {
|
||||
[Column("predecessor_mgnr")]
|
||||
public int? PredecessorMgNr { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
[Column("prefix")]
|
||||
public string? Prefix { get; set; }
|
||||
|
||||
[Column("given_name")]
|
||||
public required string GivenName { get; set; }
|
||||
public string? GivenName { get; set; }
|
||||
|
||||
[Column("middle_names")]
|
||||
public string? MiddleName { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public string[] MiddleNames {
|
||||
get => (MiddleName != null) ? MiddleName.Split(" ") : [];
|
||||
set => MiddleName = (value.Length > 0) ? string.Join(" ", value) : null;
|
||||
}
|
||||
|
||||
[Column("family_name")]
|
||||
public required string FamilyName { get; set; }
|
||||
|
||||
[Column("suffix")]
|
||||
public string? Suffix { get; set; }
|
||||
|
||||
public string Name =>
|
||||
(Prefix != null ? Prefix + " " : "") +
|
||||
GivenName + " " +
|
||||
(MiddleName != null ? MiddleName + " " : "") +
|
||||
FamilyName +
|
||||
(Suffix != null ? " " + Suffix : "");
|
||||
[Column("attn")]
|
||||
public string? ForTheAttentionOf { get; set; }
|
||||
|
||||
public string ShortName => GivenName + " " + FamilyName;
|
||||
|
||||
public string AdministrativeName => AdministrativeName1 + " " + AdministrativeName2;
|
||||
|
||||
public string AdministrativeName1 => FamilyName.Replace('ß', 'ẞ').ToUpper();
|
||||
|
||||
public string AdministrativeName2 =>
|
||||
(Prefix != null ? Prefix + " " : "") +
|
||||
GivenName +
|
||||
(MiddleName != null ? " " + MiddleName : "") +
|
||||
(Suffix != null ? " " + Suffix : "");
|
||||
[NotMapped]
|
||||
public string FullName => IsJuridicalPerson ? Name : string.Join(" ", ((string?[])[Prefix, GivenName, MiddleName, Name, Suffix]).Where(s => !string.IsNullOrWhiteSpace(s)));
|
||||
[NotMapped]
|
||||
public string ShortName => (!string.IsNullOrWhiteSpace(GivenName) ? $"{GivenName} " : "") + Name;
|
||||
[NotMapped]
|
||||
public string AdministrativeName => AdministrativeName1 + (!string.IsNullOrWhiteSpace(AdministrativeName2) ? $" {AdministrativeName2}" : "");
|
||||
[NotMapped]
|
||||
public string AdministrativeName1 => IsJuridicalPerson ? Name : Name.Replace('ß', 'ẞ').ToUpper();
|
||||
[NotMapped]
|
||||
public string? AdministrativeName2 => IsJuridicalPerson ? null : string.Join(" ", ((string?[])[Prefix, GivenName, MiddleName, Suffix]).Where(s => !string.IsNullOrWhiteSpace(s)));
|
||||
|
||||
[Column("birthday")]
|
||||
public string? Birthday { get; set; }
|
||||
|
||||
[Column("entry_date")]
|
||||
public string? EntryDateString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly? EntryDate {
|
||||
get => EntryDateString != null ? DateOnly.ParseExact(EntryDateString, "yyyy-MM-dd") : null;
|
||||
@ -68,7 +61,6 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[Column("exit_date")]
|
||||
public string? ExitDateString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly? ExitDate {
|
||||
get => ExitDateString != null ? DateOnly.ParseExact(ExitDateString, "yyyy-MM-dd") : null;
|
||||
@ -90,6 +82,9 @@ namespace Elwig.Models.Entities {
|
||||
[Column("ustid_nr")]
|
||||
public string? UstIdNr { get; set; }
|
||||
|
||||
[Column("juridical_pers")]
|
||||
public bool IsJuridicalPerson { get; set; }
|
||||
|
||||
[Column("volllieferant")]
|
||||
public bool IsVollLieferant { get; set; }
|
||||
|
||||
@ -146,10 +141,19 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[ForeignKey("DefaultKgNr")]
|
||||
public virtual WbKg? DefaultWbKg { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
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")]
|
||||
public virtual Branch? Branch { get; private set; }
|
||||
|
||||
@ -179,8 +183,8 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
public int SearchScore(IEnumerable<string> keywords) {
|
||||
return Utils.GetSearchScore([
|
||||
FamilyName, MiddleName, GivenName,
|
||||
BillingAddress?.Name,
|
||||
Name, MiddleName, GivenName,
|
||||
BillingAddress?.FullName,
|
||||
Comment,
|
||||
], keywords);
|
||||
}
|
||||
|
@ -11,12 +11,31 @@ namespace Elwig.Models.Entities {
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("amount")]
|
||||
public long AmountValue { get; set; }
|
||||
[Column("mod_abs")]
|
||||
public long? ModAbsValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal Amount {
|
||||
get => Utils.DecFromDb(AmountValue, 2);
|
||||
set => AmountValue = Utils.DecToDb(value, 2);
|
||||
public decimal? ModAbs {
|
||||
get => ModAbsValue != null ? Utils.DecFromDb(ModAbsValue.Value, 2) : null;
|
||||
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")]
|
||||
|
@ -35,9 +35,9 @@ namespace Elwig.Models.Entities {
|
||||
public bool TestVariant { get; set; }
|
||||
|
||||
[Column("calc_time")]
|
||||
public int? CalcTimeUnix { get; set; }
|
||||
public long? CalcTimeUnix { get; set; }
|
||||
[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")]
|
||||
public string? Comment { get; set; }
|
||||
@ -45,6 +45,16 @@ namespace Elwig.Models.Entities {
|
||||
[Column("data")]
|
||||
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")]
|
||||
public virtual Season Season { get; private set; } = null!;
|
||||
|
||||
|
@ -2,7 +2,7 @@ using Elwig.Models.Entities;
|
||||
|
||||
namespace Elwig.Models {
|
||||
public interface IAddress {
|
||||
string Name { get; }
|
||||
string FullName { get; }
|
||||
string Address { get; }
|
||||
PostalDest PostalDest { get; }
|
||||
}
|
||||
|
91
Elwig/Resources/Sql/25-26.sql
Normal file
91
Elwig/Resources/Sql/25-26.sql
Normal 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;
|
48
Elwig/Resources/Sql/26-27.sql
Normal file
48
Elwig/Resources/Sql/26-27.sql
Normal 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)
|
13
Elwig/Resources/Sql/27-28.sql
Normal file
13
Elwig/Resources/Sql/27-28.sql
Normal 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;
|
183
Elwig/Services/DeliveryAncmtService.cs
Normal file
183
Elwig/Services/DeliveryAncmtService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
183
Elwig/Services/DeliveryScheduleService.cs
Normal file
183
Elwig/Services/DeliveryScheduleService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,10 @@ using System.Net.Http;
|
||||
namespace Elwig.Services {
|
||||
public static class DeliveryService {
|
||||
|
||||
public enum ExportSubject {
|
||||
FromFilters, FromToday, FromSeasonAndBranch, Selected,
|
||||
};
|
||||
|
||||
public static async Task<Member?> GetMemberAsync(int mgnr) {
|
||||
using var ctx = new AppDbContext();
|
||||
return await ctx.Members
|
||||
@ -522,23 +526,31 @@ namespace Elwig.Services {
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
|
||||
public static async Task GenerateDeliveryJournal(this DeliveryAdminViewModel vm, int modeWho, ExportMode exportMode) {
|
||||
public static async Task GenerateDeliveryJournal(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||
using var ctx = new AppDbContext();
|
||||
IQueryable<DeliveryPart> query;
|
||||
List<string> filterNames = [];
|
||||
if (modeWho == 0) {
|
||||
if (subject == ExportSubject.FromFilters) {
|
||||
var (f, _, q, _, _) = await vm.GetFilters(ctx);
|
||||
query = q;
|
||||
filterNames.AddRange(f);
|
||||
} else if (modeWho == 2) {
|
||||
query = ctx.DeliveryParts
|
||||
.Where(p => p.Year == Utils.CurrentLastSeason && p.Delivery.ZwstId == App.ZwstId);
|
||||
filterNames.AddRange([$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
|
||||
} else {
|
||||
} else if (subject == ExportSubject.FromToday) {
|
||||
var date = $"{Utils.Today:yyyy-MM-dd}";
|
||||
query = ctx.DeliveryParts
|
||||
.Where(p => p.Delivery.DateString == date);
|
||||
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
|
||||
} else if (subject == ExportSubject.FromSeasonAndBranch) {
|
||||
query = ctx.DeliveryParts
|
||||
.Where(p => p.Year == Utils.CurrentLastSeason && p.Delivery.ZwstId == App.ZwstId);
|
||||
filterNames.AddRange([$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
|
||||
} else if (subject == ExportSubject.Selected) {
|
||||
var lsnr = vm.SelectedDelivery?.LsNr;
|
||||
if (lsnr == null) return;
|
||||
query = ctx.DeliveryParts
|
||||
.Where(p => p.Delivery.LsNr == lsnr);
|
||||
filterNames.Add(lsnr);
|
||||
} else {
|
||||
throw new ArgumentException("Invalid value for ExportSubject");
|
||||
}
|
||||
|
||||
query = query
|
||||
@ -547,7 +559,7 @@ namespace Elwig.Services {
|
||||
.ThenBy(p => p.Delivery.LsNr)
|
||||
.ThenBy(p => p.DPNr);
|
||||
|
||||
if (exportMode == ExportMode.SaveList) {
|
||||
if (mode == ExportMode.SaveList) {
|
||||
var d = new SaveFileDialog() {
|
||||
FileName = $"{DeliveryJournal.Name}.ods",
|
||||
DefaultExt = "ods",
|
||||
@ -565,11 +577,11 @@ namespace Elwig.Services {
|
||||
}
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
} else if (exportMode == ExportMode.Export) {
|
||||
} else if (mode == ExportMode.Export) {
|
||||
var d = new SaveFileDialog() {
|
||||
FileName = $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||
DefaultExt = "zip",
|
||||
Filter = "ZIP-Datei (*.zip)|*.zip",
|
||||
FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||
DefaultExt = "elwig.zip",
|
||||
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
||||
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
|
||||
};
|
||||
if (d.ShowDialog() == true) {
|
||||
@ -587,7 +599,7 @@ namespace Elwig.Services {
|
||||
}
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
} else if (exportMode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||
@ -610,6 +622,8 @@ namespace Elwig.Services {
|
||||
}
|
||||
} catch (HttpRequestException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (TaskCanceledException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -619,7 +633,7 @@ namespace Elwig.Services {
|
||||
try {
|
||||
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
||||
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
|
||||
await Utils.ExportDocument(doc, exportMode);
|
||||
await Utils.ExportDocument(doc, mode);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -627,26 +641,28 @@ namespace Elwig.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task GenerateWineQualityStatistics(this DeliveryAdminViewModel vm, int modeWho, ExportMode exportMode) {
|
||||
public static async Task GenerateWineQualityStatistics(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||
using var ctx = new AppDbContext();
|
||||
IQueryable<DeliveryPart> query;
|
||||
List<string> filterNames = [];
|
||||
if (modeWho == 0) {
|
||||
if (subject == ExportSubject.FromFilters) {
|
||||
var (f, _, q, _, _) = await vm.GetFilters(ctx);
|
||||
query = q;
|
||||
filterNames.AddRange(f);
|
||||
} else {
|
||||
} else if (subject == ExportSubject.FromToday) {
|
||||
var date = $"{Utils.Today:yyyy-MM-dd}";
|
||||
query = ctx.DeliveryParts
|
||||
.Where(p => p.Delivery.DateString == date);
|
||||
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
|
||||
} else {
|
||||
throw new ArgumentException("Invalid value for ExportSubject");
|
||||
}
|
||||
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
|
||||
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
|
||||
await Utils.ExportDocument(doc, exportMode);
|
||||
await Utils.ExportDocument(doc, mode);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ using System.Net.Http;
|
||||
namespace Elwig.Services {
|
||||
public static class MemberService {
|
||||
|
||||
public enum ExportSubject {
|
||||
All, AllActive, FromFilters, Selected,
|
||||
};
|
||||
|
||||
public static async Task InitInputs(this MemberAdminViewModel vm) {
|
||||
using var ctx = new AppDbContext();
|
||||
vm.MgNrString = $"{await ctx.NextMgNr()}";
|
||||
@ -51,11 +55,13 @@ namespace Elwig.Services {
|
||||
vm.IsMemberSelected = true;
|
||||
vm.MgNrString = $"{m.MgNr}";
|
||||
vm.PredecessorMgNrString = $"{m.PredecessorMgNr}";
|
||||
vm.IsJuridicalPerson = m.IsJuridicalPerson;
|
||||
vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
|
||||
vm.Prefix = m.Prefix;
|
||||
vm.GivenName = m.GivenName;
|
||||
vm.FamilyName = m.FamilyName;
|
||||
vm.Name = m.Name;
|
||||
vm.Suffix = m.Suffix;
|
||||
vm.ForTheAttentionOf = m.ForTheAttentionOf;
|
||||
vm.Birthday = (m.Birthday != null) ? string.Join(".", m.Birthday.Split("-").Reverse()) : null;
|
||||
if (m.Birthday?.Length == 10) {
|
||||
vm.Age = Utils.GetAge(DateOnly.ParseExact(m.Birthday, "yyyy-MM-dd")).ToString();
|
||||
@ -105,7 +111,7 @@ namespace Elwig.Services {
|
||||
|
||||
var billingAddr = m.BillingAddress;
|
||||
if (billingAddr != null) {
|
||||
vm.BillingName = billingAddr.Name;
|
||||
vm.BillingName = billingAddr.FullName;
|
||||
vm.BillingAddress = billingAddr.Address;
|
||||
if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
|
||||
vm.BillingPlzString = $"{b.Plz}";
|
||||
@ -146,8 +152,12 @@ namespace Elwig.Services {
|
||||
vm.StatusDeliveriesThisSeasonToolTip = d2Grid;
|
||||
|
||||
var c = m.ActiveAreaCommitments(ctx, Utils.CurrentLastSeason);
|
||||
var s = await ctx.Seasons.FindAsync(await ctx.Seasons.MaxAsync(s => s.Year));
|
||||
var (text, grid) = await AreaComService.GenerateToolTip(c, s?.MaxKgPerHa ?? 10_000);
|
||||
int 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.StatusAreaCommitment = text;
|
||||
vm.StatusAreaCommitmentToolTip = grid;
|
||||
@ -359,19 +369,26 @@ namespace Elwig.Services {
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
|
||||
public static async Task GenerateMemberList(this MemberAdminViewModel vm, int modeWho, ExportMode exportMode) {
|
||||
public static async Task GenerateMemberList(this MemberAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||
using var ctx = new AppDbContext();
|
||||
IQueryable<Member> query;
|
||||
List<string> filterNames = [];
|
||||
if (modeWho == 0) {
|
||||
if (subject == ExportSubject.All) {
|
||||
query = ctx.Members;
|
||||
} else if (subject == ExportSubject.AllActive) {
|
||||
query = ctx.Members.Where(m => m.IsActive);
|
||||
filterNames.Add("aktive Mitglieder");
|
||||
} else if (modeWho == 1) {
|
||||
} else if (subject == ExportSubject.FromFilters) {
|
||||
var (f, q, _) = await vm.GetFilters(ctx);
|
||||
query = q;
|
||||
filterNames.AddRange(f);
|
||||
} else if (subject == ExportSubject.Selected) {
|
||||
var mgnr = vm.SelectedMember?.MgNr;
|
||||
if (mgnr == null) return;
|
||||
query = ctx.Members.Where(m => m.MgNr == mgnr);
|
||||
filterNames.Add($"{mgnr}");
|
||||
} else {
|
||||
query = ctx.Members;
|
||||
throw new ArgumentException("Invalid value for ExportSubject");
|
||||
}
|
||||
|
||||
if (vm.MemberListOrderByMgNr) {
|
||||
@ -381,19 +398,19 @@ namespace Elwig.Services {
|
||||
} else if (vm.MemberListOrderByName) {
|
||||
query = query
|
||||
.OrderBy(m => m.Branch!.Name)
|
||||
.ThenBy(m => m.FamilyName)
|
||||
.ThenBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr);
|
||||
} else if (vm.MemberListOrderByOrt) {
|
||||
query = query
|
||||
.OrderBy(m => m.Branch!.Name)
|
||||
.ThenBy(m => m.DefaultWbKg!.AtKg.Name)
|
||||
.ThenBy(m => m.FamilyName)
|
||||
.ThenBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr);
|
||||
}
|
||||
|
||||
if (exportMode == ExportMode.SaveList) {
|
||||
if (mode == ExportMode.SaveList) {
|
||||
var d = new SaveFileDialog() {
|
||||
FileName = $"{MemberList.Name}.ods",
|
||||
DefaultExt = "ods",
|
||||
@ -403,7 +420,7 @@ namespace Elwig.Services {
|
||||
if (d.ShowDialog() == true) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
var data = await MemberListData.FromQuery(query, filterNames);
|
||||
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
||||
using var ods = new OdsFile(d.FileName);
|
||||
await ods.AddTable(data);
|
||||
} catch (Exception exc) {
|
||||
@ -411,51 +428,62 @@ namespace Elwig.Services {
|
||||
}
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
} else if (exportMode == ExportMode.Export) {
|
||||
} else if (mode == ExportMode.Export) {
|
||||
var d = new SaveFileDialog() {
|
||||
FileName = $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||
DefaultExt = "zip",
|
||||
Filter = "ZIP-Datei (*.zip)|*.zip",
|
||||
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||
DefaultExt = ".elwig.zip",
|
||||
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
||||
Title = $"{MemberList.Name} speichern unter - Elwig"
|
||||
};
|
||||
if (d.ShowDialog() == true) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
await ElwigData.Export(d.FileName, await query
|
||||
var members = await query
|
||||
.OrderBy(m => m.MgNr)
|
||||
.Include(m => m.BillingAddress)
|
||||
.Include(m => m.TelephoneNumbers)
|
||||
.Include(m => m.EmailAddresses)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync(), filterNames);
|
||||
.ToListAsync();
|
||||
var areaComs = await query
|
||||
.SelectMany(m => m.AreaCommitments)
|
||||
.Include(c => c.Rd)
|
||||
.ToListAsync();
|
||||
await ElwigData.Export(d.FileName, members, areaComs, filterNames);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
} else if (exportMode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||
var path = Path.Combine(App.TempPath, filename);
|
||||
var list = await query
|
||||
var members = await query
|
||||
.OrderBy(m => m.MgNr)
|
||||
.Include(m => m.BillingAddress)
|
||||
.Include(m => m.TelephoneNumbers)
|
||||
.Include(m => m.EmailAddresses)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
if (list.Count == 0) {
|
||||
var areaComs = await query
|
||||
.SelectMany(m => m.AreaCommitments)
|
||||
.Include(c => c.Rd)
|
||||
.ToListAsync();
|
||||
if (members.Count == 0) {
|
||||
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} else {
|
||||
await ElwigData.Export(path, list, filterNames);
|
||||
await ElwigData.Export(path, members, areaComs, filterNames);
|
||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
MessageBox.Show($"Hochladen von {list.Count} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
||||
MessageBox.Show($"Hochladen von {members.Count} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
} catch (HttpRequestException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (TaskCanceledException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -463,9 +491,9 @@ namespace Elwig.Services {
|
||||
} else {
|
||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||
try {
|
||||
var data = await MemberListData.FromQuery(query, filterNames);
|
||||
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
||||
using var doc = new MemberList(string.Join(" / ", filterNames), data);
|
||||
await Utils.ExportDocument(doc, exportMode);
|
||||
await Utils.ExportDocument(doc, mode);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -479,10 +507,12 @@ namespace Elwig.Services {
|
||||
var m = new Member {
|
||||
MgNr = oldMgNr ?? newMgNr,
|
||||
PredecessorMgNr = vm.PredecessorMgNr,
|
||||
Prefix = string.IsNullOrEmpty(vm.Prefix) ? null : vm.Prefix,
|
||||
GivenName = vm.GivenName!,
|
||||
FamilyName = vm.FamilyName!,
|
||||
Suffix = string.IsNullOrEmpty(vm.Suffix) ? null : vm.Suffix,
|
||||
IsJuridicalPerson = vm.IsJuridicalPerson,
|
||||
Prefix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Prefix) ? null : vm.Prefix,
|
||||
GivenName = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.GivenName) ? null : vm.GivenName,
|
||||
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()),
|
||||
IsDeceased = vm.IsDeceased,
|
||||
CountryNum = 40, // Austria AT AUT
|
||||
@ -522,7 +552,7 @@ namespace Elwig.Services {
|
||||
var p = vm.BillingOrt;
|
||||
ctx.Add(new BillingAddr {
|
||||
MgNr = m.MgNr,
|
||||
Name = vm.BillingName,
|
||||
FullName = vm.BillingName,
|
||||
Address = vm.BillingAddress ?? "",
|
||||
CountryNum = p.CountryNum,
|
||||
PostalDestId = p.Id,
|
||||
@ -531,7 +561,7 @@ namespace Elwig.Services {
|
||||
|
||||
ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(t => t.MgNr == oldMgNr));
|
||||
ctx.AddRange(vm.PhoneNrs
|
||||
.Where(input => input.Number != null)
|
||||
.Where(input => input.Number != null && input.Number != "")
|
||||
.Select((input, i) => new MemberTelNr {
|
||||
MgNr = m.MgNr,
|
||||
Nr = i + 1,
|
||||
|
70
Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs
Normal file
70
Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
101
Elwig/ViewModels/DeliveryScheduleAdminViewModel.cs
Normal file
101
Elwig/ViewModels/DeliveryScheduleAdminViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ namespace Elwig.ViewModels {
|
||||
[ObservableProperty]
|
||||
private bool _enableSearchInputs = true;
|
||||
[ObservableProperty]
|
||||
public IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ];
|
||||
private IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ];
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _memberListOrderByMgNr;
|
||||
@ -54,14 +54,18 @@ namespace Elwig.ViewModels {
|
||||
private string? _predecessorMgNrString;
|
||||
public int? PredecessorMgNr => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null;
|
||||
[ObservableProperty]
|
||||
private bool _isJuridicalPerson;
|
||||
[ObservableProperty]
|
||||
private string? _prefix;
|
||||
[ObservableProperty]
|
||||
private string? _givenName;
|
||||
[ObservableProperty]
|
||||
private string? _familyName;
|
||||
private string? _name;
|
||||
[ObservableProperty]
|
||||
private string? _suffix;
|
||||
[ObservableProperty]
|
||||
private string? _forTheAttentionOf;
|
||||
[ObservableProperty]
|
||||
private string? _birthday;
|
||||
[ObservableProperty]
|
||||
private string? _age;
|
||||
@ -176,5 +180,9 @@ namespace Elwig.ViewModels {
|
||||
private Visibility _controlButtonsVisibility = Visibility.Visible;
|
||||
[ObservableProperty]
|
||||
private Visibility _editingButtonsVisibility = Visibility.Hidden;
|
||||
[ObservableProperty]
|
||||
private Visibility _personalNameVisibility = Visibility.Visible;
|
||||
[ObservableProperty]
|
||||
private Visibility _juridicalNameVisibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,10 @@ namespace Elwig.Windows {
|
||||
|
||||
protected void ValidateRequiredInputs() {
|
||||
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);
|
||||
Valid[input] = false;
|
||||
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
|
||||
@ -402,7 +405,7 @@ namespace Elwig.Windows {
|
||||
|
||||
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
|
||||
if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
|
||||
MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
MessageBox.Show(res.ErrorContent?.ToString(), msg ?? res.ErrorContent?.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return res.IsValid;
|
||||
}
|
||||
|
||||
|
@ -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:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@ -255,7 +256,7 @@
|
||||
</GroupBox>
|
||||
</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>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
|
@ -117,7 +117,7 @@ namespace Elwig.Windows {
|
||||
FinishInputFilling();
|
||||
}
|
||||
|
||||
private async void InitInputs() {
|
||||
private async Task InitInputs() {
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
await ViewModel.InitInputs();
|
||||
@ -159,14 +159,14 @@ namespace Elwig.Windows {
|
||||
NewAreaCommitmentButton_Click(null, null);
|
||||
}
|
||||
|
||||
private void NewAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
private async void NewAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
IsCreating = true;
|
||||
AreaCommitmentList.IsEnabled = false;
|
||||
AreaCommitmentList.SelectedItem = null;
|
||||
HideAreaCommitmentNewEditDeleteButtons();
|
||||
ShowAreaCommitmentSaveResetCancelButtons();
|
||||
UnlockInputs();
|
||||
InitInputs();
|
||||
await InitInputs();
|
||||
LockSearchInputs();
|
||||
}
|
||||
|
||||
@ -197,17 +197,25 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private async void DeleteAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem;
|
||||
if (a == null) return;
|
||||
|
||||
if (AreaCommitmentList.SelectedItem is not AreaCom a)
|
||||
return;
|
||||
var r = MessageBox.Show(
|
||||
$"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
|
||||
"Flächenbindung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
|
||||
if (r == MessageBoxResult.OK) {
|
||||
using var ctx = new AppDbContext();
|
||||
ctx.Remove(a);
|
||||
await ctx.SaveChangesAsync();
|
||||
await RefreshList();
|
||||
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, "Flächenbindung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,11 +254,11 @@ namespace Elwig.Windows {
|
||||
AreaCommitmentResetButton_Click(null, null);
|
||||
}
|
||||
|
||||
private void AreaCommitmentResetButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
private async void AreaCommitmentResetButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
if (IsEditing) {
|
||||
RefreshInputs();
|
||||
} else if (IsCreating) {
|
||||
InitInputs();
|
||||
await InitInputs();
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
|
@ -124,6 +124,11 @@
|
||||
<MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Export">
|
||||
<MenuItem x:Name="Menu_Export_ExportSelected" Header="...von ausgewählter Lieferung speichern..." IsEnabled="False"
|
||||
Click="Menu_Export_ExportSelected_Click"/>
|
||||
<MenuItem x:Name="Menu_Export_UploadSelected" Header="...von ausgewählter Lieferung hochladen" IsEnabled="False"
|
||||
Click="Menu_Export_UploadSelected_Click"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="Menu_Export_ExportFilters" Header="...aus Filtern speichern..."
|
||||
Click="Menu_Export_ExportFilters_Click"/>
|
||||
<MenuItem x:Name="Menu_Export_UploadFilters" Header="...aus Filtern hochladen"
|
||||
@ -192,6 +197,7 @@
|
||||
HorizontalAlignment="Right" Margin="0,24,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
Checked="AllSeasonsInput_Changed" Unchecked="AllSeasonsInput_Changed"/>
|
||||
<DataGrid x:Name="DeliveryList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
ItemsSource="{Binding Deliveries, Mode=TwoWay}" SelectedItem="{Binding SelectedDelivery, Mode=TwoWay}"
|
||||
SelectionChanged="DeliveryList_SelectionChanged"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
|
||||
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
|
||||
@ -599,7 +605,7 @@
|
||||
</GroupBox>
|
||||
</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>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
|
@ -175,33 +175,55 @@ namespace Elwig.Windows {
|
||||
}
|
||||
}
|
||||
|
||||
private async void Menu_DeliveryJournal_SaveToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(1, ExportMode.SaveList); }
|
||||
private async void Menu_DeliveryJournal_SavePdfToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(1, ExportMode.SavePdf); }
|
||||
private async void Menu_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(1, ExportMode.Show); }
|
||||
private async void Menu_Export_ExportSeason_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(2, ExportMode.Export); }
|
||||
private async void Menu_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(1, ExportMode.Print); }
|
||||
private async void Menu_DeliveryJournal_SaveFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(0, ExportMode.SaveList); }
|
||||
private async void Menu_DeliveryJournal_SavePdfFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(0, ExportMode.SavePdf); }
|
||||
private async void Menu_DeliveryJournal_ShowFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(0, ExportMode.Show); }
|
||||
private async void Menu_DeliveryJournal_PrintFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(0, ExportMode.Print); }
|
||||
private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateDeliveryJournal(0, ExportMode.Export); }
|
||||
private async void Menu_DeliveryJournal_SaveToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SaveList);
|
||||
private async void Menu_DeliveryJournal_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
|
||||
private async void Menu_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
|
||||
private async void Menu_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
|
||||
private async void Menu_DeliveryJournal_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
|
||||
private async void Menu_DeliveryJournal_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
|
||||
private async void Menu_DeliveryJournal_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
|
||||
private async void Menu_DeliveryJournal_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
|
||||
|
||||
private async void Menu_Export_ExportSeason_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Export);
|
||||
private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Export);
|
||||
private async void Menu_Export_ExportSelected_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.Selected, ExportMode.Export);
|
||||
|
||||
private async void Menu_Export_UploadSeason_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateDeliveryJournal(2, ExportMode.Upload);
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
|
||||
}
|
||||
|
||||
private async void Menu_Export_UploadFilters_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateDeliveryJournal(0, ExportMode.Upload);
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Upload);
|
||||
}
|
||||
private async void Menu_Export_UploadSelected_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
|
||||
}
|
||||
|
||||
private async void Menu_WineQualityStatistics_ShowToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(1, ExportMode.Show); }
|
||||
private async void Menu_WineQualityStatistics_SavePdfToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(1, ExportMode.SavePdf); }
|
||||
private async void Menu_WineQualityStatistics_PrintToday_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(1, ExportMode.Print); }
|
||||
private async void Menu_WineQualityStatistics_ShowFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(0, ExportMode.Show); }
|
||||
private async void Menu_WineQualityStatistics_SavePdfFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(0, ExportMode.SavePdf); }
|
||||
private async void Menu_WineQualityStatistics_PrintFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateWineQualityStatistics(0, ExportMode.Print); }
|
||||
private async void Menu_WineQualityStatistics_ShowToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
|
||||
private async void Menu_WineQualityStatistics_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
|
||||
private async void Menu_WineQualityStatistics_PrintToday_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
|
||||
private async void Menu_WineQualityStatistics_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
|
||||
private async void Menu_WineQualityStatistics_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
|
||||
private async void Menu_WineQualityStatistics_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
|
||||
|
||||
private async void Menu_WineQualityStatistics_Mode_Click(object sender, RoutedEventArgs evt) {
|
||||
Menu_WineQualityStatistics.IsSubmenuOpen = true;
|
||||
@ -412,7 +434,7 @@ namespace Elwig.Windows {
|
||||
.Where(m => m.IsActive || !IsCreating)
|
||||
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||
.Include(m => m.DefaultWbKg!.AtKg)
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ToListAsync());
|
||||
ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
|
||||
@ -598,12 +620,16 @@ namespace Elwig.Windows {
|
||||
Menu_DeliveryNote_SavePdf.IsEnabled = !IsEditing && !IsCreating;
|
||||
Menu_DeliveryNote_Print.IsEnabled = !IsEditing && !IsCreating;
|
||||
Menu_DeliveryNote_Email.IsEnabled = !IsEditing && !IsCreating && App.Config.Smtp != null && d.Member.EmailAddresses.Count > 0;
|
||||
Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
|
||||
Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
|
||||
} else {
|
||||
DeleteDeliveryButton.IsEnabled = false;
|
||||
Menu_DeliveryNote_Show.IsEnabled = false;
|
||||
Menu_DeliveryNote_SavePdf.IsEnabled = false;
|
||||
Menu_DeliveryNote_Print.IsEnabled = false;
|
||||
Menu_DeliveryNote_Email.IsEnabled = false;
|
||||
Menu_Export_ExportSelected.IsEnabled = false;
|
||||
Menu_Export_UploadSelected.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -738,7 +764,7 @@ namespace Elwig.Windows {
|
||||
.Where(m => m.IsActive || !ViewModel.IsReceipt)
|
||||
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||
.Include(m => m.DefaultWbKg!.AtKg)
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ToListAsync());
|
||||
if (DeliveryList.SelectedItem is not Delivery d) {
|
||||
@ -784,7 +810,7 @@ namespace Elwig.Windows {
|
||||
.Where(m => m.IsActive || !ViewModel.IsReceipt)
|
||||
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||
.Include(m => m.DefaultWbKg!.AtKg)
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ToListAsync());
|
||||
IsCreating = true;
|
||||
@ -1217,7 +1243,7 @@ namespace Elwig.Windows {
|
||||
|
||||
private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
if (WineVarietyInput.SelectedItem is WineVar s)
|
||||
ViewModel.SortId = s.SortId;
|
||||
ViewModel.SortId = s.SortId;
|
||||
}
|
||||
|
||||
private void UpdateWineQualityLevels() {
|
||||
|
323
Elwig/Windows/DeliveryAncmtAdminWindow.xaml
Normal file
323
Elwig/Windows/DeliveryAncmtAdminWindow.xaml
Normal file
@ -0,0 +1,323 @@
|
||||
<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.25*" 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='ddd.'}" Width="20"/>
|
||||
<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="" 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>
|
465
Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs
Normal file
465
Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
284
Elwig/Windows/DeliveryScheduleAdminWindow.xaml
Normal file
284
Elwig/Windows/DeliveryScheduleAdminWindow.xaml
Normal 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="ScheduleDateInput_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>
|
311
Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs
Normal file
311
Elwig/Windows/DeliveryScheduleAdminWindow.xaml.cs
Normal 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 void ScheduleDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
@ -166,7 +166,7 @@ namespace Elwig.Windows {
|
||||
}
|
||||
ControlUtils.RenewItemsSource(MemberCustomInput, await ctx.Members
|
||||
.Where(m => m.IsActive)
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.Include(m => m.Branch)
|
||||
.Include(m => m.DefaultWbKg!.AtKg)
|
||||
@ -272,7 +272,7 @@ namespace Elwig.Windows {
|
||||
private void SelectDocumentButton_Click(object sender, RoutedEventArgs evt) {
|
||||
var d = new OpenFileDialog() {
|
||||
Title = "Dokument auswählen - Elwig",
|
||||
DefaultExt = ".pdf",
|
||||
DefaultExt = "pdf",
|
||||
Filter = "PDF-Dokument (*.pdf)|*.pdf",
|
||||
Multiselect = true,
|
||||
};
|
||||
@ -438,7 +438,7 @@ namespace Elwig.Windows {
|
||||
.ToList();
|
||||
} else if (OrderNameInput.IsChecked == true) {
|
||||
recipients = recipients
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToList();
|
||||
@ -447,7 +447,7 @@ namespace Elwig.Windows {
|
||||
recipients = recipients
|
||||
.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.FamilyName)
|
||||
.ThenBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToList();
|
||||
@ -455,7 +455,7 @@ namespace Elwig.Windows {
|
||||
recipients = recipients
|
||||
.OrderBy(m => m.PostalDest.AtPlz?.Plz)
|
||||
.ThenBy(m => m.PostalDest.AtPlz?.Ort.Name)
|
||||
.ThenBy(m => m.FamilyName)
|
||||
.ThenBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToList();
|
||||
@ -561,7 +561,7 @@ namespace Elwig.Windows {
|
||||
double totalNum = printNum + emailNum;
|
||||
|
||||
var email = memberDocs
|
||||
.Where(d => d.Docs.Count > 0 && d.Member.EmailAddresses.Count > 0 && (emailMode == 2 || (emailMode == 1 && d.Member.ContactViaEmail)))
|
||||
.Where(d => d.Member.EmailAddresses.Count > 0 && (emailMode == 2 || (emailMode == 1 && d.Member.ContactViaEmail)))
|
||||
.ToDictionary(d => d.Member, m => {
|
||||
var docs = m.Docs.Select(d => d.Doc).ToList();
|
||||
foreach (var doc in docs) {
|
||||
|
@ -64,7 +64,7 @@
|
||||
Margin="205,210,0,0"/>
|
||||
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
|
||||
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"/>
|
||||
|
||||
<Button x:Name="DownloadButton" Click="DownloadButton_Click"
|
||||
|
@ -13,6 +13,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@ -37,13 +38,21 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs evt) {
|
||||
if (App.NumWindows > 1 && !App.ForceShutdown && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
|
||||
var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
|
||||
"Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (res != MessageBoxResult.Yes) {
|
||||
evt.Cancel = true;
|
||||
} else {
|
||||
Application.Current.Shutdown();
|
||||
if (App.NumWindows > 1 && !App.ForceShutdown) {
|
||||
foreach (var w in App.Current.Windows.Cast<Window>().Where(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
|
||||
try {
|
||||
w.Close();
|
||||
} catch { }
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
if (App.NumWindows > 1 && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
|
||||
var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
|
||||
"Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (res != MessageBoxResult.Yes) {
|
||||
evt.Cancel = true;
|
||||
} else {
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,7 +108,7 @@ namespace Elwig.Windows {
|
||||
try {
|
||||
var d = new OpenFileDialog() {
|
||||
Title = "Export-Datei auswählen - Elwig",
|
||||
DefaultExt = ".elwig.zip",
|
||||
DefaultExt = "elwig.zip",
|
||||
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
||||
Multiselect = true,
|
||||
};
|
||||
@ -147,6 +156,8 @@ namespace Elwig.Windows {
|
||||
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
|
||||
} catch (HttpRequestException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (TaskCanceledException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -180,6 +191,8 @@ namespace Elwig.Windows {
|
||||
}
|
||||
} catch (HttpRequestException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (TaskCanceledException exc) {
|
||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -200,6 +213,10 @@ namespace Elwig.Windows {
|
||||
w.Show();
|
||||
}
|
||||
|
||||
private void DeliveryAncmtButton_Click(object sender, RoutedEventArgs evt) {
|
||||
App.FocusDeliveryAncmt();
|
||||
}
|
||||
|
||||
private void BaseDataButton_Click(object sender, RoutedEventArgs evt) {
|
||||
App.FocusBaseData();
|
||||
}
|
||||
|
@ -125,6 +125,11 @@
|
||||
Click="Menu_List_Order_Click"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Export">
|
||||
<MenuItem x:Name="Menu_Export_ExportSelected" Header="...von ausgewähltem Mitglied speichern..." IsEnabled="False"
|
||||
Click="Menu_Export_ExportSelected_Click"/>
|
||||
<MenuItem x:Name="Menu_Export_UploadSelected" Header="...von ausgewähltem Mitglied hochladen" IsEnabled="False"
|
||||
Click="Menu_Export_UploadSelected_Click"/>
|
||||
<Separator/>
|
||||
<MenuItem x:Name="Menu_Export_ExportFilters" Header="...aus Filtern speichern..."
|
||||
Click="Menu_Export_ExportFilters_Click"/>
|
||||
<MenuItem x:Name="Menu_Export_UploadFilters" Header="...aus Filtern hochladen"
|
||||
@ -194,7 +199,7 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</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="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
@ -203,7 +208,7 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Rechnungsadresse" Binding="{Binding BillingAddress.Name}" Width="200"/>
|
||||
<DataGridTextColumn Header="Rechnungsadresse" Binding="{Binding BillingAddress.FullName}" Width="200"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
@ -280,6 +285,9 @@
|
||||
<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"
|
||||
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"/>
|
||||
<TextBox x:Name="PredecessorMgNrInput" Text="{Binding PredecessorMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
@ -289,27 +297,33 @@
|
||||
Margin="53,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" IsEnabled="{Binding EnableMemberReferenceButton}" ToolTip="Zu Vorgänger springen"
|
||||
Click="MemberReferenceButton_Click"/>
|
||||
|
||||
<Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/>
|
||||
<TextBox x:Name="PrefixInput" Text="{Binding Prefix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
<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}" Visibility="{Binding PersonalNameVisibility}"
|
||||
Margin="0,40,10,0" Grid.Column="3"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="GivenNameInput" Text="{Binding GivenName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
<Label Content="Name:" Margin="10,40,0,0" Grid.Column="0" Visibility="{Binding JuridicalNameVisibility}"/>
|
||||
<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"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="FamilyNameInput" Text="{Binding FamilyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
<Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0" Visibility="{Binding PersonalNameVisibility}"/>
|
||||
<TextBox x:Name="NameInput" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="0,70,0,0" Grid.Column="1"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2"/>
|
||||
<TextBox x:Name="SuffixInput" Text="{Binding Suffix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
<Label Content="Zu Handen:" Margin="10,70,0,0" Grid.Column="0" Visibility="{Binding JuridicalNameVisibility}"/>
|
||||
<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"
|
||||
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}"
|
||||
Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="PartialDateInput_TextChanged" LostFocus="PartialDateInput_LostFocus"/>
|
||||
@ -632,7 +646,7 @@
|
||||
</GroupBox>
|
||||
</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>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
|
@ -41,7 +41,7 @@ namespace Elwig.Windows {
|
||||
SearchInput, ActiveMemberInput, MemberList,
|
||||
];
|
||||
RequiredInputs = [
|
||||
MgNrInput, GivenNameInput, FamilyNameInput,
|
||||
MgNrInput, GivenNameInput, NameInput,
|
||||
AddressInput, PlzInput, OrtInput, BillingOrtInput,
|
||||
BusinessSharesInput, BranchInput, DefaultKgInput
|
||||
];
|
||||
@ -130,8 +130,9 @@ namespace Elwig.Windows {
|
||||
var dict = members.AsParallel()
|
||||
.ToDictionary(m => m, m => m.SearchScore(filter))
|
||||
.OrderByDescending(a => a.Value)
|
||||
.ThenBy(a => a.Key.FamilyName)
|
||||
.ThenBy(a => a.Key.GivenName);
|
||||
.ThenBy(a => a.Key.Name)
|
||||
.ThenBy(a => a.Key.GivenName)
|
||||
.ThenBy(a => a.Key.MgNr);
|
||||
var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
|
||||
members = dict
|
||||
.Where(a => a.Value > threshold)
|
||||
@ -139,8 +140,9 @@ namespace Elwig.Windows {
|
||||
.ToList();
|
||||
} else {
|
||||
members = members
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@ -167,7 +169,7 @@ namespace Elwig.Windows {
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private async void InitInputs() {
|
||||
private async Task InitInputs() {
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
await ViewModel.InitInputs();
|
||||
@ -282,6 +284,13 @@ namespace Elwig.Windows {
|
||||
|
||||
private async void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) {
|
||||
await RefreshInputs();
|
||||
if (MemberList.SelectedItem is Member m) {
|
||||
Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
|
||||
Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
|
||||
} else {
|
||||
Menu_Export_ExportSelected.IsEnabled = false;
|
||||
Menu_Export_UploadSelected.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ActiveMemberInput_Changed(object sender, RoutedEventArgs evt) {
|
||||
@ -294,7 +303,7 @@ namespace Elwig.Windows {
|
||||
NewMemberButton_Click(null, null);
|
||||
}
|
||||
|
||||
private void NewMemberButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
private async void NewMemberButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||
IsCreating = true;
|
||||
MemberList.IsEnabled = false;
|
||||
ViewModel.SelectedMember = null;
|
||||
@ -304,7 +313,7 @@ namespace Elwig.Windows {
|
||||
ShowSaveResetCancelButtons();
|
||||
UnlockInputs();
|
||||
UpdateContactInfoVisibility(true);
|
||||
InitInputs();
|
||||
await InitInputs();
|
||||
ViewModel.EnableSearchInputs = false;
|
||||
}
|
||||
|
||||
@ -418,7 +427,7 @@ namespace Elwig.Windows {
|
||||
await RefreshInputs();
|
||||
} else if (IsCreating) {
|
||||
ClearInputs();
|
||||
InitInputs();
|
||||
await InitInputs();
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
@ -551,32 +560,52 @@ namespace Elwig.Windows {
|
||||
await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email);
|
||||
}
|
||||
|
||||
private async void Menu_List_SaveActive_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(0, ExportMode.SaveList); }
|
||||
private async void Menu_List_ShowActive_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(0, ExportMode.Show); }
|
||||
private async void Menu_List_SavePdfActive_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(0, ExportMode.SavePdf); }
|
||||
private async void Menu_List_PrintActive_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(0, ExportMode.Print); }
|
||||
private async void Menu_List_SaveFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(1, ExportMode.SaveList); }
|
||||
private async void Menu_List_ShowFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(1, ExportMode.Show); }
|
||||
private async void Menu_List_SavePdfFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(1, ExportMode.SavePdf); }
|
||||
private async void Menu_List_PrintFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(1, ExportMode.Print); }
|
||||
private async void Menu_List_SaveAll_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(2, ExportMode.SaveList); }
|
||||
private async void Menu_List_ShowAll_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(2, ExportMode.Show); }
|
||||
private async void Menu_List_SavePdfAll_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(2, ExportMode.SavePdf); }
|
||||
private async void Menu_List_PrintAll_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(2, ExportMode.Print); }
|
||||
private async void Menu_List_SaveActive_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.SaveList);
|
||||
private async void Menu_List_ShowActive_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.Show);
|
||||
private async void Menu_List_SavePdfActive_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.SavePdf);
|
||||
private async void Menu_List_PrintActive_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.AllActive, ExportMode.Print);
|
||||
private async void Menu_List_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.SaveList);
|
||||
private async void Menu_List_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Show);
|
||||
private async void Menu_List_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.SavePdf);
|
||||
private async void Menu_List_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Print);
|
||||
private async void Menu_List_SaveAll_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.SaveList);
|
||||
private async void Menu_List_ShowAll_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Show);
|
||||
private async void Menu_List_SavePdfAll_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.SavePdf);
|
||||
private async void Menu_List_PrintAll_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Print);
|
||||
|
||||
private async void Menu_Export_ExportAll_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(2, ExportMode.Export); }
|
||||
private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) { await ViewModel.GenerateMemberList(1, ExportMode.Export); }
|
||||
private async void Menu_Export_ExportAll_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Export);
|
||||
private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Export);
|
||||
private async void Menu_Export_ExportSelected_Click(object sender, RoutedEventArgs evt) =>
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Export);
|
||||
|
||||
private async void Menu_Export_UploadAll_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateMemberList(2, ExportMode.Upload);
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Upload);
|
||||
}
|
||||
|
||||
private async void Menu_Export_UploadFilters_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateMemberList(1, ExportMode.Upload);
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Upload);
|
||||
}
|
||||
|
||||
private async void Menu_Export_UploadSelected_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null) return;
|
||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Upload);
|
||||
}
|
||||
|
||||
private async void Menu_List_Order_Click(object sender, RoutedEventArgs evt) {
|
||||
Menu_List.IsSubmenuOpen = true;
|
||||
@ -699,7 +728,7 @@ namespace Elwig.Windows {
|
||||
private async void PredecessorMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
var valid = InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr);
|
||||
if (valid && ViewModel.PredecessorMgNr is int mgnr && (IsEditing || IsCreating)) {
|
||||
if (MemberList.SelectedItem is not Member m || m.MgNr == mgnr)
|
||||
if (ViewModel.MgNr == mgnr)
|
||||
return;
|
||||
using var ctx = new AppDbContext();
|
||||
var areaComs = await ctx.AreaCommitments
|
||||
@ -709,11 +738,16 @@ namespace Elwig.Windows {
|
||||
return;
|
||||
|
||||
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)
|
||||
return;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -748,7 +782,7 @@ namespace Elwig.Windows {
|
||||
if (ViewModel.SelectedMember is not Member m) return;
|
||||
var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
|
||||
//$"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}";
|
||||
Process.Start(new ProcessStartInfo(url) {
|
||||
UseShellExecute = true,
|
||||
@ -777,5 +811,26 @@ namespace Elwig.Windows {
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
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>
|
||||
<ctrl:UnitConverter x:Key="CurrencyConverter" Precision="2" Unit="€"/>
|
||||
<ctrl:UnitConverter x:Key="WeightConverter" Precision="0" Unit="kg"/>
|
||||
@ -74,7 +74,7 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</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="GA" Binding="{Binding BusinessShares, StringFormat='{}{0:N0} '}" Width="35">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
@ -83,6 +83,20 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</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.CellStyle>
|
||||
<Style>
|
||||
@ -118,7 +132,7 @@
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</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>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
@ -139,7 +153,7 @@
|
||||
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||
|
||||
<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">
|
||||
<Grid>
|
||||
<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"
|
||||
Click="MemberReferenceButton_Click"/>
|
||||
|
||||
<Label Content="Betrag:" Margin="10,40,10,10"/>
|
||||
<ctrl:UnitTextBox x:Name="CustomAmountInput" Width="80" Margin="70,40,10,10" Unit="€"
|
||||
<Label Content="Vor MwSt." Margin="70,37.5,10,10"/>
|
||||
<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"/>
|
||||
|
||||
<Label Content="Freitext:" Margin="10,70,10,10"/>
|
||||
<TextBox x:Name="CustomCommentInput" Margin="70,70,10,10"/>
|
||||
<Label Content="Freitext:" Margin="10,170,10,10"/>
|
||||
<TextBox x:Name="CustomCommentInput" Margin="70,170,10,10"/>
|
||||
|
||||
<Button x:Name="SaveCustomButton" Content="Speichern" Margin="0,0,125,10" Width="120"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||
|
@ -45,12 +45,12 @@ namespace Elwig.Windows {
|
||||
var members = await ctx.Members
|
||||
.Select(m => new {
|
||||
m.MgNr,
|
||||
m.FamilyName,
|
||||
m.Name,
|
||||
m.GivenName,
|
||||
m.BusinessShares,
|
||||
m.IsActive,
|
||||
})
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToListAsync();
|
||||
@ -70,7 +70,7 @@ namespace Elwig.Windows {
|
||||
|
||||
var list = members
|
||||
.Select(m => new {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
m.IsActive,
|
||||
BusinessShares = m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0),
|
||||
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,
|
||||
})
|
||||
.Select(m => new {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
m.BusinessShares,
|
||||
Weight = weight.GetValueOrDefault(m.MgNr, 0),
|
||||
OverUnder = weight.TryGetValue(m.MgNr, out int v1) ?
|
||||
@ -89,7 +89,7 @@ namespace Elwig.Windows {
|
||||
AdjustAmount = m.Adjust * -season.BusinessShareValue,
|
||||
})
|
||||
.Select(m => new {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
m.BusinessShares, m.Weight, m.OverUnder,
|
||||
PenaltyBs = m.OverUnder != null && m.OverUnder < 0 ?
|
||||
(season.PenaltyPerKg * m.OverUnder ?? 0) +
|
||||
@ -103,34 +103,36 @@ namespace Elwig.Windows {
|
||||
}).Sum() : (decimal?)null,
|
||||
m.Adjust,
|
||||
m.AdjustAmount,
|
||||
Custom = CustomPayments!.GetValueOrDefault(m.MgNr, null)?.Amount,
|
||||
Custom = CustomPayments!.GetValueOrDefault(m.MgNr, null),
|
||||
})
|
||||
.Select(m => new {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
m.BusinessShares, m.Weight, m.OverUnder,
|
||||
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),
|
||||
m.Adjust,
|
||||
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 {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
m.BusinessShares, m.Weight, m.OverUnder,
|
||||
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.Custom,
|
||||
Total = (m.PenaltyBs ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.Custom ?? 0),
|
||||
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.CustomAmount ?? 0),
|
||||
})
|
||||
.Select(m => new {
|
||||
m.MgNr, m.FamilyName, m.GivenName,
|
||||
m.MgNr, m.Name, m.GivenName,
|
||||
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,
|
||||
Background = m.Weight == 0 ? Brushes.Orange : m.Weight / 2 < -m.Total ? Brushes.Red : Brushes.White,
|
||||
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)
|
||||
.ThenBy(m => m.FamilyName)
|
||||
.ThenBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.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}";
|
||||
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}";
|
||||
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}";
|
||||
NonDeliveries.Text = $"{list.Count(r => r.Weight == 0):N0}";
|
||||
|
||||
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
|
||||
.OrderBy(m => m.FamilyName)
|
||||
.OrderBy(m => m.Name)
|
||||
.ThenBy(m => m.GivenName)
|
||||
.ThenBy(m => m.MgNr)
|
||||
.ToListAsync());
|
||||
@ -239,13 +241,22 @@ namespace Elwig.Windows {
|
||||
MemberList.ScrollIntoView(MemberList.SelectedItem);
|
||||
|
||||
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}";
|
||||
CustomCommentInput.Text = p.Comment ?? "";
|
||||
} else {
|
||||
CustomModAbsInput.Text = "";
|
||||
CustomModRelInput.Text = "";
|
||||
CustomModCommentInput.Text = "";
|
||||
CustomAmountInput.Text = "";
|
||||
CustomCommentInput.Text = "";
|
||||
}
|
||||
} else {
|
||||
CustomModAbsInput.Text = "";
|
||||
CustomModRelInput.Text = "";
|
||||
CustomModCommentInput.Text = "";
|
||||
CustomAmountInput.Text = "";
|
||||
CustomCommentInput.Text = "";
|
||||
}
|
||||
@ -256,6 +267,14 @@ namespace Elwig.Windows {
|
||||
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) {
|
||||
Validator.CheckDecimal((TextBox)sender, false, 4, 2, true);
|
||||
}
|
||||
@ -268,12 +287,19 @@ namespace Elwig.Windows {
|
||||
if (CustomPayments?.TryGetValue(m.MgNr, out var p) == true) {
|
||||
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();
|
||||
ctx.Add(new PaymentCustom {
|
||||
MgNr = m.MgNr,
|
||||
Year = Year,
|
||||
Amount = num,
|
||||
ModAbs = modAbs,
|
||||
ModRel = modRel,
|
||||
ModComment = modText == "" ? null : modText,
|
||||
Amount = amount,
|
||||
Comment = text == "" ? null : text,
|
||||
});
|
||||
}
|
||||
|
@ -128,6 +128,12 @@
|
||||
HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
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"/>
|
||||
<CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen"
|
||||
Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
|
@ -44,6 +44,11 @@ namespace Elwig.Windows {
|
||||
if (!App.Config.Debug) {
|
||||
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) {
|
||||
|
30
Tests/DocumentTests/DeliveryAncmtListTest.cs
Normal file
30
Tests/DocumentTests/DeliveryAncmtListTest.cs
Normal 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"],
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace Tests.DocumentTests {
|
||||
[Test]
|
||||
public async Task Test_01_TwoMembers() {
|
||||
using var ctx = new AppDbContext();
|
||||
var data = await MemberListData.FromQuery(ctx.Members, []);
|
||||
var data = await MemberListData.FromQuery(ctx.Members, [], []);
|
||||
using var doc = new MemberList("Alle Mitglieder", data);
|
||||
var text = await Utils.GeneratePdfText(doc, true);
|
||||
var table = Utils.ExtractTable(text);
|
||||
|
@ -21,7 +21,7 @@ namespace Tests.DocumentTests {
|
||||
public static string[][] ExtractTable(string text) {
|
||||
return text.Split('\n')
|
||||
.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)
|
||||
.ToArray();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace Tests.E2ETests {
|
||||
Window.FindElement(By.WpfId("MgNrInput")).SendKeys("9999");
|
||||
|
||||
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("SuffixInput")).SendKeys("jun.");
|
||||
Window.FindElement(By.WpfId("BirthdayInput")).SendKeys("1987");
|
||||
|
@ -1,5 +1,6 @@
|
||||
-- deletes for DocumentTests
|
||||
|
||||
DELETE FROM delivery_schedule;
|
||||
DELETE FROM payment_variant;
|
||||
DELETE FROM delivery;
|
||||
DELETE FROM season;
|
||||
|
@ -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
|
||||
(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
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);
|
||||
|
@ -66,7 +66,7 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
|
||||
(15216, 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 ),
|
||||
(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 ),
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user