Compare commits

...

35 Commits

Author SHA1 Message Date
12d2aeecaf Bump version to 0.9.0
All checks were successful
Test / Run tests (push) Successful in 2m50s
Deploy / Build and Deploy (push) Successful in 2m40s
2024-07-28 22:13:17 +02:00
1bc0d67d26 [#3] Services: Add 'Check internet connection' message
All checks were successful
Test / Run tests (push) Successful in 2m46s
2024-07-28 09:35:01 +02:00
38315cd928 [#3] Services: Update 'upload successful' message
All checks were successful
Test / Run tests (push) Successful in 2m24s
2024-07-28 00:27:21 +02:00
53d3affefe DeliveryConfirmation: Use checkboxes for 'gerebelt'
All checks were successful
Test / Run tests (push) Successful in 2m2s
2024-07-27 21:55:20 +02:00
4c3f0c40fa DeliveryAdminWindow: Handle members without default KG correctly
All checks were successful
Test / Run tests (push) Successful in 2m44s
2024-07-27 20:46:13 +02:00
662862090e [#3] ElwigData: Fix import error when no deliveries are present
All checks were successful
Test / Run tests (push) Successful in 2m31s
2024-07-27 19:44:22 +02:00
a5164e286f [#3] Services: Use .elwig.zip as export extension 2024-07-27 19:44:22 +02:00
ae1e985656 [#3] MainWindow: Add functionality to import data from files 2024-07-27 19:44:22 +02:00
36c1bd35a7 [#3] MemberAdminWindow: Add Export item in menu 2024-07-27 19:44:22 +02:00
4aa3362029 DeliveryConfirmation: Use 'gerebelt' instead of 'bto./nto.' 2024-07-27 19:44:15 +02:00
cc97004b30 [#3] ElwigData: Fix importing of duplicate data
All checks were successful
Test / Run tests (push) Successful in 2m52s
2024-07-27 16:57:23 +02:00
935b31f6e3 DeliveryAdminWindow: Force user to input gerebelt gewogen input
All checks were successful
Test / Run tests (push) Successful in 2m2s
2024-07-26 19:47:47 +02:00
f09753ccc2 Remove byte order marks
All checks were successful
Test / Run tests (push) Successful in 2m4s
2024-07-26 19:44:41 +02:00
53c7cb2ec0 Workflows: Check for byte order mark 2024-07-26 19:44:37 +02:00
80c3ec1b9c Windows: Use Indeterminate event in ThreeState CheckBoxes
All checks were successful
Test / Run tests (push) Successful in 2m19s
2024-07-26 16:15:51 +02:00
b49c9c65b1 [#3] Elwig: Add user-friendly sync method
All checks were successful
Test / Run tests (push) Successful in 2m57s
2024-07-26 14:58:15 +02:00
b6afb94246 MemberListData: Include contact information like phone numbers and email addresses
All checks were successful
Test / Run tests (push) Successful in 2m25s
2024-07-24 19:28:34 +02:00
8e9f2f4e90 WeighingTests: Add Haugsdorf and Sitzendorf
All checks were successful
Test / Run tests (push) Successful in 2m46s
2024-07-24 16:42:38 +02:00
d741ba92dc Printing/Pdf: Set CreateNoWindow to true
All checks were successful
Test / Run tests (push) Successful in 2m6s
2024-07-23 20:22:59 +02:00
84f772a32f MailWindow: Fix crash when no season is present 2024-07-23 20:22:59 +02:00
fd0ed97305 Weighing: Do not ignore gross and tare weight and show it on DeliveryNote 2024-07-23 20:22:59 +02:00
1141331608 MemberAdminWindow: Add filters for E-Mail, Tel.-Nr., and contact options 2024-07-23 20:22:59 +02:00
f235d5b380 DeliveryAdminWindow: Fix ModifierInput_SelectionChanged event 2024-07-23 20:22:59 +02:00
30116f7848 DeliveryAdminWindow: Improve status bar widths 2024-07-23 20:22:59 +02:00
abe7699a5b [#26] MemberAdminWindow: Rearrange status bar items 2024-07-23 20:22:59 +02:00
bb77a4e79a [#26] AreaComAdminWindow: Overhaul status bar and search filters 2024-07-23 20:22:59 +02:00
46ea0f29ff [#26] MemberAdminWindow: Add tooltip to area commitment status bar 2024-07-23 20:22:59 +02:00
4229fbbef6 [#26] AreaComService: Add GenerateToolTip() 2024-07-23 20:22:59 +02:00
8dde1cb3f4 [#10] ViewModels: Unify FilterMember property 2024-07-23 20:22:59 +02:00
c314321039 Utils: Change CurrentLastSeason to switch in july 2024-07-23 20:22:59 +02:00
cf1e975d8e [#10] MemberAdminWindow: Add tooltip for delivieries 2024-07-23 20:22:59 +02:00
60359935a4 [#10] DeliveryAdminWindow: Move generation of tooltips to DeliveryService 2024-07-23 20:22:59 +02:00
49e988f71a [#10] AreaComAdminWindow: Implement MVVM 2024-07-23 20:22:59 +02:00
2a8de18772 [#10] DeliveryAdminWindow: Implement MVVM 2024-07-23 20:22:59 +02:00
af80e827b7 [#10] MemberAdminWindow: Implement MVVM 2024-07-23 20:22:59 +02:00
148 changed files with 4195 additions and 2445 deletions

View File

@ -2,7 +2,7 @@ name: Test
on:
push:
branches: ["**"]
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe"]
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe", ".gitea/workflows/test.yaml"]
jobs:
test:
name: Run tests
@ -10,6 +10,19 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for Byte order marks
shell: powershell
run: |
$pattern = [char]::ConvertFromUtf32(0xFEFF)
$files = git grep -IEl "^$pattern"
if ( $lastexitcode -ne 1 ) {
echo "Files with BOM found:"
echo $files
exit 1
} else {
echo "No files with BOM found"
exit 0
}
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.1
- name: Setup NuGet

View File

@ -3,10 +3,44 @@ Changelog
=========
[v0.9.0][v0.9.0] (2024-07-28) {#v0.9.0}
---------------------------------------
### Neue Funktionen {#v0.9.0-features}
* Flächenbindungen werden als Tabelle in der Status-Leiste im Mitglieder-Fenster (`MemberAdminWindow`) angezeigt. ([#26][i26])
* Filter für E-Mail-Adressen, Tel.-Nr. und Kontaktarten im Mitglieder-Fenster (`MemberAdminWindow`). (1141331608)
* Auf Lieferscheinen (`DeliveryNote`) werden nun _Brutto, Tara, Netto_ werte abgedruckt. (fd0ed97305, 4aa3362029, 53d3affefe)
* Beim Exportieren der Mitgliederliste werden auch Tel.-Nr. und E-Mail-Adressen exportiert. (b6afb94246)
* Benutzerfreundliches Synchronisieren zwischen Standorten. ([#3][i3])
* Übernehmer _müssen_ explizit entscheiden, ob _gerebelt gewogen_ oder nicht. (935b31f6e3)
### Behobene Fehler {#v0.9.0-bugfixes}
* Zu-/Abschläge im Lieferungen-Fenster (`DeliveryAdminWindow`). (f235d5b380)
* Falls keine Saison existierte führte das zu einem Absturz im Rundschreiben-Fenster (`MailWindow`). (84f772a32f)
* Beim Drucken _sollte_ kein Kommandozeilen-Fenster mehr sichtbar sein. (d741ba92dc)
* Wenn bei einem Mitglied keine Stamm-KG hinterlegt war, kam es zu inkonsistentem Verhalten im Übernahme-Fenster (`DeliveryAdminWinodw`). (4c3f0c40fa)
### Sonstiges {#v0.9.0-misc}
* Model-View-ViewModel (MVVM) implementiert! ([#10][i10])
* Die nächste Saison "beginnt" bereits im Juli (statt erst im August) (`CurrentLastSeason`). (c314321039)
* Automatisierte Tests für Waagen (Sitzendorf, Haugsdorf). (8e9f2f4e90)
* Überprüfung auf Byte-Order-Marks in allen Quellcode-Dateien. (53c7cb2ec0, f09753ccc2)
[v0.9.0]: https://git.necronda.net/winzer/elwig/releases/tag/v0.9.0
[i3]: https://git.necronda.net/winzer/elwig/issues/3
[i10]: https://git.necronda.net/winzer/elwig/issues/10
[i26]: https://git.necronda.net/winzer/elwig/issues/26
[v0.8.9][v0.8.9] (2024-07-23) {#v0.8.9}
---------------------------------------
### Behobene Fehler {#v0.8.8-bugfixes}
### Behobene Fehler {#v0.8.9-bugfixes}
* Absturz im Rundschreiben-Fenster (`MailWindow`). Fehler bei `CheckComboBox`. (49f03c0a3c)
* Excel-Exporte können nun auch Sonderzeichen (wie `&`) enthalten. (ba9e1d7201)

View File

@ -29,10 +29,11 @@ namespace Elwig {
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string ExePath = @"C:\Program Files\Elwig\";
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static Config Config { get; private set; } = new(Path.Combine(DataPath, "config.ini"));
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; }
@ -264,15 +265,15 @@ namespace Elwig {
}
public static DeliveryAdminWindow FocusReceipt() {
return FocusWindow<DeliveryAdminWindow>(() => new(true), w => w.IsReceipt);
return FocusWindow<DeliveryAdminWindow>(() => new(true), w => w.ViewModel.IsReceipt);
}
public static DeliveryAdminWindow FocusMemberDeliveries(int mgnr) {
return FocusWindow<DeliveryAdminWindow>(() => new(mgnr), w => w.MgNr == mgnr);
return FocusWindow<DeliveryAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
public static AreaComAdminWindow FocusMemberAreaComs(int mgnr) {
return FocusWindow<AreaComAdminWindow>(() => new(mgnr), w => w.MgNr == mgnr);
return FocusWindow<AreaComAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember.MgNr == mgnr);
}
public static BaseDataWindow FocusBaseData() {

View File

@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

View File

@ -1,4 +1,4 @@
using System.Windows;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.Controls {

View File

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:Elwig.Controls">
<Style TargetType="ctrl:UnitTextBox" BasedOn="{StaticResource {x:Type TextBox}}">

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Windows;
using System.Windows.Data;
using System.Globalization;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Windows;
using System.Windows.Data;
using System.Globalization;

View File

@ -1,4 +1,4 @@
using System.Windows.Controls;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Controls {

View File

@ -1,4 +1,4 @@
using System.Windows.Controls;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Controls {

View File

@ -1,4 +1,4 @@
using System.Windows.Controls;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Controls {

View File

@ -1,4 +1,4 @@
<Window x:Class="Elwig.Dialogs.AreaComDialog"
<Window x:Class="Elwig.Dialogs.AreaComDialog"
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"

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using System.Windows;
using System.Windows.Controls;

View File

@ -1,4 +1,4 @@
<Window x:Class="Elwig.Dialogs.DeleteMemberDialog"
<Window x:Class="Elwig.Dialogs.DeleteMemberDialog"
AutomationProperties.AutomationId="DeleteMemberDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

View File

@ -1,4 +1,4 @@
<Window x:Class="Elwig.Dialogs.NewSeasonDialog"
<Window x:Class="Elwig.Dialogs.NewSeasonDialog"
AutomationProperties.AutomationId="NewSeasonDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Windows;

View File

@ -1,4 +1,4 @@
<Window x:Class="Elwig.Dialogs.UpdateDialog"
<Window x:Class="Elwig.Dialogs.UpdateDialog"
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"

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using System;
using System.Diagnostics;
using System.IO;

View File

@ -33,9 +33,9 @@
<th>Gewicht</th>
<th rowspan="3" style="padding: 0;">
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
<text x="-40" y="5" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"
<text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"
style="text-anchor: start; alignment-baseline: middle;">
bto./nto.
gerebelt
</text>
</svg>
</th>
@ -81,7 +81,7 @@
}
@if (i == p.Buckets.Length - 1) {
<td class="number">@($"{p.Weight:N0}")</td>
<td class="small">@(p.IsNetWeight ? "n" : "b")</td>
<td style="font-size: 7pt;">@(p.IsNetWeight ? "\u2611" : "\u2610")</td>
} else {
<td></td>
<td></td>

View File

@ -60,8 +60,18 @@
}
}
<tr><td></td><td colspan="5">
@Raw(part.IsManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}")
(@(part.IsNetWeight ? "netto/gerebelt gewogen" : "brutto/nicht gerebelt gewogen"))@Raw(part.WeighingReason != null ? $", <i>Begründung:</i>" : "") @part.WeighingReason
@if (part.IsManualWeighing) {
<i>Handwiegung @(part.IsNetWeight ? " (gerebelt gewogen)" : " (nicht gerebelt gewogen)")</i>@Raw(part.WeighingReason != null ? ", <i>Begründung:</i> " : "") @part.WeighingReason
} else {
var info = part.WeighingInfo;
<i>Waage:</i> @(part.ScaleId ?? "?")@(", ") <i>ID:</i> @(info.Id ?? "?")
@(info.Date != null || info.Time != null ? " " : "")@(info.Time != null ? $"{info.Time:HH:mm}" : "")@(info.Date != null ? $", {info.Date:dd.MM.yyyy}" : "")
@if (info.Gross != null && info.Tare != null && info.Net != null) {
<br/><i>Brutto:</i> @($"{info.Gross:N0} kg")@(" ") <i>Tara:</i> @($"{info.Tare:N0} kg")@(" ") <i>Netto:</i> @($"{info.Net:N0} kg")@(" ")@Raw(part.IsNetWeight ? "<i>gerebelt gewogen</i>" : "<i>nicht gerebelt gewogen</i>")
} else {
@Raw($" <i>({(part.IsNetWeight ? "gerebelt gewogen" : "nicht gerebelt gewogen")})</i>")
}
}
</td></tr>
@if (part.Comment != null) {
<tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr>

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
@using RazorLight
@using RazorLight
@using Elwig.Helpers
@inherits TemplatePage<Elwig.Documents.MemberDataSheet>
@model Elwig.Documents.MemberDataSheet

View File

@ -1,4 +1,4 @@

h2 {
margin-bottom: 0.5em !important;
}

View File

@ -1,4 +1,4 @@
using Elwig.Models.Dtos;
using Elwig.Models.Dtos;
using System.Collections.Generic;
namespace Elwig.Documents {

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Dtos;
using Elwig.Models.Dtos;
using System.Collections.Generic;
namespace Elwig.Documents {

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.8.9</Version>
<Version>0.9.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>
@ -25,6 +25,7 @@
</Target>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="LinqKit" Version="1.3.0" />
<PackageReference Include="MailKit" Version="4.7.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.32" />

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Windows.Input;
namespace Elwig.Helpers {

View File

@ -9,7 +9,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 24;
public static readonly int RequiredSchemaVersion = 25;
private static int VersionOffset = 0;

View File

@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
using NJsonSchema;
using System;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;

View File

@ -11,11 +11,7 @@ using Brushes = System.Windows.Media.Brushes;
namespace Elwig.Helpers {
public class ControlUtils {
public enum RenewSourceDefault {
None,
IfOnly,
First
}
public enum RenewSourceDefault { None, IfOnly, First }
private static void SetControlBorderBrush(Control input, Brush brush) {
if (input is ComboBox cb) {
@ -171,15 +167,19 @@ namespace Elwig.Helpers {
return item;
}
public static object? GetItemFromSource(IEnumerable source, object? item) {
return GetItemFromSource(source, Utils.GetEntityIdentifier(item));
public static T? GetItemFromSource<T>(IEnumerable source, T? item) {
return (T?)GetItemFromSource(source, Utils.GetEntityIdentifier(item));
}
public static object? GetItemFromSourceWithPk(IEnumerable source, params object?[] primaryKey) {
return GetItemFromSource(source, (int?)Utils.GetEntityIdetifierForPk(primaryKey));
}
public static void SelectItemWithHash(Selector input, int? hash) {
if (hash == null) {
input.SelectedItem = null;
} else {
input.SelectedItem = GetItemFromSource(input.ItemsSource, (int)hash);
input.SelectedItem = GetItemFromSource(input.ItemsSource, hash);
}
if (input is ListBox lb && lb.SelectedItem is object lbItem) {
lb.ScrollIntoView(lbItem);

View File

@ -1,4 +1,4 @@
using System.IO.Compression;
using System.IO.Compression;
using System.IO;
using System.Threading.Tasks;
using Elwig.Models.Entities;
@ -8,12 +8,17 @@ using System.Text.Json.Nodes;
using System.Linq;
using System.Windows;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
namespace Elwig.Helpers.Export {
public static class ElwigData {
public enum ImportMode { Auto, Interactively, FromBranches }
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
private static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
public static async Task<string[]> GetImportedFiles() {
try {
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
@ -22,40 +27,73 @@ namespace Elwig.Helpers.Export {
}
}
public static async Task AddImportedFiles(IEnumerable<string> filenames) {
public static async Task AddImportedFiles(params string[] filenames) {
await File.AppendAllLinesAsync(ImportedTxt, filenames, Utils.UTF8);
}
public static Task Import(string filename, bool interactive) => Import([filename], interactive);
public static Task Import(string filename, ImportMode mode) => Import([filename], mode);
public static async Task Import(IEnumerable<string> filenames, bool interactive) {
public static async Task Import(IEnumerable<string> filenames, ImportMode mode) {
try {
using var ctx = new AppDbContext();
var currentDids = await ctx.Deliveries
.GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId));
Dictionary<string, Branch> branches;
Dictionary<int, int> currentDids;
Dictionary<string, int> currentLsNrs;
Dictionary<int, List<WbRd>> currentWbRde;
var deliveries = new List<Delivery>();
var deliveryParts = new List<DeliveryPart>();
var modifiers = new List<DeliveryPartModifier>();
using (var ctx = new AppDbContext()) {
branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
currentDids = await ctx.Deliveries
.GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId));
currentLsNrs = await ctx.Deliveries
.ToDictionaryAsync(d => d.LsNr, d => d.DId);
currentWbRde = await ctx.WbRde
.GroupBy(r => r.KgNr)
.ToDictionaryAsync(g => g.Key, g => g.ToList());
}
var metaData = new List<(string Name, int DeliveryNum, string Filters)>();
var data = new List<(
List<Member> Members,
List<BillingAddr> BillingAddresses,
List<MemberTelNr> TelephoneNumbers,
List<MemberEmailAddr> EmailAddresses,
List<AreaCom> AreaCommitments,
List<WbRd> Riede,
List<Delivery> Deliveries,
List<DeliveryPart> DeliveryParts,
List<DeliveryPartModifier> Modifiers)>();
var metaData = new List<(string FileName, string ZwstId, string Device,
int? MemberNum, string? MemberFilters,
int? AreaComNum, string? AreaComFilters,
int? DeliveryNum, string? DeliveryFilters)>();
foreach (var filename in filenames) {
// TODO read encrypted files
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
var version = zip.GetEntry("version");
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
if (await reader.ReadToEndAsync() != "elwig:1")
throw new FileFormatException("Ungültige Export-Datei");
throw new FileFormatException($"Ungültige Export-Datei ({filename})");
}
var metaJson = zip.GetEntry("meta.json");
var meta = await JsonNode.ParseAsync(metaJson!.Open());
var deliveryCount = meta!["deliveries"]?["count"]!.AsValue().GetValue<int>();
var deliveryFilters = meta!["deliveries"]?["filters"]!.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
if (deliveryCount != null && deliveryFilters != null)
metaData.Add((Path.GetFileName(filename), (int)deliveryCount, string.Join(" / ", deliveryFilters)));
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
metaData.Add((Path.GetFileName(filename),
meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
data.Add(new([], [], [], [], [], [], [], new([], [])));
var r = data[^1];
var membersJson = zip.GetEntry("members.json");
if (membersJson != null) {
@ -63,7 +101,11 @@ namespace Elwig.Helpers.Export {
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject();
// TODO import members.json
var (m, b, telNrs, emailAddrs) = obj.ToMember();
r.Members.Add(m);
if (b != null) data[^1].BillingAddresses.Add(b);
r.TelephoneNumbers.AddRange(telNrs);
r.EmailAddresses.AddRange(emailAddrs);
}
}
@ -73,7 +115,9 @@ namespace Elwig.Helpers.Export {
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject();
// TODO import area_commitments.json
var (areaCom, wbrd) = obj.ToAreaCom(currentWbRde);
r.AreaCommitments.Add(areaCom);
if (wbrd != null) r.Riede.Add(wbrd);
}
}
@ -83,85 +127,409 @@ namespace Elwig.Helpers.Export {
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject();
var (d, parts, mods) = JsonToDelivery(obj, currentDids);
deliveries.Add(d);
deliveryParts.AddRange(parts);
modifiers.AddRange(mods);
var (d, parts, mods) = obj.ToDelivery(currentLsNrs, currentDids);
r.Deliveries.Add(d);
r.DeliveryParts.AddRange(parts);
r.Modifiers.AddRange(mods);
}
}
}
var lsnrs = deliveries.Select(d => d.LsNr).ToList();
var duplicateLsNrs = await ctx.Deliveries
.Where(d => lsnrs.Contains(d.LsNr))
.Select(d => d.LsNr)
.ToListAsync();
var duplicateDIds = deliveries
.Where(d => duplicateLsNrs.Contains(d.LsNr))
.Select(d => (d.Year, d.DId))
.ToList();
bool overwriteDelivieries = false;
if (duplicateLsNrs.Count > 0) {
var res = MessageBox.Show($"Sollen {duplicateLsNrs.Count} Lieferungen überschreiben werden?", "Lieferungen überschreiben",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
overwriteDelivieries = res == MessageBoxResult.Yes;
var importedMembers = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, 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)) {
var branch = branches[meta.ZwstId];
var device = meta.Device;
using var ctx = new AppDbContext();
var mgnrs = members.Select(m => m.MgNr).ToList();
var duplicateMgNrs = await ctx.Members
.Where(m => mgnrs.Contains(m.MgNr))
.Select(m => m.MgNr)
.ToListAsync();
bool importNewMembers = false, importDuplicateMembers = false;
if (mode == ImportMode.Interactively) {
if (mgnrs.Count - duplicateMgNrs.Count > 0)
importNewMembers = ImportQuestion(branch.Name, device, "Mitglieder", false, mgnrs.Count - duplicateMgNrs.Count);
} else {
importNewMembers = true;
}
if (duplicateMgNrs.Count > 0)
importDuplicateMembers = ImportQuestion(branch.Name, device, "Mitglieder", true, duplicateMgNrs.Count);
var lsnrs = deliveries.Select(d => d.LsNr).ToList();
var duplicateLsNrs = await ctx.Deliveries
.Where(d => lsnrs.Contains(d.LsNr))
.Select(d => d.LsNr)
.ToListAsync();
var duplicateDIds = deliveries
.Where(d => duplicateLsNrs.Contains(d.LsNr))
.Select(d => (d.Year, d.DId))
.ToList();
var allowedDuplicateLsNrs = new List<string>();
bool importNewDeliveries = false, importDuplicateDeliveries = false;
if (mode == ImportMode.Interactively) {
if (lsnrs.Count - duplicateLsNrs.Count > 0)
importNewDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", false, lsnrs.Count - duplicateLsNrs.Count);
if (duplicateLsNrs.Count > 0)
importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
} else if (mode == ImportMode.FromBranches) {
importNewDeliveries = true;
if (duplicateLsNrs.Count > 0) {
allowedDuplicateLsNrs = await ctx.Deliveries
.Where(d => lsnrs.Contains(d.LsNr) && d.ZwstId == branch.ZwstId)
.Select(d => d.LsNr)
.ToListAsync();
if (duplicateLsNrs.Count - allowedDuplicateLsNrs.Count > 0)
importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count - allowedDuplicateLsNrs.Count);
}
} else {
importNewDeliveries = true;
if (duplicateLsNrs.Count > 0)
importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
}
if (importDuplicateMembers) {
ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr)));
ctx.RemoveRange(ctx.MemberEmailAddrs.Where(a => duplicateMgNrs.Contains(a.MgNr)));
ctx.UpdateRange(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)));
}
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)));
}
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 (allowedDuplicateLsNrs.Count > 0) {
var dids = deliveries
.Where(d => allowedDuplicateLsNrs.Contains(d.LsNr))
.Select(d => (d.Year, d.DId))
.ToList();
ctx.RemoveRange(ctx.DeliveryParts
.Where(p => allowedDuplicateLsNrs.Contains(p.Delivery.LsNr))
.SelectMany(p => p.PartModifiers));
ctx.RemoveRange(ctx.DeliveryParts.Where(p => allowedDuplicateLsNrs.Contains(p.Delivery.LsNr)));
ctx.UpdateRange(deliveries.Where(d => dids.Contains((d.Year, d.DId))));
ctx.AddRange(deliveryParts.Where(p => dids.Contains((p.Year, p.DId))));
ctx.AddRange(modifiers.Where(m => dids.Contains((m.Year, m.DId))));
}
if (importDuplicateDeliveries) {
var l = duplicateLsNrs.Except(allowedDuplicateLsNrs).ToList();
var dids = deliveries
.Where(d => l.Contains(d.LsNr))
.Select(d => (d.Year, d.DId))
.ToList();
ctx.RemoveRange(ctx.DeliveryParts
.Where(p => l.Contains(p.Delivery.LsNr))
.SelectMany(p => p.PartModifiers));
ctx.RemoveRange(ctx.DeliveryParts.Where(p => l.Contains(p.Delivery.LsNr)));
ctx.UpdateRange(deliveries.Where(d => dids.Contains((d.Year, d.DId))));
ctx.AddRange(deliveryParts.Where(p => dids.Contains((p.Year, p.DId))));
ctx.AddRange(modifiers.Where(m => dids.Contains((m.Year, m.DId))));
}
if (importNewDeliveries) {
ctx.AddRange(deliveries.Where(d => !duplicateDIds.Contains((d.Year, d.DId))));
ctx.AddRange(deliveryParts.Where(p => !duplicateDIds.Contains((p.Year, p.DId))));
ctx.AddRange(modifiers.Where(m => !duplicateDIds.Contains((m.Year, m.DId))));
}
if (deliveries.Count > 0) {
var n = importNewDeliveries ? deliveries.Count - duplicateDIds.Count : 0;
var o = allowedDuplicateLsNrs.Count + (importDuplicateDeliveries ? duplicateDIds.Count - allowedDuplicateLsNrs.Count : 0);
importedDeliveries.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, deliveries.Count - n - o, meta.DeliveryFilters));
}
await ctx.SaveChangesAsync();
await AddImportedFiles(Path.GetFileName(meta.FileName));
}
if (overwriteDelivieries) {
ctx.RemoveRange(ctx.Deliveries.Where(d => duplicateLsNrs.Contains(d.LsNr)));
ctx.AddRange(deliveries);
ctx.AddRange(deliveryParts);
ctx.AddRange(modifiers);
} else {
ctx.AddRange(deliveries.Where(d => !duplicateDIds.Contains((d.Year, d.DId))));
ctx.AddRange(deliveryParts.Where(p => !duplicateDIds.Contains((p.Year, p.DId))));
ctx.AddRange(modifiers.Where(m => !duplicateDIds.Contains((m.Year, m.DId))));
}
await ctx.SaveChangesAsync();
await AddImportedFiles(filenames.Select(f => Path.GetFileName(f)));
await App.HintContextChange();
MessageBox.Show(
$"Das importieren der Daten war erfolgreich!\n" +
$"Folgendes wurde importiert:\n" +
$" Lieferungen: {deliveries.Count}\n" +
string.Join("\n", metaData.Select(d => $" {d.Name} ({d.DeliveryNum})\n {d.Filters}")) +
"\n", "Importieren erfolgreich",
string.Join("\n", [
$"Mitglieder: {importedMembers.Sum(d => d.New + d.Overwritten)}",
..importedMembers.Select(d =>
$" {d.FileName} ({d.New + d.Overwritten})\n" +
$" ({d.New} neu, {d.Overwritten} überschrieben, {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" +
$" ({d.New} neu, {d.Overwritten} überschr., {d.NotImported} nicht importiert)\n" +
$" Zwst.: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" +
$" Filter: {d.Filters}")
]),
"Importieren erfolgreich",
MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
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, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
public static async Task ExportDeliveries(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
File.Delete(filename);
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
private static bool ImportQuestion(string branch, string device, string subject, bool duplicate, int number) {
return MessageBox.Show(
$"Sollen {number} {(duplicate ? "" : "neue ")}{subject} durch die Zweigstelle\n" +
$"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?",
$"{subject} importieren",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes
) == MessageBoxResult.Yes;
}
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
await writer.WriteAsync("elwig:1");
}
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<string> filters) {
return new ElwigExport {
Members = (members, filters)
}.Export(filename);
}
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
await writer.WriteAsync(
$"{{\"timestamp\": \"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}\", " +
$"\"zwstid\": \"{App.ZwstId}\", \"device\": \"{Environment.MachineName}\", " +
$"\"deliveries\": {{" +
$"\"count\": {deliveries.Count()}, " +
$"\"parts\": {deliveries.Sum(d => d.Parts.Count)}, " +
$"\"filters\": [{string.Join(", ", filters.Select(f => '"' + f + '"'))}]" +
$"}}}}");
}
public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
return new ElwigExport {
Deliveries = (deliveries, filters)
}.Export(filename);
}
var json = zip.CreateEntry("deliveries.json");
using (var writer = new StreamWriter(json.Open(), Utils.UTF8)) {
foreach (var d in deliveries) {
await writer.WriteLineAsync(DeliveryToJson(d).ToJsonString());
public class ElwigExport {
public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
public async Task Export(string filename) {
File.Delete(filename);
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
await writer.WriteAsync("elwig:1");
}
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
var obj = new JsonObject {
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName,
};
if (Members != null)
obj["members"] = new JsonObject {
["count"] = Members.Value.Members.Count(),
["filters"] = new JsonArray(Members.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (AreaComs != null)
obj["area_commitments"] = new JsonObject {
["count"] = AreaComs.Value.AreaComs.Count(),
["filters"] = new JsonArray(AreaComs.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (Deliveries != null)
obj["deliveries"] = new JsonObject {
["count"] = Deliveries.Value.Deliveries.Count(),
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
await writer.WriteAsync(obj.ToJsonString(JsonOpts));
}
// TODO encrypt files
if (Members != null) {
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var m in Members.Value.Members) {
await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts));
}
}
if (AreaComs != null) {
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var c in AreaComs.Value.AreaComs) {
await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts));
}
}
if (Deliveries != null) {
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var d in Deliveries.Value.Deliveries) {
await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts));
}
}
}
}
public static JsonObject DeliveryToJson(Delivery d) {
public static JsonObject ToJson(this Member m) {
return new JsonObject {
["mgnr"] = m.MgNr,
["predecessor_mgnr"] = m.PredecessorMgNr,
["prefix"] = m.Prefix,
["given_name"] = m.GivenName,
["middle_names"] = m.MiddleName,
["family_name"] = m.FamilyName,
["suffix"] = m.Suffix,
["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,
["business_shares"] = m.BusinessShares,
["accounting_nr"] = m.AccountingNr,
["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr,
["ustid_nr"] = m.UstIdNr,
["volllieferant"] = m.IsVollLieferant,
["buchführend"] = m.IsBuchführend,
["organic"] = m.IsOrganic,
["funktionär"] = m.IsFunktionär,
["active"] = m.IsActive,
["deceased"] = m.IsDeceased,
["iban"] = m.Iban,
["bic"] = m.Bic,
["default_kgnr"] = m.DefaultKgNr,
["contact_postal"] = m.ContactViaPost,
["contact_email"] = m.ContactViaEmail,
["address"] = new JsonObject {
["address"] = m.Address,
["postal_dest"] = m.PostalDestId,
["country"] = m.CountryNum,
},
["billing_address"] = m.BillingAddress != null ? new JsonObject {
["name"] = m.BillingAddress.Name,
["address"] = m.BillingAddress.Address,
["postal_dest"] = m.BillingAddress.PostalDestId,
["country"] = m.BillingAddress.CountryNum,
} : null,
["telephone_numbers"] = new JsonArray(m.TelephoneNumbers.OrderBy(n => n.Nr).Select(n => {
var obj = new JsonObject {
["number"] = n.Number,
["type"] = n.Type,
};
if (n.Comment != null) obj["comment"] = n.Comment;
return obj;
}).ToArray()),
["email_addresses"] = new JsonArray(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => {
var obj = new JsonObject {
["address"] = a.Address,
};
if (a.Comment != null) obj["comment"] = a.Comment;
return obj;
}).ToArray()),
["comment"] = m.Comment,
};
}
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>) ToMember(this JsonNode json) {
var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
return (new Member {
MgNr = mgnr,
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
Prefix = json["prefix"]?.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>(),
Birthday = json["birthday"]?.AsValue().GetValue<string>(),
EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
BusinessShares = json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
AccountingNr = json["accounting_nr"]?.AsValue().GetValue<string>(),
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(),
IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
IsOrganic = json["organic"]?.AsValue().GetValue<bool>() ?? false,
IsFunktionär = json["funktionär"]?.AsValue().GetValue<bool>() ?? false,
IsActive = json["active"]?.AsValue().GetValue<bool>() ?? false,
IsDeceased = json["deceased"]?.AsValue().GetValue<bool>() ?? false,
Iban = json["iban"]?.AsValue().GetValue<string>(),
Bic = json["bic"]?.AsValue().GetValue<string>(),
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>(),
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>(),
CountryNum = a["country"]!.AsValue().GetValue<int>(),
PostalDestId = a["postal_dest"]!.AsValue().GetValue<string>(),
Address = a["address"]!.AsValue().GetValue<string>(),
} : null, json["telephone_numbers"]!.AsArray().Select(n => n!.AsObject()).Select((n, i) => new MemberTelNr {
MgNr = mgnr,
Nr = i + 1,
Type = n["type"]!.AsValue().GetValue<string>(),
Number = n["number"]!.AsValue().GetValue<string>(),
Comment = n["comment"]?.AsValue().GetValue<string>(),
}).ToList(), json["email_addresses"]!.AsArray().Select(a => a!.AsObject()).Select((a, i) => new MemberEmailAddr {
MgNr = mgnr,
Nr = i + 1,
Address = a["address"]!.AsValue().GetValue<string>(),
Comment = a["comment"]?.AsValue().GetValue<string>(),
}).ToList());
}
public static JsonObject ToJson(this AreaCom c) {
return new JsonObject {
["fbnr"] = c.FbNr,
["mgnr"] = c.MgNr,
["vtrgid"] = c.VtrgId,
["cultid"] = c.CultId,
["area"] = c.Area,
["kgnr"] = c.KgNr,
["gstnr"] = c.GstNr,
["ried"] = c.Rd?.Name,
["year_from"] = c.YearFrom,
["year_to"] = c.YearTo,
["comment"] = c.Comment,
};
}
public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, 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");
rd = rde.FirstOrDefault(r => r.Name == ried);
if (rd == null) {
newRd = true;
rd = new WbRd {
KgNr = kgnr,
RdNr = (rde.Count == 0 ? 0 : rde.Max(r => r.RdNr)) + 1,
Name = ried,
};
rde.Add(rd);
}
}
return (new AreaCom {
FbNr = json["fbnr"]!.AsValue().GetValue<int>(),
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
VtrgId = json["vtrgid"]!.AsValue().GetValue<string>(),
CultId = json["cultid"]?.AsValue().GetValue<string>(),
Area = json["area"]!.AsValue().GetValue<int>(),
KgNr = kgnr,
GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
RdNr = rd?.RdNr,
YearFrom = json["year_from"]!.AsValue().GetValue<int>(),
YearTo = json["year_to"]?.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(),
}, newRd ? rd : null);
}
public static JsonObject ToJson(this Delivery d) {
return new JsonObject {
["lsnr"] = d.LsNr,
["year"] = d.Year,
@ -194,7 +562,7 @@ namespace Elwig.Helpers.Export {
if (p.Temperature != null) obj["temperature"] = p.Temperature;
if (p.Acid != null) obj["acid"] = p.Acid;
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
if (p.WeighingId != null) obj["weighing_id"] = p.WeighingId;
if (p.WeighingData != null) obj["weighing_data"] = JsonNode.Parse(p.WeighingData);
if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason;
return obj;
}).ToArray()),
@ -202,9 +570,15 @@ namespace Elwig.Helpers.Export {
};
}
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) JsonToDelivery(JsonNode json, Dictionary<int, int> currentDids) {
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids) {
var year = json["year"]!.AsValue().GetValue<int>();
var did = ++currentDids[year];
var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
if (did == -1) {
if (!currentDids.ContainsKey(year)) currentDids[year] = 0;
did = ++currentDids[year];
}
currentLsNrs[lsnr] = did;
return (new Delivery {
Year = year,
DId = did,
@ -212,7 +586,7 @@ namespace Elwig.Helpers.Export {
TimeString = json["time"]?.AsValue().GetValue<string>(),
ZwstId = json["zwstid"]!.AsValue().GetValue<string>(),
LNr = json["lnr"]!.AsValue().GetValue<int>(),
LsNr = json["lsnr"]!.AsValue().GetValue<string>(),
LsNr = lsnr,
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(),
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
@ -238,7 +612,7 @@ namespace Elwig.Helpers.Export {
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
Acid = p["acid"]?.AsValue().GetValue<double>(),
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
WeighingId = p["weighing_id"]?.AsValue().GetValue<string>(),
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts),
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
Year = year,

View File

@ -1,4 +1,4 @@
using Elwig.Models.Dtos;
using Elwig.Models.Dtos;
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;

View File

@ -1,4 +1,4 @@
namespace Elwig.Helpers {
namespace Elwig.Helpers {
public enum ExportMode {
Show, SaveList, SavePdf, Print, Email, Export, Upload
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Net.Http;
using System.Runtime.InteropServices;

View File

@ -94,7 +94,11 @@ namespace Elwig.Helpers.Printing {
public static async Task Print(string path, int copies = 1) {
try {
var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } };
var p = new Process() { StartInfo = new() {
FileName = PdfToPrinter,
CreateNoWindow = true,
UseShellExecute = false,
} };
p.StartInfo.ArgumentList.Add(path);
p.StartInfo.ArgumentList.Add("/s");
p.StartInfo.ArgumentList.Add($"copies={copies}");

View File

@ -36,7 +36,7 @@ namespace Elwig.Helpers {
public static int CurrentYear => DateTime.Now.Year;
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 6 ? 1 : 0);
public static int FollowingSeason => DateTime.Now.Year + (DateTime.Now.Month >= 11 ? 1 : 0);
public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1);
@ -537,6 +537,11 @@ namespace Elwig.Helpers {
}
}
public static int GetEntityIdetifierForPk(params object?[] primaryKey) {
var pk = primaryKey.Select(k => k?.GetHashCode() ?? 0).ToArray();
return ((IStructuralEquatable)pk).GetHashCode(EqualityComparer<int>.Default);
}
public static int? GetEntityIdentifier(object? obj) {
if (obj == null) {
return null;
@ -552,15 +557,15 @@ namespace Elwig.Helpers {
}
public static Expression<Func<AreaCom, bool>> ActiveAreaCommitments() => ActiveAreaCommitments(CurrentYear);
public static Expression<Func<AreaCom, bool>> ActiveAreaCommitments(int yearTo) =>
c => (c.YearFrom <= yearTo) && (c.YearTo == null || c.YearTo >= yearTo);
public static Expression<Func<AreaCom, bool>> ActiveAreaCommitments(int year) =>
c => (c.YearFrom <= year) && (c.YearTo == null || c.YearTo >= year);
public static IQueryable<AreaCom> ActiveAreaCommitments(IQueryable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
public static IQueryable<AreaCom> ActiveAreaCommitments(IQueryable<AreaCom> query, int yearTo) =>
query.Where(ActiveAreaCommitments(yearTo));
public static IQueryable<AreaCom> ActiveAreaCommitments(IQueryable<AreaCom> query, int year) =>
query.Where(ActiveAreaCommitments(year));
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int yearTo) =>
query.Where(c => ActiveAreaCommitments(yearTo).Invoke(c));
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int year) =>
query.Where(c => ActiveAreaCommitments(year).Invoke(c));
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text;
using System.Threading;
@ -79,7 +79,7 @@ namespace Elwig.Helpers.Weighing {
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
var parsedDate = DateOnly.Parse(date);
return new() {
Weight = int.Parse(netto),
NetWeight = int.Parse(netto),
WeighingId = identNr,
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
Date = parsedDate,

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.IO.Ports;
using System.Text;
@ -69,13 +69,14 @@ namespace Elwig.Helpers.Weighing {
var time = line[37..43];
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
var parsedDate = DateOnly.ParseExact(date, "yyyyMMdd");
return new() {
Weight = int.Parse(netto),
GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto),
WeighingId = identNr,
FullWeighingId = identNr,
Date = parsedDate,
Time = TimeOnly.ParseExact(time, "HHmmss"),
Date = DateOnly.TryParseExact(date, "yyyyMMdd", out var d) ? d : null,
Time = TimeOnly.TryParseExact(time, "HHmmss", out var t) ? t : null,
};
}

View File

@ -1,8 +1,8 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing {
/// <summary>
/// Interface for controlling a a scale which responds to commands sent to it
/// Interface for controlling a weighing scale which responds to commands sent to it
/// </summary>
public interface ICommandScale : IScale {
/// <summary>

View File

@ -1,6 +1,6 @@
namespace Elwig.Helpers.Weighing {
namespace Elwig.Helpers.Weighing {
/// <summary>
/// Interface for controlling a a scale which automatically sends weighing updates
/// Interface for controlling a weighing scale which automatically sends weighing updates
/// </summary>
public interface IEventScale : IScale {

View File

@ -2,7 +2,7 @@ using System;
namespace Elwig.Helpers.Weighing {
/// <summary>
/// Interface for controlling a industrial scale (industrial terminal, "IT")
/// Interface for controlling a industrial weighing scale (industrial terminal, "IT")
/// </summary>
public interface IScale : IDisposable {
/// <summary>

View File

@ -1,4 +1,4 @@
using System.IO.Ports;
using System.IO.Ports;
using System.IO;
using System.Net.Sockets;
using System;

View File

@ -98,7 +98,9 @@ namespace Elwig.Helpers.Weighing {
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
var parsedDate = DateOnly.Parse(date);
return new() {
Weight = int.Parse(netto),
GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto),
WeighingId = identNr,
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
Date = parsedDate,

View File

@ -1,12 +1,7 @@
using System;
using System;
namespace Elwig.Helpers.Weighing {
public class WeighingEventArgs : EventArgs {
public WeighingResult Result { get; set; }
public WeighingEventArgs(WeighingResult result) {
Result = result;
}
public class WeighingEventArgs(WeighingResult result) : EventArgs {
public readonly WeighingResult Result = result;
}
}

View File

@ -1,14 +1,25 @@
using System;
using System.Text.Json.Nodes;
namespace Elwig.Helpers.Weighing {
/// <summary>
/// Result of a weighing process on an industrial scale
/// Result of a weighing process on an industrial weighing scale
/// </summary>
public struct WeighingResult {
/// <summary>
/// Measured gross weight in kg
/// </summary>
public int? GrossWeight;
/// <summary>
/// Measured tare weight in kg
/// </summary>
public int? TareWeight;
/// <summary>
/// Measured net weight in kg
/// </summary>
public int? Weight;
public int? NetWeight;
/// <summary>
/// Weighing id (or IdentNr) provided by the scale
@ -30,12 +41,22 @@ namespace Elwig.Helpers.Weighing {
/// </summary>
public TimeOnly? Time;
/// <returns>&lt;Weight/WeighingId/Date/Time&gt;</returns>
/// <returns>&lt;[GrossWeight-TaraWeight=]NetWeight/WeighingId/Date/Time&gt;</returns>
public override readonly string ToString() {
var w = Weight != null ? $"{Weight}kg" : "";
var w = NetWeight != null ? (GrossWeight != null && TareWeight != null ? $"{GrossWeight}-{TareWeight}=" : "") + $"{NetWeight}kg" : "";
return $"<{w}/{WeighingId}/{Date:yyyy-MM-dd}/{Time:HH:mm}>";
}
public readonly JsonObject ToJson() {
var obj = new JsonObject();
if (FullWeighingId != null) obj["id"] = FullWeighingId;
if (WeighingId != null) obj["nr"] = int.Parse(WeighingId);
if (Date != null) obj["date"] = $"{Date:yyyy-MM-dd}";
if (Time != null) obj["time"] = $"{Time:HH:mm:ss}";
if (GrossWeight != null) obj["gross_weight"] = GrossWeight;
if (TareWeight != null) obj["tare_weight"] = TareWeight;
if (NetWeight != null) obj["net_weight"] = NetWeight;
return obj;
}
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers.Billing;
using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,4 @@
using Elwig.Documents;
using Elwig.Documents;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;

View File

@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

View File

@ -1,4 +1,4 @@
using Elwig.Documents;
using Elwig.Documents;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -34,7 +34,11 @@ namespace Elwig.Models.Dtos {
("UstIdNr", "UID", null, 25),
("Iban", "IBAN", null, 45),
("Bic", "BIC", null, 30),
("Comment", "Anmerkung", null, 60),
("TelNrLandline", "Festnetz", null, 35),
("TelNrMobile", "Mobil", null, 35),
("EmailAddress", "E-Mail", null, 70),
("AdditionalContact", "Weitere", null, 70),
("Comment", "Anmerkung", null, 80),
];
public MemberListData(IEnumerable<MemberListRow> rows, List<string> filterNames) :
@ -48,6 +52,9 @@ namespace Elwig.Models.Dtos {
.Include(m => m.Branch)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.AsSplitQuery()
.ToListAsync()).Select(m => new MemberListRow(m, areaCom[m.MgNr])), filterNames);
}
}
@ -76,6 +83,10 @@ namespace Elwig.Models.Dtos {
public bool IsActive;
public DateOnly? EntryDate;
public DateOnly? ExitDate;
public string? TelNrLandline;
public string? TelNrMobile;
public string? EmailAddress;
public string? AdditionalContact;
public string? Comment;
public MemberListRow(Member m, int? areaCom = null) {
@ -103,6 +114,15 @@ namespace Elwig.Models.Dtos {
IsActive = m.IsActive;
EntryDate = m.EntryDate;
ExitDate = m.ExitDate;
TelNrLandline = m.TelephoneNumbers.OrderBy(n => n.Nr).FirstOrDefault(n => n.Type == "landline")?.Number;
TelNrMobile = m.TelephoneNumbers.OrderBy(n => n.Nr).FirstOrDefault(n => n.Type == "mobile")?.Number;
EmailAddress = m.EmailAddresses.OrderBy(a => a.Nr).FirstOrDefault()?.Address;
AdditionalContact = string.Join(", ", m.TelephoneNumbers
.OrderBy(n => n.Nr)
.Select(n => n.Number)
.Except([TelNrLandline, TelNrMobile])
.Distinct()
.Concat(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).Except([EmailAddress])));
Comment = m.Comment;
AreaCommitment = areaCom == 0 ? null : areaCom;
}

View File

@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Threading.Tasks;

View File

@ -1,4 +1,4 @@
using Elwig.Documents;
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;

View File

@ -1,8 +1,10 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text.Json.Nodes;
namespace Elwig.Models.Entities {
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
@ -99,8 +101,26 @@ namespace Elwig.Models.Entities {
[Column("scale_id")]
public string? ScaleId { get; set; }
[Column("weighing_id")]
public string? WeighingId { get; set; }
[Column("weighing_data")]
public string? WeighingData { get; set; }
[NotMapped]
public (string? Id, int? Gross, int? Tare, int? Net, DateOnly? Date, TimeOnly? Time) WeighingInfo {
get {
try {
var obj = JsonNode.Parse(WeighingData!)!.AsObject();
return (
obj["id"]?.AsValue().GetValue<string>(),
obj["gross_weight"]?.AsValue().GetValue<int>(),
obj["tare_weight"]?.AsValue().GetValue<int>(),
obj["net_weight"]?.AsValue().GetValue<int>(),
DateOnly.TryParseExact(obj["date"]?.AsValue().GetValue<string>(), "yyyy-MM-dd", out var d) ? d : null,
TimeOnly.TryParseExact(obj["time"]?.AsValue().GetValue<string>(), ["HH:mm:ss", "HH:mm"], out var t) ? t : null
);
} catch {
return (null, null, null, null, null, null);
}
}
}
[Column("weighing_reason")]
public string? WeighingReason { get; set; }

View File

@ -156,8 +156,8 @@ namespace Elwig.Models.Entities {
[InverseProperty(nameof(AreaCom.Member))]
public virtual ICollection<AreaCom> AreaCommitments { get; private set; } = null!;
public IQueryable<AreaCom> ActiveAreaCommitments(AppDbContext ctx) {
return ctx.AreaCommitments.Where(c => c.MgNr == MgNr).Where(Utils.ActiveAreaCommitments());
public IQueryable<AreaCom> ActiveAreaCommitments(AppDbContext ctx, int? year = null) {
return ctx.AreaCommitments.Where(c => c.MgNr == MgNr).Where(Utils.ActiveAreaCommitments(year ?? Utils.CurrentYear));
}
[InverseProperty(nameof(BillingAddr.Member))]

View File

@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;

View File

@ -1,4 +1,4 @@
namespace Elwig.Models {
namespace Elwig.Models {
public interface IDelivery {
int Weight { get; }
double Kmw { get; }

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->

View File

@ -1,4 +1,4 @@
-- schema version 23 to 24
-- schema version 23 to 24
ALTER TABLE modifier DROP COLUMN standard;
ALTER TABLE modifier DROP COLUMN quick_select;

View File

@ -0,0 +1,4 @@
-- schema version 24 to 25
ALTER TABLE delivery_part RENAME COLUMN weighing_id TO weighing_data;
UPDATE delivery_part SET weighing_data = '{"id":"' || weighing_data || '","nr":' || SUBSTR(weighing_data, INSTR(weighing_data, '/') + 1) || '}';

View File

@ -0,0 +1,243 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Services {
public static class AreaComService {
public static async Task InitInputs(this AreaComAdminViewModel vm) {
using var ctx = new AppDbContext();
vm.FbNr = await ctx.NextFbNr();
vm.MgNr = vm.FilterMember.MgNr;
vm.YearFrom = Utils.CurrentYear;
vm.WineCult = null;
}
public static void ClearInputs(this AreaComAdminViewModel vm) {
}
public static void FillInputs(this AreaComAdminViewModel vm, AreaCom a) {
vm.FbNr = a.FbNr;
vm.MgNr = a.MgNr;
vm.YearFrom = a.YearFrom;
vm.YearTo = a.YearTo;
vm.AreaComType = ControlUtils.GetItemFromSourceWithPk(vm.AreaComTypeSource, a.VtrgId) as AreaComType;
vm.WineCult = ControlUtils.GetItemFromSourceWithPk(vm.WineCultSource, a.CultId) as WineCult;
vm.Comment = a.Comment;
vm.Kg = ControlUtils.GetItemFromSourceWithPk(vm.KgSource, a.KgNr) as AT_Kg;
vm.Rd = ControlUtils.GetItemFromSourceWithPk(vm.RdSource, a.KgNr, a.RdNr) as WbRd;
vm.GstNr = a.GstNr;
vm.Area = a.Area;
}
public static async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters(this AreaComAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<AreaCom> areaComQuery = ctx.AreaCommitments.Where(a => a.MgNr == vm.FilterMember.MgNr).OrderBy(a => a.FbNr);
if (vm.ShowOnlyActiveAreaComs) {
areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, Utils.CurrentLastSeason);
filterNames.Add($"laufend {Utils.CurrentLastSeason}");
}
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterAttr = new List<string>();
var filterNotAttr = new List<string>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var attr = await ctx.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);
var attrId = await ctx.WineAttributes.ToDictionaryAsync(a => a.AttrId, a => a);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
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($"ohne {var[e.ToUpper()].Name}");
} else if (attr.ContainsKey(e.ToLower())) {
var a = attr[e.ToLower()];
filterAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"Attribut {a.Name}");
} else if (e[0] == '!' && attr.ContainsKey(e[1..].ToLower())) {
var a = attr[e[1..].ToLower()];
filterNotAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Attribut {a.Name}");
} else if (e.Length > 2 && var.ContainsKey(e.ToUpper()[..2]) && attrId.ContainsKey(e[2..].ToUpper())) {
filterVar.Add(e[..2].ToUpper());
filterAttr.Add(e[2..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add(var[e[..2].ToUpper()].Name);
filterNames.Add($"Attribut {attrId[e[2..].ToUpper()].Name}");
} else if (e[0] == '!' && e.Length > 3 && var.ContainsKey(e.ToUpper()[1..3]) && attrId.ContainsKey(e[3..].ToUpper())) {
filterNotVar.Add(e[1..3].ToUpper());
filterNotAttr.Add(e[3..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add($"ohne {var[e[1..3].ToUpper()].Name}");
filterNames.Add($"ohne Attribut {attrId[e[3..].ToUpper()].Name}");
}
}
if (filterVar.Count > 0) areaComQuery = areaComQuery.Where(a => filterVar.Contains(a.AreaComType.WineVar.SortId));
if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId));
if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId));
if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId == null || !filterNotAttr.Contains(a.AreaComType.WineAttr.AttrId));
}
return (filterNames, areaComQuery, filter);
}
public static async Task<int> UpdateAreaCommitment(this AreaComAdminViewModel vm, int? oldFbNr) {
using var ctx = new AppDbContext();
int newFbNr = (int)vm.FbNr!;
var a = new AreaCom {
FbNr = oldFbNr ?? newFbNr,
MgNr = (int)vm.MgNr!,
YearFrom = (int)vm.YearFrom!,
YearTo = vm.YearTo,
VtrgId = vm.AreaComType!.VtrgId,
CultId = vm.WineCult?.CultId,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
KgNr = vm.Kg!.KgNr,
RdNr = vm.Rd?.RdNr,
GstNr = vm.GstNr!.Trim(),
Area = (int)vm.Area!,
};
if (vm.Rd?.RdNr == 0) {
vm.Rd.RdNr = await ctx.NextRdNr(a.KgNr);
a.RdNr = vm.Rd.RdNr;
ctx.Add(vm.Rd);
}
if (oldFbNr != null) {
ctx.Update(a);
} else {
ctx.Add(a);
}
await ctx.SaveChangesAsync();
if (newFbNr != a.FbNr) {
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {oldFbNr}");
}
await App.HintContextChange();
return newFbNr;
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int area, int? min, int? max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{area:N0} m²", row, 2, 1, bold, true);
AddToolTipCell(grid, min == null ? "" : $"{min:N0} kg", row, 3, 1, bold, true);
AddToolTipCell(grid, max == null ? "" : $"{max:N0} kg", row, 4, 1, bold, true);
}
public static async Task<(string, Grid)> GenerateToolTip(IQueryable<AreaCom> areaComs, int maxKgPerHa) {
var grid = new Grid();
grid.ColumnDefinitions.Add(new() { Width = new(10) });
grid.ColumnDefinitions.Add(new() { Width = new(60) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
AddToolTipCell(grid, "Lieferpflicht", 0, 3, 1, false, false, true);
AddToolTipCell(grid, "Lieferrecht", 0, 4, 1, false, false, true);
var text = "-";
var area = await areaComs.SumAsync(p => p.Area);
text = $"{area:N0} m²";
AddToolTipRow(grid, 1, "Geb. Fläche", null, area, null, null);
if (await areaComs.AnyAsync()) {
var attrGroups = await areaComs
.Where(c => c.AreaComType.WineAttr != null)
.GroupBy(c => c.AreaComType.WineAttr!.Name)
.Select(g => new {
Attr = g.Key,
Area = g.Sum(c => c.Area),
Min = g.Sum(c => c.Area * (c.AreaComType.MinKgPerHa ?? 0) / 10_000),
Max = g.Sum(c => c.Area * (c.AreaComType.WineAttr!.MaxKgPerHa ?? maxKgPerHa) / 10_000),
})
.OrderByDescending(g => g.Area)
.ThenBy(g => g.Attr)
.ToListAsync();
var groups = await areaComs
.Where(c => c.AreaComType.WineAttr != null)
.GroupBy(c => new {
Attr = c.AreaComType.WineAttr!.Name,
c.AreaComType.SortId,
})
.Select(g => new {
g.Key.Attr,
g.Key.SortId,
Area = g.Sum(c => c.Area),
Min = g.Sum(c => c.Area * (c.AreaComType.MinKgPerHa ?? 0) / 10_000),
Max = g.Sum(c => c.Area * (c.AreaComType.WineAttr!.MaxKgPerHa ?? maxKgPerHa) / 10_000),
})
.OrderByDescending(g => g.Area)
.ThenBy(g => g.Attr)
.ThenBy(g => g.SortId)
.ToListAsync();
var noAttr = await areaComs
.Where(c => c.AreaComType.WineAttr == null || !c.AreaComType.WineAttr.IsStrict)
.GroupBy(c => c.AreaComType.SortId)
.Select(g => new {
SortId = g.Key,
Area = g.Sum(c => c.Area),
Min = g.Sum(c => c.Area * (c.AreaComType.MinKgPerHa ?? 0) / 10_000),
Max = g.Sum(c => c.Area * (c.AreaComType.WineAttr!.MaxKgPerHa ?? maxKgPerHa) / 10_000),
})
.OrderByDescending(g => g.Area)
.ThenBy(g => g.SortId)
.ToListAsync();
int rowNum = 2;
if (noAttr.Count > 0) {
rowNum++;
AddToolTipRow(grid, rowNum++, null, null, noAttr.Sum(g => g.Area), noAttr.Sum(g => g.Min), noAttr.Sum(g => g.Max));
foreach (var g in noAttr) {
AddToolTipRow(grid, rowNum++, null, g.SortId, g.Area, g.Min, g.Max);
}
}
foreach (var attrG in attrGroups) {
rowNum++;
AddToolTipRow(grid, rowNum++, attrG.Attr, null, attrG.Area, attrG.Min, attrG.Max);
foreach (var g in groups.Where(g => g.Attr == attrG.Attr).OrderByDescending(g => g.Area).ThenBy(g => g.SortId)) {
AddToolTipRow(grid, rowNum++, null, g.SortId, g.Area, g.Min, g.Max);
}
}
}
return (text, grid);
}
}
}

View File

@ -0,0 +1,804 @@
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Helpers;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using System;
using Elwig.ViewModels;
using LinqKit;
using System.Globalization;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using System.IO;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Windows.Controls;
using System.Net.Http;
namespace Elwig.Services {
public static class DeliveryService {
public static async Task<Member?> GetMemberAsync(int mgnr) {
using var ctx = new AppDbContext();
return await ctx.Members
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.FirstOrDefaultAsync(m => m.MgNr == mgnr);
}
public static Member? GetMember(int mgnr) {
return GetMemberAsync(mgnr).GetAwaiter().GetResult();
}
public static void ClearInputs(this DeliveryAdminViewModel vm) {
}
public static void FillInputs(this DeliveryAdminViewModel vm, Delivery d) {
vm.MgNrString = $"{d.MgNr}";
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, d.ZwstId);
vm.LsNr = d.LsNr;
vm.Date = $"{d.Date:dd.MM.yyyy}";
vm.Time = $"{d.Time:HH:mm}";
vm.Comment = d.Comment ?? "";
vm.SortId = "";
vm.GradationKmwString = "";
vm.WeightString = "";
vm.IsManualWeighing = false;
vm.PartComment = "";
vm.TemperatureString = "";
vm.AcidString = "";
}
public static void FillInputs(this DeliveryAdminViewModel vm, DeliveryPart p) {
vm.SortId = p.SortId;
vm.WineAttr = ControlUtils.GetItemFromSourceWithPk(vm.WineAttrSource, p.AttrId) as WineAttr;
vm.WineCult = ControlUtils.GetItemFromSourceWithPk(vm.WineCultSource, p.CultId) as WineCult;
vm.GradationKmwString = $"{p.Kmw:N1}";
vm.WineQualityLevel = (WineQualLevel?)ControlUtils.GetItemFromSourceWithPk(vm.WineQualityLevelSource, p.QualId);
vm.WineKg = ControlUtils.GetItemFromSourceWithPk(vm.WineKgSource, p.KgNr) as AT_Kg;
vm.WineRd = ControlUtils.GetItemFromSourceWithPk(vm.WineRdSource, p.KgNr, p.RdNr) as WbRd;
vm.WineOrigin = ControlUtils.GetItemFromSourceWithPk(vm.WineOriginSource, p.HkId) as WineOrigin;
vm.WeightString = $"{p.Weight:N0}";
vm.IsManualWeighing = p.IsManualWeighing;
vm.IsNetWeight = p.IsNetWeight;
vm.Modifiers.Clear();
foreach (var m in p.Modifiers) {
vm.Modifiers.Add((Modifier)ControlUtils.GetItemFromSourceWithPk(vm.ModifiersSource, m.Year, m.ModId)!);
}
vm.PartComment = p.Comment ?? "";
vm.TemperatureString = (p.Temperature != null) ? $"{p.Temperature:N1}" : "";
vm.AcidString = (p.Acid != null) ? $"{p.Acid:N1}" : "";
vm.IsLesewagen = p.IsLesewagen ?? false;
vm.IsHandPicked = p.IsHandPicked;
vm.IsGebunden = p.IsGebunden;
vm.ScaleId = p.ScaleId;
vm.WeighingData = p.WeighingData;
vm.ManualWeighingReason = p.WeighingReason;
}
public static async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, Predicate<DeliveryPart>, List<string>)> GetFilters(this DeliveryAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<Delivery> deliveryQuery = ctx.Deliveries;
if (vm.IsReceipt && App.BranchNum > 1) {
deliveryQuery = deliveryQuery.Where(d => d.ZwstId == App.ZwstId);
filterNames.Add($"Zweigstelle {App.BranchName}");
}
if (vm.FilterMember != null) {
deliveryQuery = deliveryQuery.Where(d => d.MgNr == vm.FilterMember.MgNr);
filterNames.Add(vm.FilterMember.AdministrativeName);
}
if (vm.FilterTodayOnly) {
deliveryQuery = deliveryQuery
.Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) ||
(d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0)));
filterNames.Add(Utils.Today.ToString("dd.MM.yyyy"));
} else if (!vm.FilterAllSeasons) {
deliveryQuery = deliveryQuery.Where(d => d.Year == vm.FilterSeason);
filterNames.Add($"{vm.FilterSeason}");
}
Expression<Func<DeliveryPart, bool>> prd = p => true;
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterQual = new List<string>();
var filterNotQual = new List<string>();
var filterMgNr = new List<int>();
var filterZwst = new List<string>();
var filterAttr = new List<string>();
var filterNotAttr = new List<string>();
var filterCult = new List<string>();
var filterNotCult = new List<string>();
var filterDate = new List<(string?, string?)>();
var filterTime = new List<(string?, string?)>();
int filterYearGt = 0, filterYearLt = 0;
double filterKmwGt = 0, filterKmwLt = 0;
double filterOeGt = 0, filterOeLt = 0;
var filter = vm.TextFilter;
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var qual = await ctx.WineQualityLevels.Where(q => !q.IsPredicate).ToDictionaryAsync(q => q.QualId, q => q);
var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var zwst = await ctx.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b);
var attr = await ctx.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(' ')[0], a => a);
var cult = await ctx.WineCultivations.ToDictionaryAsync(c => c.Name.ToLower().Split(' ')[0], c => c);
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 >= 3 && e.Length <= 8 && "gebunden".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsGebunden == true);
filter.RemoveAt(i--);
filterNames.Add("gebunden");
} else if (e.Length >= 4 && e.Length <= 9 && "!gebunden".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsGebunden != true);
filter.RemoveAt(i--);
filterNames.Add("nicht gebunden");
} else if (e.Length >= 5 && e.Length <= 10 && "ungebunden".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsGebunden == false);
filter.RemoveAt(i--);
filterNames.Add("ungebunden");
} else if (e.Length >= 6 && e.Length <= 11 && "!ungebunden".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsGebunden != false);
filter.RemoveAt(i--);
filterNames.Add("nicht ungebunden");
} else if (e.Length >= 5 && e.Length <= 8 && "handlese".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsHandPicked == true);
filter.RemoveAt(i--);
filterNames.Add("Handlese");
} else if (e.Length >= 6 && e.Length <= 9 && "!handlese".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsHandPicked == false);
filter.RemoveAt(i--);
filterNames.Add("keine Handlese");
} else if (e.Length >= 5 && e.Length <= 11 && "handwiegung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsManualWeighing == true);
filter.RemoveAt(i--);
filterNames.Add("Handwiegung");
} else if (e.Length >= 6 && e.Length <= 12 && "!handwiegung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsManualWeighing == false);
filter.RemoveAt(i--);
filterNames.Add("keine Handwiegung");
} else if (e.ToLower() == "!gerebelt") {
prd = prd.And(p => p.IsNetWeight == false);
filter.RemoveAt(i--);
filterNames.Add("brutto Wiegung");
} else if (e.ToLower() == "gerebelt") {
prd = prd.And(p => p.IsNetWeight == true);
filter.RemoveAt(i--);
filterNames.Add("netto Wiegung");
} else if (e.Length >= 5 && e.Length <= 9 && "lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsLesewagen == true);
filter.RemoveAt(i--);
filterNames.Add("Lesewagen");
} else if (e.Length >= 6 && e.Length <= 10 && "!lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.IsLesewagen == false);
filter.RemoveAt(i--);
filterNames.Add("kein Lesewagen");
} 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.Length == 3 && qual.ContainsKey(e.ToUpper())) {
var qualId = e.ToUpper();
filterQual.Add(qualId);
filter.RemoveAt(i--);
filterNames.Add(qualId == "WEI" ? "abgewertet" : qual[e.ToUpper()].Name);
} else if (e[0] == '!' && qual.ContainsKey(e[1..].ToUpper())) {
var qualId = e[1..].ToUpper();
filterNotQual.Add(qualId);
filter.RemoveAt(i--);
filterNames.Add(qualId == "WEI" ? "nicht abgewertet" : "außer " + qual[e[1..].ToUpper()].Name);
} else if (e.Length >= 5 && e.Length <= 10 && "abgewertet".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterQual.Add("WEI");
filter.RemoveAt(i--);
filterNames.Add("abgewertet");
} else if (e.Length >= 6 && e.Length <= 11 && "!abgewertet".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterNotQual.Add("WEI");
filter.RemoveAt(i--);
filterNames.Add("nicht abgewertet");
} 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 (attr.ContainsKey(e.ToLower())) {
var a = attr[e.ToLower()];
filterAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"Attribut {a.Name}");
} else if (e[0] == '!' && attr.ContainsKey(e[1..].ToLower())) {
var a = attr[e[1..].ToLower()];
filterNotAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Attribut {a.Name}");
} else if (cult.ContainsKey(e.ToLower())) {
var c = cult[e.ToLower()];
filterCult.Add(c.CultId);
filter.RemoveAt(i--);
filterNames.Add($"Bewirtschaftung {c.Name}");
} else if (e[0] == '!' && cult.ContainsKey(e[1..].ToLower())) {
var c = cult[e[1..].ToLower()];
filterNotCult.Add(c.CultId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Bewirtschaftung {c.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 (e.StartsWith('>') || e.StartsWith('<')) {
if (double.TryParse(e[1..], out var num)) {
switch ((e[0], num)) {
case ('>', <= 30): filterKmwGt = num; break;
case ('<', <= 30): filterKmwLt = num; break;
case ('>', >= 1900): filterYearGt = (int)num; break;
case ('<', >= 1900): filterYearLt = (int)num; break;
case ('>', _): filterOeGt = num; break;
case ('<', _): filterOeLt = num; break;
}
filter.RemoveAt(i--);
}
if (e.Length == 1) filter.RemoveAt(i--);
} else if (e.Length > 1 && Utils.FromToRegex.IsMatch(e)) {
var parts = e.Split("-");
double? from = (parts[0].Length > 0) ? double.Parse(parts[0], CultureInfo.InvariantCulture) : null;
double? to = (parts[1].Length > 0) ? double.Parse(parts[1], CultureInfo.InvariantCulture) : null;
switch ((from, to)) {
case ( <= 30, <= 30):
case ( <= 30, null):
case (null, <= 30):
filterKmwGt = from ?? 0;
filterKmwLt = to ?? 0;
break;
case ( >= 1900, >= 1900):
case ( >= 1900, null):
case (null, >= 1900):
filterYearGt = (int)(from ?? 0);
filterYearLt = (int)(to ?? -1) + 1;
break;
case (_, _):
filterOeGt = from ?? 0;
filterOeLt = to ?? 0;
break;
}
filter.RemoveAt(i--);
} else if (e.Length > 1 && Utils.FromToTimeRegex.IsMatch(e)) {
var parts = e.Split("-");
filterTime.Add((TimeOnly.TryParse(parts[0], out var from) ? $"{from:HH:mm}" : null, TimeOnly.TryParse(parts[1], out var to) ? $"{to:HH:mm}" : null));
filter.RemoveAt(i--);
var t = filterTime.Last();
if (t.Item1 != null && t.Item2 != null) {
filterNames.Add($"{t.Item1}{t.Item2}");
} else if (t.Item1 != null) {
filterNames.Add($"ab {t.Item1}");
} else if (t.Item2 != null) {
filterNames.Add($"bis {t.Item2}");
}
} 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 (filterYearGt > 0) prd = prd.And(p => p.Year >= filterYearGt);
if (filterYearLt > 0) prd = prd.And(p => p.Year < filterYearLt);
if (filterMgNr.Count > 0) prd = prd.And(p => filterMgNr.Contains(p.Delivery.MgNr));
if (filterDate.Count > 0) {
var pr = PredicateBuilder.New<DeliveryPart>(false);
foreach (var (d1, d2) in filterDate)
pr.Or(p => (d1 == null || d1.CompareTo(p.Delivery.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(p.Delivery.DateString.Substring(10 - d2.Length)) >= 0));
prd = prd.And(pr);
}
if (filterTime.Count > 0) {
var pr = PredicateBuilder.New<DeliveryPart>(false);
foreach (var (t1, t2) in filterTime)
pr.Or(p => (t1 == null || t1.CompareTo(p.Delivery.TimeString) <= 0) && (t2 == null || t2.CompareTo(p.Delivery.TimeString) > 0));
prd = prd.And(p => p.Delivery.TimeString != null).And(pr);
}
if (filterVar.Count > 0) prd = prd.And(p => filterVar.Contains(p.SortId));
if (filterNotVar.Count > 0) prd = prd.And(p => !filterNotVar.Contains(p.SortId));
if (filterQual.Count > 0) prd = prd.And(p => filterQual.Contains(p.QualId));
if (filterNotQual.Count > 0) prd = prd.And(p => !filterNotQual.Contains(p.QualId));
if (filterZwst.Count > 0) prd = prd.And(p => filterZwst.Contains(p.Delivery.ZwstId));
if (filterAttr.Count > 0) prd = prd.And(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
if (filterNotAttr.Count > 0) prd = prd.And(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
if (filterCult.Count > 0) prd = prd.And(p => p.CultId != null && filterCult.Contains(p.CultId));
if (filterNotCult.Count > 0) prd = prd.And(p => p.CultId == null || !filterNotCult.Contains(p.CultId));
if (filterKmwGt > 0) prd = prd.And(p => p.Kmw >= filterKmwGt);
if (filterKmwLt > 0) prd = prd.And(p => p.Kmw < filterKmwLt);
if (filterOeGt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
if (filterOeLt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
if (filterYearGt > 0 && filterYearLt > 0) {
filterNames.Insert(0, $"{filterYearGt}{filterYearLt - 1}");
} else if (filterYearGt > 0) {
filterNames.Insert(0, $"ab {filterYearGt}");
} else if (filterYearLt > 0) {
filterNames.Insert(0, $"bis {filterYearLt - 1}");
}
if (filterKmwGt > 0 && filterKmwLt > 0) {
filterNames.Add($"{filterKmwGt:N1}{filterKmwLt:N1} °KMW");
} else if (filterKmwGt > 0) {
filterNames.Add($"ab {filterKmwGt:N1} °KMW");
} else if (filterKmwLt > 0) {
filterNames.Add($"unter {filterKmwLt:N1} °KMW");
}
if (filterOeGt > 0 && filterOeLt > 0) {
filterNames.Add($"{filterOeGt:N1}{filterOeLt:N1} °Oe");
} else if (filterOeGt > 0) {
filterNames.Add($"ab {filterOeGt:N1} °Oe");
} else if (filterOeLt > 0) {
filterNames.Add($"unter {filterOeLt:N1} °Oe");
}
}
IQueryable<DeliveryPart> dpq = deliveryQuery
.SelectMany(d => d.Parts)
.Where(prd)
.OrderBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr);
return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, prd.Invoke, filter);
}
public static async Task<DeliveryPart> UpdateDeliveryPart(this DeliveryAdminViewModel vm, int? oldYear, int? oldDid, int? oldDpnr, bool dateHasChanged, bool timeHasChanged, bool timeIsDefault) {
using var ctx = new AppDbContext();
int year = oldYear ?? Utils.CurrentYear;
int did = oldDid ?? await ctx.NextDId(year);
int dpnr = oldDpnr ?? await ctx.NextDPNr(year, did);
var oldDelivery = await ctx.Deliveries.FindAsync(year, did);
bool deliveryNew = (oldDid == null);
bool partNew = (oldDpnr == null);
var originalMgNr = oldDelivery?.MgNr;
var originalMemberKgNr = oldDelivery?.Member.DefaultKgNr;
var date = DateOnly.ParseExact(vm.Date!, "dd.MM.yyyy");
int? newLnr = (deliveryNew || dateHasChanged) ? await ctx.NextLNr(date) : null;
string? newTimeString = null;
if (partNew && timeIsDefault) {
newTimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (partNew || timeHasChanged) {
newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00";
}
var d = new Delivery {
Year = year,
DId = did,
DateString = $"{date:yyyy-MM-dd}",
TimeString = newTimeString ?? oldDelivery?.TimeString,
LNr = newLnr ?? oldDelivery!.LNr,
ZwstId = vm.Branch!.ZwstId,
LsNr = vm.LsNr!,
MgNr = (int)vm.MgNr!,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
};
var p = new DeliveryPart {
Year = year,
DId = did,
DPNr = dpnr,
SortId = vm.WineVar!.SortId,
AttrId = vm.WineAttr?.AttrId,
CultId = vm.WineCult?.CultId,
Kmw = (double)vm.GradationKmw!,
QualId = vm.WineQualityLevel!.QualId,
HkId = vm.WineOrigin!.HkId,
KgNr = vm.WineKg?.KgNr,
RdNr = vm.WineRd?.RdNr,
IsNetWeight = vm.IsNetWeight,
IsHandPicked = vm.IsHandPicked,
IsLesewagen = vm.IsLesewagen,
IsGebunden = vm.IsGebunden,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Weight = (int)vm.Weight!,
IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId,
WeighingData = vm.WeighingData,
WeighingReason = vm.ManualWeighingReason,
};
try {
if (oldDelivery != null && ctx.Entry(oldDelivery) is EntityEntry<Delivery> entry) {
entry.State = EntityState.Detached;
}
if (!deliveryNew) {
ctx.Update(d);
} else {
ctx.Add(d);
}
if (!partNew) {
ctx.Update(p);
} else {
ctx.Add(p);
}
ctx.UpdateDeliveryPartModifiers(p, await ctx.DeliveryPartModifiers
.Where(m => m.Year == p.Year && m.DId == p.DId && m.DPNr == p.DPNr)
.Select(m => m.Modifier)
.ToListAsync(), vm.Modifiers);
if (originalMgNr != null && originalMgNr.Value != d.MgNr) {
// update origin (KgNr), if default is selected
var newKgNr = (await ctx.Members.FindAsync(d.MgNr))?.DefaultKgNr;
foreach (var part in d.Parts.Where(part => part.DPNr != dpnr && part.KgNr == originalMemberKgNr)) {
part.KgNr = newKgNr;
ctx.Update(part);
}
}
await ctx.SaveChangesAsync();
} 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, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
return p;
}
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ctx = new AppDbContext();
var d = (await ctx.Deliveries.FindAsync(year, did))!;
using var doc = new DeliveryNote(d, ctx);
await Utils.ExportDocument(doc, mode, d.LsNr, (d.Member, $"{DeliveryNote.Name} Nr. {d.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {d.LsNr}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryJournal(this DeliveryAdminViewModel vm, int modeWho, ExportMode exportMode) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;
List<string> filterNames = [];
if (modeWho == 0) {
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 {
var date = $"{Utils.Today:yyyy-MM-dd}";
query = ctx.DeliveryParts
.Where(p => p.Delivery.DateString == date);
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
}
query = query
.OrderBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr);
if (exportMode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryJournal.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryJournalData.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 if (exportMode == 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",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
await ElwigData.Export(d.FileName, await query
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts)
.ThenInclude(p => p.PartModifiers)
.AsSplitQuery()
.ToListAsync(), 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) {
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
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts)
.ThenInclude(p => p.PartModifiers)
.AsSplitQuery()
.ToListAsync();
if (list.Count == 0) {
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
} else {
await ElwigData.Export(path, list, filterNames);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
MessageBox.Show($"Hochladen von {list.Count} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
MessageBoxButton.OK, MessageBoxImage.Information);
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, exportMode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateWineQualityStatistics(this DeliveryAdminViewModel vm, int modeWho, ExportMode exportMode) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;
List<string> filterNames = [];
if (modeWho == 0) {
var (f, _, q, _, _) = await vm.GetFilters(ctx);
query = q;
filterNames.AddRange(f);
} else {
var date = $"{Utils.Today:yyyy-MM-dd}";
query = ctx.DeliveryParts
.Where(p => p.Delivery.DateString == date);
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
}
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);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true);
if (total1 != null && total1 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true);
if (total2 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true);
}
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{min:N1}°", row, 2, 1, bold, true);
AddToolTipCell(grid, $"{avg:N1}°", row, 3, 1, bold, true);
AddToolTipCell(grid, $"{max:N1}°", row, 4, 1, bold, true);
}
public static async Task<(string WeightText, Grid WeightGrid, string GradationText, Grid GradationGrid)> GenerateToolTip(IQueryable<DeliveryPart> deliveryParts) {
var wGrid = new Grid();
wGrid.ColumnDefinitions.Add(new() { Width = new(10) });
wGrid.ColumnDefinitions.Add(new() { Width = new(60) });
wGrid.ColumnDefinitions.Add(new() { Width = new(80) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
var wText = "-";
var gGrid = new Grid();
gGrid.ColumnDefinitions.Add(new() { Width = new(10) });
gGrid.ColumnDefinitions.Add(new() { Width = new(60) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
AddToolTipCell(gGrid, "Min.", 0, 2, 1, false, false, true);
AddToolTipCell(gGrid, "⌀", 0, 3, 1, false, false, true);
AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true);
var gText = "-";
var weight = await deliveryParts.SumAsync(p => p.Weight);
wText = $"{weight:N0} kg";
AddWeightToolTipRow(wGrid, 0, "Gewicht", null, weight, null, weight);
if (await deliveryParts.AnyAsync()) {
var kmwMin = await deliveryParts.MinAsync(p => p.Kmw);
var kmwAvg = Utils.AggregateDeliveryPartsKmw(deliveryParts);
var kmwMax = await deliveryParts.MaxAsync(p => p.Kmw);
gText = $"{kmwMin:N1}° / {kmwAvg:N1}° / {kmwMax:N1}°";
AddGradationToolTipRow(gGrid, 1, "Gradation", null, kmwMin, kmwAvg, kmwMax);
var attrGroups = await deliveryParts
.GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name })
.Select(g => new {
g.Key.Attr,
g.Key.Cult,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
Max = g.Max(p => p.Kmw),
})
.OrderByDescending(g => g.Weight)
.ThenBy(g => g.Attr)
.ToListAsync();
var sortGroups = await deliveryParts
.GroupBy(p => p.SortId)
.Select(g => new {
SortId = g.Key,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
Max = g.Max(p => p.Kmw),
})
.OrderByDescending(g => g.Weight)
.ThenBy(g => g.SortId)
.ToListAsync();
var groups = await deliveryParts
.GroupBy(p => new {
Attr = p.Attribute!.Name,
Cult = p.Cultivation!.Name,
p.SortId,
})
.Select(g => new {
g.Key.Attr,
g.Key.Cult,
g.Key.SortId,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
Max = g.Max(p => p.Kmw)
})
.OrderByDescending(g => g.SortId)
.ThenBy(g => g.Attr)
.ThenBy(g => g.SortId)
.ToListAsync();
int rowNum = 1;
foreach (var attrG in attrGroups) {
rowNum++;
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
AddWeightToolTipRow(wGrid, rowNum++, name, null, attrG.Weight, attrG.Weight, weight);
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
AddWeightToolTipRow(wGrid, rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight);
}
}
rowNum = 2;
foreach (var attrG in attrGroups) {
rowNum++;
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
AddGradationToolTipRow(gGrid, rowNum++, name, null, attrG.Min, attrG.Avg, attrG.Max);
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
AddGradationToolTipRow(gGrid, rowNum++, null, g.SortId, g.Min, g.Avg, g.Max);
}
}
if (attrGroups.Count == 1) {
var g = attrGroups.First();
var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult;
if (name != null) {
wText += $" [{name}]";
gText += $" [{name}]";
}
if (sortGroups.Count > 1 && sortGroups.Count <= 4) {
wText += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}";
gText += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.SortId == null ? "" : $" [{g.SortId}]")))}";
}
} else if (attrGroups.Count <= 4) {
wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
}
}
return (wText, wGrid, gText, gGrid);
}
}
}

View File

@ -0,0 +1,602 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using System.Windows.Input;
using System.Windows;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using Elwig.ViewModels;
using System.IO;
using System.Net.Http;
namespace Elwig.Services {
public static class MemberService {
public static async Task InitInputs(this MemberAdminViewModel vm) {
using var ctx = new AppDbContext();
vm.MgNrString = $"{await ctx.NextMgNr()}";
vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy");
if (vm.BranchSource.Count() == 1)
vm.Branch = vm.BranchSource.First();
vm.IsActive = true;
vm.ContactViaPost = true;
vm.EnableMemberReferenceButton = false;
}
public static void ClearInputs(this MemberAdminViewModel vm) {
vm.IsMemberSelected = false;
vm.MemberHasEmail = false;
vm.MemberCanSendEmail = false;
vm.EnableMemberReferenceButton = false;
vm.StatusDeliveriesLastSeason = "-";
vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
vm.StatusDeliveriesLastSeasonToolTip = null;
vm.StatusDeliveriesThisSeason = "-";
vm.StatusDeliveriesThisSeasonInfo = $"{Utils.CurrentLastSeason}";
vm.StatusDeliveriesThisSeasonToolTip = null;
vm.StatusAreaCommitment = "-";
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitmentToolTip = null;
vm.Age = "-";
}
public static async Task FillInputs(this MemberAdminViewModel vm, Member m) {
vm.IsMemberSelected = true;
vm.MgNrString = $"{m.MgNr}";
vm.PredecessorMgNrString = $"{m.PredecessorMgNr}";
vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
vm.Prefix = m.Prefix;
vm.GivenName = m.GivenName;
vm.FamilyName = m.FamilyName;
vm.Suffix = m.Suffix;
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();
} else if (m.Birthday != null) {
vm.Age = "ca. " + (DateTime.Now.Year - int.Parse(m.Birthday[^4..])).ToString();
} else {
vm.Age = "-";
}
vm.IsDeceased = m.IsDeceased;
vm.Address = m.Address;
if (m.PostalDest.AtPlz is AT_PlzDest p) {
vm.PlzString = $"{p.Plz}";
vm.Ort = ControlUtils.GetItemFromSource(vm.OrtSource, p);
} else {
vm.PlzString = null;
vm.Ort = null;
}
var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
for (int i = 0; i < vm.EmailAddresses.Count; i++) {
if (i < emailAddrs.Count) {
var emailAddr = emailAddrs[i];
vm.EmailAddresses[i] = emailAddr.Address;
} else {
vm.EmailAddresses[i] = null;
}
}
var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList();
for (int i = 0; i < vm.PhoneNrs.Count; i++) {
if (i < phoneNrs.Count) {
var phoneNr = phoneNrs[i];
var idx = vm.PhoneNrTypes.Select((e, i) => (e, i)).FirstOrDefault(kv => kv.e.Key == phoneNr.Type).i;
vm.PhoneNrs[i] = new(idx, phoneNr.Number, phoneNr.Comment);
} else {
vm.PhoneNrs[i] = new();
}
}
vm.Iban = m.Iban;
vm.Bic = m.Bic;
vm.UstIdNr = m.UstIdNr;
vm.LfbisNr = m.LfbisNr;
vm.IsBuchführend = m.IsBuchführend;
vm.IsOrganic = m.IsOrganic;
var billingAddr = m.BillingAddress;
if (billingAddr != null) {
vm.BillingName = billingAddr.Name;
vm.BillingAddress = billingAddr.Address;
if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
vm.BillingPlzString = $"{b.Plz}";
vm.BillingOrt = ControlUtils.GetItemFromSource(vm.BillingOrtSource, b);
}
} else {
vm.BillingName = null;
vm.BillingAddress = null;
vm.BillingPlzString = null;
vm.BillingOrt = null;
}
vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
vm.BusinessSharesString = $"{m.BusinessShares}";
vm.AccountingNr = m.AccountingNr;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
vm.Comment = m.Comment;
vm.IsActive = m.IsActive;
vm.IsVollLieferant = m.IsVollLieferant;
vm.IsFunktionär = m.IsFunktionär;
vm.ContactViaPost = m.ContactViaPost;
vm.ContactViaEmail = m.ContactViaEmail;
Dictionary<int, int> deliveries;
using (var ctx = new AppDbContext()) {
var d1 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr);
var (_, d1Grid, _, _) = await DeliveryService.GenerateToolTip(d1.SelectMany(d => d.Parts));
vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
vm.StatusDeliveriesLastSeason = $"{await d1.CountAsync():N0} ({await d1.SumAsync(d => d.Parts.Count):N0}), {await d1.SelectMany(d => d.Parts).SumAsync(p => p.Weight):N0} kg";
vm.StatusDeliveriesLastSeasonToolTip = d1Grid;
var d2 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason && d.MgNr == m.MgNr);
var (_, d2Grid, _, _) = await DeliveryService.GenerateToolTip(d2.SelectMany(d => d.Parts));
vm.StatusDeliveriesThisSeasonInfo = $"{Utils.CurrentLastSeason}";
vm.StatusDeliveriesThisSeason = $"{await d2.CountAsync():N0} ({await d2.SumAsync(d => d.Parts.Count):N0}), {await d2.SelectMany(d => d.Parts).SumAsync(p => p.Weight):N0} kg";
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);
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitment = text;
vm.StatusAreaCommitmentToolTip = grid;
deliveries = ctx.Deliveries
.Where(d => d.MgNr == m.MgNr)
.SelectMany(d => d.Parts)
.GroupBy(d => d.Year)
.ToDictionary(g => g.Key, g => g.Sum(d => d.Weight));
}
vm.MemberHasEmail = m.EmailAddresses.Count > 0;
vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, 0) > 0).ToList();
}
public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<Member> memberQuery = ctx.Members;
if (vm.ShowOnlyActiveMembers) {
memberQuery = memberQuery.Where(m => m.IsActive);
filterNames.Add("aktive Mitglieder");
}
var filterMgNr = new List<int>();
var filterZwst = new List<string>();
var filterKgNr = new List<int>();
var filterLfbisNr = new List<string>();
var filterUstIdNr = new List<string>();
var filterAreaCom = new List<string>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
var branches = await ctx.Branches.ToListAsync();
var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var kgs = await ctx.WbKgs.ToDictionaryAsync(k => k.AtKg.Name.ToLower(), k => k.AtKg);
var areaComs = await ctx.AreaCommitmentTypes.ToDictionaryAsync(t => $"{t.SortId}{t.AttrId}", t => t);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
if (e.Length >= 5 && e.Length <= 10 && "funktionär".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsFunktionär);
filter.RemoveAt(i--);
filterNames.Add("Funktionäre");
} else if (e.Length >= 6 && e.Length <= 11 && e[0] == '!' && "funktionär".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsFunktionär);
filter.RemoveAt(i--);
filterNames.Add("Nicht-Funktionäre");
} else if (e.Equals("bio", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsOrganic);
filter.RemoveAt(i--);
filterNames.Add("Bio-Betriebe");
} else if (e.Equals("!bio", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsOrganic);
filter.RemoveAt(i--);
filterNames.Add("Nicht-Bio-Betriebe");
} else if (e.Length >= 4 && e.Length <= 13 && "volllieferant".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsVollLieferant);
filter.RemoveAt(i--);
filterNames.Add("Volllieferanten");
} else if (e.Length >= 5 && e.Length <= 14 && e[0] == '!' && "volllieferant".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsVollLieferant);
filter.RemoveAt(i--);
filterNames.Add("Nicht-Vollieferanten");
} else if (e.Length >= 5 && e.Length <= 11 && "buchführend".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsBuchführend);
filter.RemoveAt(i--);
filterNames.Add("buchführend");
} else if (e.Length >= 6 && e.Length <= 12 && e[0] == '!' && "buchführend".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsBuchführend);
filter.RemoveAt(i--);
filterNames.Add("pauschaliert");
} else if (e.Length >= 8 && e.Length <= 12 && "pauschaliert".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsBuchführend);
filter.RemoveAt(i--);
filterNames.Add("pauschaliert");
} else if (e.Length >= 9 && e.Length <= 13 && e[0] == '!' && "pauschaliert".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsBuchführend);
filter.RemoveAt(i--);
filterNames.Add("buchführend");
} else if (e.Equals("email", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.EmailAddresses.Count > 0);
filter.RemoveAt(i--);
filterNames.Add("mind. eine E-Mail-Adresse");
} else if (e.Equals("!email", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.EmailAddresses.Count == 0);
filter.RemoveAt(i--);
filterNames.Add("keine E-Mail-Adresse");
} else if (e.Equals("telnr", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Count > 0);
filter.RemoveAt(i--);
filterNames.Add("mind. eine Tel.-Nr.");
} else if (e.Equals("!telnr", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Count == 0);
filter.RemoveAt(i--);
filterNames.Add("keine Tel.-Nr.");
} else if (e.Equals("kontakt:email", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.ContactViaEmail);
filter.RemoveAt(i--);
filterNames.Add("Kontaktart E-Mail");
} else if (e.Equals("!kontakt:email", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.ContactViaEmail);
filter.RemoveAt(i--);
filterNames.Add("nicht Kontaktart E-Mail");
} else if (e.Equals("kontakt:post", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.ContactViaPost);
filter.RemoveAt(i--);
filterNames.Add("Kontaktart Post");
} else if (e.Equals("!kontakt:post", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
filter.RemoveAt(i--);
filterNames.Add("nicht Kontaktart Post");
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);
filterNames.Add($"MgNr. {e}");
} else if (kgs.TryGetValue(e, out var kg)) {
filterKgNr.Add(kg.KgNr);
filter.RemoveAt(i--);
filterNames.Add($"Stammgemeinde {kg.Name}");
} else if (e.StartsWith("zwst:")) {
try {
var branch = branches.Where(b => b.Name.StartsWith(e[5..], StringComparison.CurrentCultureIgnoreCase)).Single();
filterZwst.Add(branch.ZwstId);
filter.RemoveAt(i--);
filterNames.Add($"Zweigstelle {branch.Name}");
} catch (InvalidOperationException) { }
} else if (e.StartsWith('+') && e[1..].All(char.IsAsciiDigit)) {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
filter.RemoveAt(i--);
filterNames.Add($"Tel.-Nr. {e}");
} else if (areaComs.ContainsKey(e.ToUpper())) {
filterAreaCom.Add(e.ToUpper());
filter.RemoveAt(i--);
filterNames.Add($"Flächenbindung {e.ToUpper()}");
} else if (Validator.CheckLfbisNr(e)) {
filterLfbisNr.Add(e);
filter.RemoveAt(i--);
filterNames.Add($"Betriebsnummer {e}");
} else if (Validator.CheckUstIdNr(e.ToUpper())) {
filterUstIdNr.Add(e.ToUpper());
filter.RemoveAt(i--);
filterNames.Add($"UID {e.ToUpper()}");
} 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) memberQuery = memberQuery.Where(m => filterMgNr.Contains(m.MgNr));
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId)));
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
}
return (filterNames, memberQuery, filter);
}
public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ctx = new AppDbContext();
using var doc = new MemberDataSheet(m, ctx);
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets();
await App.HintContextChange();
using var ctx = new AppDbContext();
var data = await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, year, m);
using var doc = new DeliveryConfirmation(ctx, year, m, data);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr);
var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);
using var doc = new CreditNote(ctx, p, data[m.MgNr],
b.ConsiderContractPenalties, b.ConsiderTotalPenalty, b.ConsiderAutoBusinessShares, b.ConsiderCustomModifiers,
await ctx.GetMemberUnderDelivery(year, m.MgNr));
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {v.Name}", $"Im Anhang finden Sie die Traubengutschrift {v.Name}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
public static async Task GenerateMemberList(this MemberAdminViewModel vm, int modeWho, ExportMode exportMode) {
using var ctx = new AppDbContext();
IQueryable<Member> query;
List<string> filterNames = [];
if (modeWho == 0) {
query = ctx.Members.Where(m => m.IsActive);
filterNames.Add("aktive Mitglieder");
} else if (modeWho == 1) {
var (f, q, _) = await vm.GetFilters(ctx);
query = q;
filterNames.AddRange(f);
} else {
query = ctx.Members;
}
if (vm.MemberListOrderByMgNr) {
query = query
.OrderBy(m => m.Branch!.Name)
.ThenBy(m => m.MgNr);
} else if (vm.MemberListOrderByName) {
query = query
.OrderBy(m => m.Branch!.Name)
.ThenBy(m => m.FamilyName)
.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.GivenName)
.ThenBy(m => m.MgNr);
}
if (exportMode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{MemberList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{MemberList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await MemberListData.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 if (exportMode == 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",
Title = $"{MemberList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
await ElwigData.Export(d.FileName, await query
.OrderBy(m => m.MgNr)
.Include(m => m.BillingAddress)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.AsSplitQuery()
.ToListAsync(), 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) {
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
.OrderBy(m => m.MgNr)
.Include(m => m.BillingAddress)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.AsSplitQuery()
.ToListAsync();
if (list.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 Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
MessageBox.Show($"Hochladen von {list.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);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await MemberListData.FromQuery(query, filterNames);
using var doc = new MemberList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, exportMode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
public static async Task<int> UpdateMember(this MemberAdminViewModel vm, int? oldMgNr) {
using var ctx = new AppDbContext();
var newMgNr = (int)vm.MgNr!;
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,
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
IsDeceased = vm.IsDeceased,
CountryNum = 40, // Austria AT AUT
PostalDestId = vm.Ort!.Id,
Address = vm.Address!,
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic,
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
BusinessShares = (int)vm.BusinessShares!,
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
IsActive = vm.IsActive,
IsVollLieferant = vm.IsVollLieferant,
IsFunktionär = vm.IsFunktionär,
ZwstId = vm.Branch?.ZwstId,
DefaultKgNr = vm.DefaultKg?.KgNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
ContactViaPost = vm.ContactViaPost,
ContactViaEmail = vm.ContactViaEmail,
};
if (oldMgNr != null) {
ctx.Update(m);
} else {
ctx.Add(m);
}
ctx.RemoveRange(ctx.BillingAddresses.Where(a => a.MgNr == oldMgNr));
if (vm.BillingOrt != null && vm.BillingName != null) {
var p = vm.BillingOrt;
ctx.Add(new BillingAddr {
MgNr = m.MgNr,
Name = vm.BillingName,
Address = vm.BillingAddress ?? "",
CountryNum = p.CountryNum,
PostalDestId = p.Id,
});
}
ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(t => t.MgNr == oldMgNr));
ctx.AddRange(vm.PhoneNrs
.Where(input => input.Number != null)
.Select((input, i) => new MemberTelNr {
MgNr = m.MgNr,
Nr = i + 1,
Type = input.Type == -1 ? (input.Number!.StartsWith("+43 ") && input.Number![4] == '6' ? "mobile" : "landline") : vm.PhoneNrTypes[input.Type].Key,
Number = input.Number!,
Comment = input.Comment,
}));
ctx.RemoveRange(ctx.MemberEmailAddrs.Where(e => e.MgNr == oldMgNr));
ctx.AddRange(vm.EmailAddresses
.Where(input => input != null && input != "")
.Select((input, i) => new MemberEmailAddr {
MgNr = m.MgNr,
Nr = i + 1,
Address = input!,
Comment = null,
}));
await ctx.SaveChangesAsync();
if (vm.TransferPredecessorAreaComs is int year && m.PredecessorMgNr is int predecessor) {
var areaComs = await ctx.AreaCommitments
.Where(c => c.MgNr == predecessor && (c.YearTo == null || c.YearTo >= year))
.ToListAsync();
var fbNr = await ctx.NextFbNr();
ctx.AddRange(areaComs.Select((c, i) => new AreaCom {
FbNr = fbNr + i,
MgNr = m.MgNr,
VtrgId = c.VtrgId,
CultId = c.CultId,
Area = c.Area,
KgNr = c.KgNr,
GstNr = c.GstNr,
RdNr = c.RdNr,
YearFrom = year,
YearTo = c.YearTo,
}));
foreach (var ac in areaComs)
ac.YearTo = year - 1;
ctx.UpdateRange(areaComs);
await ctx.SaveChangesAsync();
}
vm.TransferPredecessorAreaComs = null;
if (vm.CancelAreaComs is int yearTo) {
var areaComs = await ctx.AreaCommitments
.Where(c => c.MgNr == m.MgNr && (c.YearTo == null || c.YearTo > yearTo))
.ToListAsync();
foreach (var ac in areaComs)
ac.YearTo = yearTo;
ctx.UpdateRange(areaComs);
await ctx.SaveChangesAsync();
}
vm.CancelAreaComs = null;
if (newMgNr != m.MgNr) {
await ctx.Database.ExecuteSqlAsync($"UPDATE member SET mgnr = {newMgNr} WHERE mgnr = {oldMgNr}");
}
await App.HintContextChange();
return newMgNr;
}
}
}

View File

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Elwig;component/Controls/UnitTextBox.xaml"/>
<ResourceDictionary Source="/Elwig;component/Controls/IntegerUpDown.xaml"/>

View File

@ -0,0 +1,95 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
namespace Elwig.ViewModels {
public partial class AreaComAdminViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Flächenbindungen - Elwig";
public required Member FilterMember { get; set; }
[ObservableProperty]
private string? _searchQuery = "";
public List<string> TextFilter {
get => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
set => SearchQuery = string.Join(' ', value.Where(e => e.Length > 0));
}
[ObservableProperty]
private bool _showOnlyActiveAreaComs;
[ObservableProperty]
private string? _fbNrString;
public int? FbNr {
get => int.TryParse(FbNrString, out var mgnr) ? mgnr : null;
set => FbNrString = $"{value}";
}
[ObservableProperty]
private string? _mgNrString;
public int? MgNr {
get => int.TryParse(MgNrString, out var mgnr) ? mgnr : null;
set => MgNrString = $"{value}";
}
[ObservableProperty]
private string? _yearFromString;
public int? YearFrom {
get => int.TryParse(YearFromString, out var year) ? year : null;
set => YearFromString = $"{value}";
}
[ObservableProperty]
private string? _yearToString;
public int? YearTo {
get => int.TryParse(YearToString, out var year) ? year : null;
set => YearToString = $"{value}";
}
[ObservableProperty]
private AreaComType? _areaComType;
[ObservableProperty]
private IEnumerable<AreaComType> _areaComTypeSource = [];
[ObservableProperty]
private object? _wineCultObj;
public WineCult? WineCult {
get => WineCultObj as WineCult;
set => WineCultObj = value ?? WineCultSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _wineCultSource = [];
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private AT_Kg? _kg;
[ObservableProperty]
private IEnumerable<AT_Kg> _kgSource = [];
[ObservableProperty]
private object? _rdObj;
public WbRd? Rd {
get => RdObj as WbRd;
set => RdObj = value ?? RdSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _rdSource = [];
[ObservableProperty]
private string? _gstNr;
[ObservableProperty]
private string? _areaString;
public int? Area {
get => int.TryParse(AreaString, out var area) ? area : null;
set => AreaString = $"{value}";
}
[ObservableProperty]
private string _statusAreaCommitments = "-";
[ObservableProperty]
private string _statusContracts = "-";
[ObservableProperty]
private string _statusArea = "-";
[ObservableProperty]
private Grid? _statusAreaToolTip;
}
}

View File

@ -0,0 +1,180 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Controls;
namespace Elwig.ViewModels {
public partial class DeliveryAdminViewModel : ObservableObject {
public bool IsReceipt = false;
[ObservableProperty]
private string _title = "Lieferungen - Elwig";
public Member? FilterMember { get; set; }
[ObservableProperty]
private string? _searchQuery = "";
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
[ObservableProperty]
private string? _lastScaleError;
[ObservableProperty]
private string? _manualWeighingReason;
[ObservableProperty]
private string? _scaleId;
[ObservableProperty]
private string? _weighingData;
[ObservableProperty]
private bool _filterTodayOnly;
[ObservableProperty]
private bool _filterAllSeasons;
[ObservableProperty]
private bool _enableAllSeasons;
[ObservableProperty]
private string? _filterSeasonString;
public int? FilterSeason {
get => int.TryParse(FilterSeasonString, out var year) ? year : null;
set => FilterSeasonString = $"{value}";
}
[ObservableProperty]
private Delivery? _selectedDelivery;
[ObservableProperty]
private IEnumerable<Delivery> _deliveries = [];
[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 string? _lsNr;
[ObservableProperty]
private string? _date;
[ObservableProperty]
private string? _time;
[ObservableProperty]
private Branch? _branch;
[ObservableProperty]
private IEnumerable<Branch> _branchSource = [];
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private string? _sortId;
[ObservableProperty]
private WineVar? _wineVar;
[ObservableProperty]
private IEnumerable<WineVar> _wineVarSource = [];
[ObservableProperty]
private object? _wineAttrObj;
public WineAttr? WineAttr {
get => WineAttrObj as WineAttr;
set => WineAttrObj = value ?? WineAttrSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _wineAttrSource = [];
[ObservableProperty]
private object? _wineCultObj;
public WineCult? WineCult {
get => WineCultObj as WineCult;
set => WineCultObj = value ?? WineCultSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _wineCultSource = [];
[ObservableProperty]
private string? _gradationOeString;
public double? GradationOe => double.TryParse(GradationOeString, out var oe) ? oe : null;
[ObservableProperty]
private string? _gradationKmwString;
public double? GradationKmw => double.TryParse(GradationKmwString, out var kmw) ? kmw : null;
[ObservableProperty]
private WineQualLevel? _wineQualityLevel;
[ObservableProperty]
private IEnumerable<WineQualLevel> _wineQualityLevelSource = [];
[ObservableProperty]
private bool _abgewertet;
[ObservableProperty]
private string? _weightString;
public int? Weight {
get => int.TryParse(WeightString?.Replace(Utils.GroupSeparator, ""), out var w) ? w : null;
set => WeightString = $"{value:N0}";
}
[ObservableProperty]
private bool _isManualWeighing;
[ObservableProperty]
private bool? _isNetWeightValue = false;
public bool IsNetWeight {
get => IsNetWeightValue ?? false;
set => IsNetWeightValue = value;
}
[ObservableProperty]
private WineOrigin? _wineOrigin;
[ObservableProperty]
private IEnumerable<WineOrigin> _wineOriginSource = [];
[ObservableProperty]
private object? _wineKgObj;
public AT_Kg? WineKg {
get => WineKgObj as AT_Kg;
set => WineKgObj = value ?? WineKgSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _wineKgSource = [];
[ObservableProperty]
private object? _wineRdObj;
public WbRd? WineRd {
get => WineRdObj as WbRd;
set => WineRdObj = value ?? WineRdSource.FirstOrDefault();
}
[ObservableProperty]
private IEnumerable<object> _wineRdSource = [];
[ObservableProperty]
private bool? _isGebunden;
public ObservableCollection<Modifier> Modifiers { get; set; } = [];
[ObservableProperty]
private IEnumerable<Modifier> _modifiersSource = [];
[ObservableProperty]
private string? _partComment;
[ObservableProperty]
private string? _temperatureString;
public double? Temperature => double.TryParse(TemperatureString, out var t) ? t : null;
[ObservableProperty]
private string? _acidString;
public double? Acid => double.TryParse(AcidString, out var a) ? a : null;
[ObservableProperty]
private bool _isLesewagen;
[ObservableProperty]
private bool? _isHandPicked;
[ObservableProperty]
private string _statusMembers = "-";
[ObservableProperty]
private string _statusDeliveries = "-";
[ObservableProperty]
private string _statusVarieties = "-";
[ObservableProperty]
private string _statusWeight = "-";
[ObservableProperty]
private string _statusGradation = "-";
[ObservableProperty]
private Grid? _statusWeightToolTip;
[ObservableProperty]
private Grid? _statusGradationToolTip;
}
}

View File

@ -0,0 +1,180 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject {
public int? TransferPredecessorAreaComs = null;
public int? CancelAreaComs = null;
public ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new(Utils.PhoneNrTypes);
[ObservableProperty]
private string? _searchQuery = "";
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
[ObservableProperty]
private bool _showOnlyActiveMembers;
[ObservableProperty]
private Member? _selectedMember;
[ObservableProperty]
private IEnumerable<Member> _members = [];
[ObservableProperty]
private bool _isMemberSelected;
[ObservableProperty]
private bool _memberHasEmail;
[ObservableProperty]
private bool _memberCanSendEmail;
[ObservableProperty]
private bool _enableMemberReferenceButton;
[ObservableProperty]
private bool _enableSearchInputs = true;
[ObservableProperty]
public IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ];
[ObservableProperty]
private bool _memberListOrderByMgNr;
[ObservableProperty]
private bool _memberListOrderByName;
[ObservableProperty]
private bool _memberListOrderByOrt;
[ObservableProperty]
private string? _mgNrString;
public int? MgNr => int.TryParse(MgNrString, out var mgnr) ? mgnr : null;
[ObservableProperty]
private string? _predecessorMgNrString;
public int? PredecessorMgNr => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null;
[ObservableProperty]
private string? _prefix;
[ObservableProperty]
private string? _givenName;
[ObservableProperty]
private string? _familyName;
[ObservableProperty]
private string? _suffix;
[ObservableProperty]
private string? _birthday;
[ObservableProperty]
private string? _age;
[ObservableProperty]
private bool _isDeceased;
[ObservableProperty]
private string? _address;
[ObservableProperty]
private string? _plzString;
public int? Plz => int.TryParse(PlzString, out var plz) ? plz : null;
[ObservableProperty]
private AT_PlzDest? _ort;
[ObservableProperty]
private IEnumerable<AT_PlzDest> _ortSource = [];
[ObservableProperty]
private string? _billingName;
[ObservableProperty]
private string? _billingAddress;
[ObservableProperty]
private string? _billingPlzString;
public int? BillingPlz => int.TryParse(BillingPlzString, out var plz) ? plz : null;
[ObservableProperty]
private AT_PlzDest? _billingOrt;
[ObservableProperty]
private IEnumerable<AT_PlzDest> _billingOrtSource = [];
[ObservableProperty]
private string? _iban;
[ObservableProperty]
private string? _bic;
[ObservableProperty]
private string? _ustIdNr;
[ObservableProperty]
private string? _lfbisNr;
[ObservableProperty]
private bool _isBuchführend;
[ObservableProperty]
private bool _isOrganic;
[ObservableProperty]
private string? _entryDate;
[ObservableProperty]
private string? _exitDate;
[ObservableProperty]
private string? _businessSharesString;
public int? BusinessShares => int.TryParse(BusinessSharesString, out var bs) ? bs : null;
[ObservableProperty]
private string? _accountingNr;
[ObservableProperty]
private bool _isActive;
[ObservableProperty]
private bool _isVollLieferant;
[ObservableProperty]
private bool _isFunktionär;
[ObservableProperty]
private Branch? _branch;
[ObservableProperty]
private IEnumerable<Branch> _branchSource = [];
[ObservableProperty]
private AT_Kg? _defaultKg;
[ObservableProperty]
private IEnumerable<AT_Kg> _defaultKgSource = [];
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private bool _contactViaPost;
[ObservableProperty]
private bool _contactViaEmail;
public ObservableCollection<string?> EmailAddresses { get; private set; } = [null, null, null, null, null, null, null, null, null];
public partial class PhoneNr(int? type = null, string? number = null, string? comment = null) : ObservableObject {
[ObservableProperty]
public int _type = type ?? -1;
[ObservableProperty]
public string? _number = number;
[ObservableProperty]
public string? _comment = comment;
}
public ObservableCollection<PhoneNr> PhoneNrs { get; private set; } = [new(), new(), new(), new(), new(), new(), new(), new(), new()];
[ObservableProperty]
private string _statusMembers = "-";
[ObservableProperty]
private string? _statusMembersToolTip;
[ObservableProperty]
private string _statusBusinessShares = "-";
[ObservableProperty]
private string? _statusBusinessSharesToolTip;
[ObservableProperty]
private string _statusDeliveriesLastSeason = "-";
[ObservableProperty]
private string _statusDeliveriesLastSeasonInfo = "letzte Saison";
[ObservableProperty]
private Grid? _statusDeliveriesLastSeasonToolTip;
[ObservableProperty]
private string _statusDeliveriesThisSeason = "-";
[ObservableProperty]
private string _statusDeliveriesThisSeasonInfo = "aktuelle Saison";
[ObservableProperty]
private Grid? _statusDeliveriesThisSeasonToolTip;
[ObservableProperty]
private string _statusAreaCommitment = "-";
[ObservableProperty]
private Grid? _statusAreaCommitmentToolTip;
[ObservableProperty]
private string _statusAreaCommitmentInfo = "aktuelle Saison";
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
}
}

View File

@ -109,7 +109,7 @@ namespace Elwig.Windows {
private void OnClosing(object? sender, CancelEventArgs evt) {
if ((IsCreating || IsEditing) && HasChanged) {
var r = System.Windows.MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schließen bestätigen",
var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schließen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
evt.Cancel = true;
@ -162,11 +162,11 @@ namespace Elwig.Windows {
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) {
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
ControlUtils.SetInputInvalid(input);
} else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null) {
} else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null && lb.ItemsSource.Cast<object>().Any()) {
ControlUtils.SetInputInvalid(input);
} else if (input is CheckBox ckb && ckb.IsChecked != true) {
} else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is RadioButton rb && rb.IsChecked != true) {
@ -271,16 +271,28 @@ namespace Elwig.Windows {
}
protected void ClearInputs(bool validate = true) {
foreach (var tb in TextBoxInputs)
foreach (var tb in TextBoxInputs) {
tb.Text = "";
foreach (var cb in ComboBoxInputs)
var binding = tb.GetBindingExpression(TextBox.TextProperty);
binding?.UpdateSource();
}
foreach (var cb in ComboBoxInputs) {
cb.SelectedItem = null;
var binding = cb.GetBindingExpression(ComboBox.SelectedItemProperty);
binding?.UpdateSource();
}
foreach (var lb in ListBoxInputs)
lb.SelectedItems.Clear();
foreach (var cb in CheckBoxInputs)
foreach (var cb in CheckBoxInputs) {
cb.IsChecked = cb.IsThreeState ? null : false;
foreach (var rb in RadioButtonInputs)
var binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
binding?.UpdateSource();
}
foreach (var rb in RadioButtonInputs) {
rb.IsChecked = rb.IsThreeState ? null : false;
var binding = rb.GetBindingExpression(RadioButton.IsCheckedProperty);
binding?.UpdateSource();
}
if (validate) ValidateRequiredInputs();
}
@ -327,7 +339,7 @@ namespace Elwig.Windows {
protected async Task UpdatePlz(TextBox plzInput, ComboBox ortInput) {
var plzInputValid = Validator.CheckPlz(plzInput, RequiredInputs.Contains(plzInput)).IsValid;
List<AT_PlzDest>? list = null;
List<AT_PlzDest> list = [];
if (plzInputValid && plzInput.Text.Length == 4) {
var plz = int.Parse(plzInput.Text);
using var ctx = new AppDbContext();
@ -338,7 +350,7 @@ namespace Elwig.Windows {
}
ControlUtils.RenewItemsSource(ortInput, list);
if (list != null && ortInput.SelectedItem == null && list.Count == 1)
if (ortInput.SelectedItem == null && list.Count == 1)
ortInput.SelectedItem = list[0];
UpdateComboBox(ortInput);
}
@ -390,20 +402,24 @@ namespace Elwig.Windows {
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
System.Windows.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;
}
protected void CheckBox_Changed(object sender, RoutedEventArgs evt) {
var input = (CheckBox)sender;
if (SenderIsRequired(input) && input.IsChecked != true) {
if (SenderIsRequired(input) && ((input.IsThreeState && input.IsChecked == null) || (!input.IsThreeState && input.IsChecked != true))) {
ValidateInput(input, false);
ControlUtils.SetInputInvalid(input);
} else if (InputHasChanged(input)) {
ControlUtils.SetInputChanged(input);
} else if (InputIsNotDefault(input)) {
ControlUtils.SetInputNotDefault(input);
} else {
ControlUtils.ClearInputState(input);
ValidateInput(input, true);
if (InputHasChanged(input)) {
ControlUtils.SetInputChanged(input);
} else if (InputIsNotDefault(input)) {
ControlUtils.SetInputNotDefault(input);
} else {
ControlUtils.ClearInputState(input);
}
}
UpdateButtons();
}
@ -411,13 +427,17 @@ namespace Elwig.Windows {
protected void RadioButton_Changed(object sender, RoutedEventArgs evt) {
var input = (RadioButton)sender;
if (SenderIsRequired(input) && input.IsChecked != true) {
ValidateInput(input, false);
ControlUtils.SetInputInvalid(input);
} else if (InputHasChanged(input)) {
ControlUtils.SetInputChanged(input);
} else if (InputIsNotDefault(input)) {
ControlUtils.SetInputNotDefault(input);
} else {
ControlUtils.ClearInputState(input);
ValidateInput(input, true);
if (InputHasChanged(input)) {
ControlUtils.SetInputChanged(input);
} else if (InputIsNotDefault(input)) {
ControlUtils.SetInputNotDefault(input);
} else {
ControlUtils.ClearInputState(input);
}
}
UpdateButtons();
}
@ -444,9 +464,9 @@ namespace Elwig.Windows {
private void UpdateComboBox(Control input) {
bool valid = false;
if (input is ComboBox cb) {
valid = cb.ItemsSource == null || cb.SelectedItem != null || !RequiredInputs.Contains(input);
valid = cb.ItemsSource == null || cb.SelectedItem != null || !RequiredInputs.Contains(input) || !cb.ItemsSource.Cast<object>().Any();
} else if (input is ListBox lb) {
valid = lb.ItemsSource == null || lb.SelectedItem != null || !RequiredInputs.Contains(input);
valid = lb.ItemsSource == null || lb.SelectedItem != null || !RequiredInputs.Contains(input) || !lb.ItemsSource.Cast<object>().Any();
}
if (valid) {
ValidateInput(input, true);
@ -464,115 +484,115 @@ namespace Elwig.Windows {
UpdateButtons();
}
protected void ComboBox_SelectionChanged(object sender, RoutedEventArgs evt) {
protected void ComboBox_SelectionChanged(object sender, RoutedEventArgs? evt) {
UpdateComboBox((Control)sender);
}
protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs? evt) {
// FIXME
InputTextChanged((TextBox)sender, Validator.CheckInteger);
}
protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs? evt) {
// FIXME
InputTextChanged((TextBox)sender, Validator.CheckDecimal);
}
protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckPartialDate);
}
protected void PartialDateInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void PartialDateInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckPartialDate);
}
protected void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void DateInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckDate);
}
protected void DateInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void DateInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckDate);
}
protected void TimeInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void TimeInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckTime);
}
protected void TimeInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void TimeInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckTime);
}
protected async void PlzInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected async void PlzInput_TextChanged(object sender, TextChangedEventArgs? evt) {
var plz = (TextBox)sender;
InputTextChanged(plz, Validator.CheckPlz);
if ("PLZ".Equals(plz.Tag))
await UpdatePlz(plz, GetPlzOrtInput(plz));
}
protected async void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
protected async void PlzInput_LostFocus(object sender, RoutedEventArgs? evt) {
var plz = (TextBox)sender;
InputLostFocus(plz, Validator.CheckPlz);
if ("PLZ".Equals(plz.Tag))
await UpdatePlz(plz, GetPlzOrtInput(plz));
}
protected void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckEmailAddress);
}
protected void EmailAddressInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void EmailAddressInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckEmailAddress);
}
protected void PhoneNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void PhoneNrInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckPhoneNumber);
}
protected void PhoneNrInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void PhoneNrInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckPhoneNumber);
}
protected void IbanInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void IbanInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckIban);
}
protected void IbanInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void IbanInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckIban);
}
protected void BicInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void BicInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckBic);
}
protected void BicInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void BicInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckBic);
}
protected void UstIdNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void UstIdNrInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckUstIdNr);
}
protected void UstIdNrInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void UstIdNrInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckUstIdNr);
}
protected void LfbisNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void LfbisNrInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckLfbisNr);
}
protected void LfbisNrInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void LfbisNrInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckLfbisNr);
}
protected void UpperCaseInput_TextChanged(object sender, TextChangedEventArgs evt) {
protected void UpperCaseInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckUpperCase);
}
protected void UpperCaseInput_LostFocus(object sender, RoutedEventArgs evt) {
protected void UpperCaseInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckUpperCase);
}
protected void AreaComTypeDetailsButton_Click(object sender, RoutedEventArgs evt) {
protected void AreaComTypeDetailsButton_Click(object sender, RoutedEventArgs? evt) {
App.FocusBaseDataAreaComType();
}
}

View File

@ -5,9 +5,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls"
mc:Ignorable="d"
Title="Flächenbindungen - Elwig" Height="500" MinHeight="440" Width="920" MinWidth="860"
xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="{Binding Title}" Height="550" MinHeight="450" Width="920" MinWidth="860"
Loaded="Window_Loaded">
<Window.DataContext>
<vm:AreaComAdminViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
@ -48,6 +51,7 @@
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
@ -57,9 +61,12 @@
<ColumnDefinition Width="*" MinWidth="280"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
</Menu>
<Grid Grid.Row="1" Grid.Column="0" Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="37"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
@ -69,11 +76,25 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,7,145,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged"/>
<CheckBox x:Name="ActiveAreaCommitmentInput" Content="Nur aktive anzeigen"
<TextBox x:Name="SearchInput" Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.ColumnSpan="3" Margin="5,5,190,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
<Bold>Strg+F</Bold><LineBreak/><LineBreak/>
Flächenbindungen 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, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. Kabinett, dac, ... <LineBreak/>
<Bold>Flächenbindung</Bold>: z.B. GVK, GVD, ...
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<CheckBox x:Name="ActiveAreaCommitmentInput" Content="Nur laufende anzeigen (2020)" IsChecked="{Binding ShowOnlyActiveAreaComs}"
Checked="ActiveAreaCommitmentInput_Changed" Unchecked="ActiveAreaCommitmentInput_Changed"
HorizontalAlignment="Right" Margin="0,12,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<DataGrid x:Name="AreaCommitmentList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" SelectionChanged="AreaCommitmentList_SelectionChanged" Grid.Column="0" Grid.Row="1"
@ -90,7 +111,13 @@
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Sorte" Binding="{Binding AreaComType.WineVar.SortId}" Width="70"/>
<DataGridTextColumn Header="Sorte" Binding="{Binding AreaComType.WineVar.SortId}" Width="70">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Attribut" Binding="{Binding AreaComType.WineAttr.Name}" Width="120"/>
</DataGrid.Columns>
</DataGrid>
@ -134,12 +161,12 @@
</Button>
</Grid>
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2" Grid.Row="0">
<Grid Grid.Column="2" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="1.125*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<GroupBox Header="Vertrag" Grid.Column="2" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5">
@ -152,34 +179,40 @@
</Grid.ColumnDefinitions>
<Label Content="FbNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="FbNrInput" Margin="0,10,10,0" Grid.Column="1" TextAlignment="Right"
<TextBox x:Name="FbNrInput" Text="{Binding FbNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" TextAlignment="Right"
TextChanged="FbNrInput_TextChanged" LostFocus="FbNrInput_LostFocus"/>
<Label Content="MgNr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" IsEnabled="False"
<TextBox x:Name="MgNrInput" Text="{Binding MgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="False"
Margin="0,40,10,0" Grid.Column="1" TextAlignment="Right"/>
<Label Content="Von:" Margin="10,10,0,0" Grid.Column="2"/>
<TextBox x:Name="YearFromInput" Margin="0,10,10,0" Grid.Column="3" TextAlignment="Right"
<TextBox x:Name="YearFromInput" Text="{Binding YearFromString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="3" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Label Content="Bis:" Margin="10,40,0,0" Grid.Column="2"/>
<TextBox x:Name="YearToInput" Margin="0,40,10,0" Grid.Column="3" TextAlignment="Right"
<TextBox x:Name="YearToInput" Text="{Binding YearToString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="3" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Label Content="Vertragsart:" Margin="10,70,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<ComboBox x:Name="AreaComTypeInput" DisplayMemberPath="DisplayName" TextSearch.TextPath="DisplayName"
<ComboBox x:Name="AreaComTypeInput" SelectedItem="{Binding AreaComType, Mode=TwoWay}" ItemsSource="{Binding AreaComTypeSource, Mode=TwoWay}"
DisplayMemberPath="DisplayName" TextSearch.TextPath="DisplayName"
HorizontalAlignment="Stretch" Margin="0,70,40,10" Grid.Column="1" Grid.ColumnSpan="3"/>
<Button x:Name="AreaComTypeDetailsButton" Content="&#xE712;" FontFamily="Segoe MDL2 Assets" FontSize="14" Padding="0,1,0,0"
Click="AreaComTypeDetailsButton_Click"
Grid.Column="3" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,70,10,10"/>
<Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
<ComboBox x:Name="WineCultivationInput" SelectedItem="{Binding WineCultObj, Mode=TwoWay}" ItemsSource="{Binding WineCultSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name"
HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label Content="Anmerkung:" Margin="10,130,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<TextBox x:Name="CommentInput" TextChanged="TextBox_TextChanged"
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="TextBox_TextChanged"
HorizontalAlignment="Stretch" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
@ -192,7 +225,8 @@
</Grid.ColumnDefinitions>
<Label Content="KG:" Margin="10,10,0,0" Grid.Column="0"/>
<ComboBox x:Name="KgInput" ItemTemplate="{StaticResource KgNrTemplate}" TextSearch.TextPath="Name"
<ComboBox x:Name="KgInput" SelectedItem="{Binding Kg, Mode=TwoWay}" ItemsSource="{Binding KgSource, Mode=TwoWay}"
ItemTemplate="{StaticResource KgNrTemplate}" TextSearch.TextPath="Name"
HorizontalAlignment="Stretch" Margin="0,10,40,10" Grid.Column="1"
SelectionChanged="KgInput_SelectionChanged"/>
<Button x:Name="KgDetailsButton" Content="&#xE712;" FontFamily="Segoe MDL2 Assets" FontSize="14" Padding="0,1,0,0"
@ -200,7 +234,8 @@
Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,10,10,10"/>
<Label Content="Ried:" Margin="10,40,0,0" Grid.Column="0"/>
<ComboBox x:Name="RdInput" DisplayMemberPath="Name" TextSearch.TextPath="Name" IsEditable="True"
<ComboBox x:Name="RdInput" SelectedItem="{Binding RdObj, Mode=TwoWay}" ItemsSource="{Binding RdSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name" IsEditable="True"
HorizontalAlignment="Stretch" Margin="0,40,40,10" Grid.Column="1"
SelectionChanged="RdInput_SelectionChanged"/>
<Button x:Name="RdAddButton" Content="&#xE73E;" FontFamily="Segoe MDL2 Assets" FontSize="16" Padding="0,0,0,0" IsEnabled="False"
@ -208,11 +243,13 @@
Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,40,10,10"/>
<Label Content="Parzelle(n):" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="GstNrInput" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Stretch"
<TextBox x:Name="GstNrInput" Text="{Binding GstNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Stretch"
TextChanged="TextBox_TextChanged"/>
<Label Content="Fläche:" Margin="10,100,0,0" Grid.Column="0"/>
<ctrl:UnitTextBox x:Name="AreaInput" Unit="m²" TextChanged="IntegerInput_TextChanged"
<ctrl:UnitTextBox x:Name="AreaInput" Unit="m²" Text="{Binding AreaString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="IntegerInput_TextChanged"
Grid.Column="1" Width="70" Margin="0,100,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
@ -223,9 +260,9 @@
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
@ -233,15 +270,21 @@
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock Name="StatusAreaCommitments" Text="Flächenbindungen: -"/>
<TextBlock>
Flächenbindungen: <Run Text="{Binding StatusAreaCommitments}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock Name="StatusArea" Text="Fläche: -"/>
<TextBlock ToolTip="{Binding StatusAreaToolTip}">
Fläche: <Run Text="{Binding StatusArea}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock Name="StatusContracts" Text="Vertragsarten: -"/>
<TextBlock>
Vertragsarten: <Run Text="{Binding StatusContracts}"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>

View File

@ -6,21 +6,23 @@ using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Elwig.ViewModels;
using Elwig.Services;
using System.Windows.Input;
namespace Elwig.Windows {
public partial class AreaComAdminWindow : AdministrationWindow {
public int MgNr => Member.MgNr;
public AreaComAdminViewModel ViewModel => (AreaComAdminViewModel)DataContext;
private readonly Member Member;
private List<string> TextFilter = [];
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(AreaComAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
public AreaComAdminWindow(int mgnr) {
InitializeComponent();
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
using var ctx = new AppDbContext();
Member = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Flächenbindungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ExemptInputs = [
MgNrInput, AreaCommitmentList, NewAreaCommitmentButton,
EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton,
@ -30,6 +32,10 @@ namespace Elwig.Windows {
FbNrInput, YearFromInput, KgInput, RdInput,
GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput
];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged;
ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}");
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
@ -37,21 +43,27 @@ namespace Elwig.Windows {
LockInputs();
}
private async Task RefreshAreaCommitmentList() {
await RefreshAreaCommitmentListQuery();
private void FocusSearchInput(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) {
SearchInput.Focus();
SearchInput.SelectAll();
}
}
private async Task RefreshAreaCommitmentListQuery(bool updateSort = false) {
private async Task RefreshList(bool updateSort = false) {
using var ctx = new AppDbContext();
var (_, areaComQuery, filter) = await GetFilters(ctx);
var (_, areaComQuery, filter) = await ViewModel.GetFilters(ctx);
var areaComs = await areaComQuery
.Include(a => a.Kg.AtKg)
.Include(a => a.Rd!.Kg.AtKg)
.Include(a => a.WineCult)
.Include(a => a.AreaComType.WineAttr)
.Include(a => a.AreaComType.WineVar)
.ToListAsync();
if (filter.Count > 0 && areaComs.Count > 0) {
var dict = areaComs.AsParallel()
.ToDictionary(d => d, d => d.SearchScore(TextFilter))
.ToDictionary(d => d, d => d.SearchScore(ViewModel.TextFilter))
.OrderByDescending(c => c.Value);
var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
areaComs = dict
@ -64,66 +76,21 @@ namespace Elwig.Windows {
AreaCommitmentList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
RefreshInputs();
if(filter.Count == 0) {
StatusAreaCommitments.Text = $"Flächenbindungen: {await areaComQuery.CountAsync()}";
StatusArea.Text = $"Fläche: {await areaComQuery.Select(a => a.Area).SumAsync():N0} m²";
if (filter.Count == 0) {
ViewModel.StatusAreaCommitments = $"{await areaComQuery.CountAsync():N0}";
var s = await ctx.Seasons.FindAsync(await ctx.Seasons.MaxAsync(s => s.Year));
var (text, grid) = await AreaComService.GenerateToolTip(areaComQuery, s?.MaxKgPerHa ?? 10_000);
ViewModel.StatusArea = text;
ViewModel.StatusAreaToolTip = grid;
} else {
StatusAreaCommitments.Text = $"Flächenbindungen: {areaComs.Count}";
StatusArea.Text = $"Fläche: {areaComs.Select(a => a.Area).Sum():N0} m²";
ViewModel.StatusAreaCommitments = $"{areaComs.Count:N0}";
ViewModel.StatusArea = $"{areaComs.Select(a => a.Area).Sum():N0} m²";
ViewModel.StatusAreaToolTip = null;
}
var groups = areaComs.GroupBy(a => $"{a.AreaComType.SortId}{a.AreaComType.AttrId}").Select(a => (a.Key, a.Sum(b => b.Area))).OrderByDescending(a => a.Item2).ToList();
StatusContracts.Text = $"Vertragsarten: {groups.Count} (" + string.Join(", ", groups.Select(g => $"{g.Key}: {g.Item2:N0} m²")) + ")";
groups = areaComs.GroupBy(a => a.AreaComType.DisplayName).Select(a => (a.Key, a.Sum(b => b.Area))).OrderByDescending(a => a.Item2).ToList();
StatusContracts.ToolTip = $"Vertragsarten: {groups.Count}\n" + string.Join($"\n", groups.Select(g => $"{g.Key}: {g.Item2:N0} m²"));
}
private async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters(AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<AreaCom> areaComQuery = ctx.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr);
if (ActiveAreaCommitmentInput.IsChecked == true) {
areaComQuery = Utils.ActiveAreaCommitments(areaComQuery);
filterNames.Add("aktiv");
}
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterAttr = new List<string>();
var filterNotAttr = new List<string>();
var filter = TextFilter.ToList();
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var attr = await ctx.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
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--);
} else if (attr.ContainsKey(e.ToLower())) {
var a = attr[e.ToLower()];
filterAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"Attribut {a.Name}");
} else if (e[0] == '!' && attr.ContainsKey(e[1..].ToLower())) {
var a = attr[e[1..].ToLower()];
filterNotAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Attribut {a.Name}");
}
}
if (filterVar.Count > 0) areaComQuery = areaComQuery.Where(a => filterVar.Contains(a.AreaComType.WineVar.SortId));
if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId));
if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr != null && a.AreaComType.WineAttr.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId));
if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr == null || a.AreaComType.WineAttr.AttrId == null || !filterAttr.Contains(a.AreaComType.WineAttr.AttrId));
}
return (filterNames, areaComQuery, filter);
ViewModel.StatusContracts = $"{groups.Count:N0}";
if (groups.Count > 0)
ViewModel.StatusContracts += $" ({string.Join(", ", groups.Select(g => g.Key))})";
}
private void RefreshInputs(bool validate = false) {
@ -146,50 +113,28 @@ namespace Elwig.Windows {
private void FillInputs(AreaCom a) {
ClearOriginalValues();
ClearDefaultValues();
FbNrInput.Text = a.FbNr.ToString();
MgNrInput.Text = a.MgNr.ToString();
YearFromInput.Text = a.YearFrom.ToString();
YearToInput.Text = a.YearTo.ToString();
ControlUtils.SelectItemWithPk(KgInput, a.KgNr);
if (a.RdNr != null) {
ControlUtils.SelectItemWithPk(RdInput, a.KgNr, a.RdNr);
} else {
RdInput.SelectedIndex = 0;
}
GstNrInput.Text = a.GstNr;
AreaInput.Text = a.Area.ToString();
ControlUtils.SelectItemWithPk(AreaComTypeInput, a.VtrgId);
if (a.CultId != null) {
ControlUtils.SelectItemWithPk(WineCultivationInput, a.CultId);
} else {
WineCultivationInput.SelectedIndex = 0;
}
CommentInput.Text = a.Comment;
ViewModel.FillInputs(a);
FinishInputFilling();
}
private async void InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
using (var ctx = new AppDbContext()) {
FbNrInput.Text = (await ctx.NextFbNr()).ToString();
MgNrInput.Text = Member.MgNr.ToString();
YearFromInput.Text = DateTime.Now.Year.ToString();
WineCultivationInput.SelectedIndex = 0;
}
await ViewModel.InitInputs();
SetDefaultValue(FbNrInput);
ValidateRequiredInputs();
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
if (await ctx.Members.FindAsync(ViewModel.FilterMember.MgNr) is not Member m) {
Close();
return;
}
ViewModel.FilterMember = m;
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ControlUtils.RenewItemsSource(KgInput, await ctx.WbKgs
.Include(k => k.AtKg.WbKg!.Rds)
.Select(k => k.AtKg)
@ -205,7 +150,7 @@ namespace Elwig.Windows {
.Cast<object>().ToListAsync();
cultList.Insert(0, new NullItem());
ControlUtils.RenewItemsSource(WineCultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
await RefreshAreaCommitmentList();
await RefreshList();
}
protected override void ShortcutNew() {
@ -262,53 +207,10 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
ctx.Remove(a);
await ctx.SaveChangesAsync();
await RefreshAreaCommitmentList();
await RefreshList();
}
}
private async Task<int> UpdateAreaCom(int? oldFbNr) {
using var ctx = new AppDbContext();
int newFbNr = int.Parse(FbNrInput.Text);
var a = new AreaCom {
FbNr = oldFbNr ?? newFbNr,
MgNr = int.Parse(MgNrInput.Text),
YearFrom = int.Parse(YearFromInput.Text),
YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text),
KgNr = ((AT_Kg)KgInput.SelectedItem).KgNr,
RdNr = (RdInput.SelectedItem as WbRd)?.RdNr,
GstNr = GstNrInput.Text.Trim(),
Area = int.Parse(AreaInput.Text),
VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)!.VtrgId,
CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId,
Comment = (CommentInput.Text == "") ? null : CommentInput.Text.Trim(),
};
if (RdInput.SelectedItem is WbRd rd) {
if (rd.RdNr == 0) {
rd.RdNr = await ctx.NextRdNr(a.KgNr);
a.RdNr = rd.RdNr;
ctx.Add(rd);
}
}
if (oldFbNr != null) {
ctx.Update(a);
} else {
ctx.Add(a);
}
await ctx.SaveChangesAsync();
if (newFbNr != a.FbNr) {
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {oldFbNr}");
}
await App.HintContextChange();
return newFbNr;
}
protected override void ShortcutSave() {
if (!AreaCommitmentSaveButton.IsEnabled || AreaCommitmentSaveButton.Visibility != Visibility.Visible)
return;
@ -318,7 +220,7 @@ namespace Elwig.Windows {
private async void AreaCommitmentSaveButton_Click(object? sender, RoutedEventArgs? evt) {
int? fbnr = null;
try {
fbnr = await UpdateAreaCom((AreaCommitmentList.SelectedItem as AreaCom)?.FbNr);
fbnr = await ViewModel.UpdateAreaCommitment((AreaCommitmentList.SelectedItem as AreaCom)?.FbNr);
} 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;
@ -332,9 +234,9 @@ namespace Elwig.Windows {
LockInputs();
UnlockSearchInputs();
FinishInputFilling();
await RefreshAreaCommitmentList();
await RefreshList();
RefreshInputs();
SearchInput.Text = "";
ViewModel.SearchQuery = "";
ControlUtils.SelectItem(AreaCommitmentList, AreaCommitmentList.ItemsSource.Cast<AreaCom>().Where(a => a.FbNr == fbnr).FirstOrDefault());
}
@ -435,12 +337,13 @@ namespace Elwig.Windows {
}
private async void ActiveAreaCommitmentInput_Changed(object sender, RoutedEventArgs evt) {
await RefreshAreaCommitmentListQuery();
await RefreshList();
}
private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0);
await RefreshAreaCommitmentListQuery(true);
var binding = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
binding?.UpdateSource();
await RefreshList(true);
}
private void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using Elwig.Models.Entities;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using LinqKit;
using Microsoft.EntityFrameworkCore;

View File

@ -1,4 +1,4 @@
using Elwig.Dialogs;
using Elwig.Dialogs;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
<local:ContextWindow
<local:ContextWindow
x:Class="Elwig.Windows.ChartWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@ -4,9 +4,13 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Lieferungen - Elwig" Height="720" Width="1100" MinHeight="720" MinWidth="1000"
Title="{Binding Title}" Height="720" Width="1100" MinHeight="720" MinWidth="1000"
Loaded="Window_Loaded">
<Window.DataContext>
<vm:DeliveryAdminViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
@ -125,10 +129,10 @@
<MenuItem x:Name="Menu_Export_UploadFilters" Header="...aus Filtern hochladen"
Click="Menu_Export_UploadFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_Export_ExportToday" Header="...von heute speichern..."
Click="Menu_Export_ExportToday_Click"/>
<MenuItem x:Name="Menu_Export_UploadToday" Header="...von heute hochladen"
Click="Menu_Export_UploadToday_Click" InputGestureText="Strg+H"/>
<MenuItem x:Name="Menu_Export_ExportSeason" Header="...von Saison/Zweigstelle speichern..."
Click="Menu_Export_ExportSeason_Click"/>
<MenuItem x:Name="Menu_Export_UploadSeason" Header="...von Saison/Zweigstelle hochladen"
Click="Menu_Export_UploadSeason_Click"/>
</MenuItem>
<MenuItem Header="Einstellungen">
<MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
@ -148,7 +152,8 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False"
<TextBox x:Name="SearchInput" Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.ColumnSpan="3" Margin="5,10,156,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
@ -171,18 +176,19 @@
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
<Bold>Brutto/Netto Wiegung</Bold>: bto, Brut[to], nto, Net[to], gerebelt, !Gerebelt<LineBreak/>
<Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<ctrl:IntegerUpDown x:Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1900" Maximum="9999"
Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right" Text="2020"
<ctrl:IntegerUpDown x:Name="SeasonInput" Text="{Binding FilterSeasonString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1900" Maximum="9999"
Margin="0,10,95,0" VerticalAlignment="Top" HorizontalAlignment="Right"
TextChanged="SeasonInput_TextChanged"/>
<CheckBox x:Name="TodayOnlyInput" Content="Nur heute"
<CheckBox x:Name="TodayOnlyInput" Content="Nur heute" IsChecked="{Binding FilterTodayOnly, Mode=TwoWay}"
HorizontalAlignment="Right" Margin="0,7,18,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"
Checked="TodayOnlyInput_Changed" Unchecked="TodayOnlyInput_Changed"/>
<CheckBox x:Name="AllSeasonsInput" Content="Jede Saison" IsEnabled="False"
<CheckBox x:Name="AllSeasonsInput" Content="Jede Saison" IsEnabled="{Binding EnableAllSeasons, Mode=TwoWay}" IsChecked="{Binding FilterAllSeasons, Mode=TwoWay}"
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"
@ -325,9 +331,11 @@
</Grid.ColumnDefinitions>
<Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right"
<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" Grid.Column="1" Margin="53,10,40,10" IsEditable="True"
<ComboBox x:Name="MemberInput" SelectedItem="{Binding Member, Mode=TwoWay}" ItemsSource="{Binding MemberSource, Mode=TwoWay}"
Grid.Column="1" Margin="53,10,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Button x:Name="MemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
@ -335,7 +343,8 @@
Click="MemberReferenceButton_Click"/>
<Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/>
<TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22"
<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>
@ -348,26 +357,31 @@
</Grid.ColumnDefinitions>
<Label Content="LieferscheinNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="LsNrInput" Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0"
<TextBox x:Name="LsNrInput" Text="{Binding LsNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0"
IsReadOnly="True" IsTabStop="False"
TextChanged="TextBox_TextChanged"/>
<Label Content="Datum/Uhrzeit:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="DateInput" Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
<TextBox x:Name="DateInput" Text="{Binding Date, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
IsReadOnly="True"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<TextBox x:Name="TimeInput" Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
<TextBox x:Name="TimeInput" Text="{Binding Time, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
IsReadOnly="True"
TextChanged="TimeInput_TextChanged" LostFocus="TimeInput_LostFocus"/>
<Label Content="Zweigstelle:" Margin="10,70,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
<ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}"
Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
IsEnabled="False"
SelectionChanged="BranchInput_SelectionChanged"
DisplayMemberPath="Name" TextSearch.TextPath="Name"/>
<Label Content="Anmerkung:" Margin="10,100,0,10"/>
<TextBox x:Name="CommentInput" Grid.Column="1" Margin="0,100,10,10"
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Margin="0,100,10,10"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
@ -381,9 +395,11 @@
</Grid.ColumnDefinitions>
<Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
<TextBox x:Name="SortIdInput" Text="{Binding SortId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="41,10,10,10"
<ComboBox x:Name="WineVarietyInput" SelectedItem="{Binding WineVar, Mode=TwoWay}" ItemsSource="{Binding WineVarSource, Mode=TwoWay}"
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="41,10,10,10"
TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp">
<ComboBox.ItemTemplateSelector>
@ -392,12 +408,14 @@
</ComboBox>
<Label Content="Attr./Bewirt.:" Margin="10,40,0,0" Grid.Column="0"/>
<ComboBox x:Name="AttributeInput" Grid.Row="1" Grid.Column="1" Margin="0,40,5,10"
<ComboBox x:Name="AttributeInput" SelectedItem="{Binding WineAttrObj, Mode=TwoWay}" ItemsSource="{Binding WineAttrSource, Mode=TwoWay}"
Grid.Row="1" Grid.Column="1" Margin="0,40,5,10"
DisplayMemberPath="Name"
SelectionChanged="AttributeInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="CultivationInput" Grid.Row="1" Grid.Column="2" Margin="0,40,10,10"
KeyUp="Input_KeyUp"/>
<ComboBox x:Name="CultivationInput" SelectedItem="{Binding WineCultObj, Mode=TwoWay}" ItemsSource="{Binding WineCultSource, Mode=TwoWay}"
Grid.Row="1" Grid.Column="2" Margin="0,40,10,10"
DisplayMemberPath="Name"
SelectionChanged="CultivationInput_SelectionChanged" KeyUp="Input_KeyUp"/>
KeyUp="Input_KeyUp"/>
</Grid>
</GroupBox>
@ -408,15 +426,18 @@
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Gradation:" Margin="10,10,10,10"/>
<ctrl:UnitTextBox x:Name="GradationOeInput" Unit="°Oe" TextChanged="GradationOeInput_TextChanged" KeyUp="Input_KeyUp"
<ctrl:UnitTextBox x:Name="GradationOeInput" Unit="°Oe" Text="{Binding GradationOeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="GradationOeInput_TextChanged" KeyUp="Input_KeyUp"
Grid.Column="1" Width="54" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="GradationKmwInput" Unit="°KMW" TextChanged="GradationKmwInput_TextChanged" KeyUp="Input_KeyUp"
<ctrl:UnitTextBox x:Name="GradationKmwInput" Unit="°KMW" Text="{Binding GradationKmwString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="GradationKmwInput_TextChanged" KeyUp="Input_KeyUp"
Grid.Column="1" Width="68" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
<ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
<ComboBox x:Name="WineQualityLevelInput" SelectedItem="{Binding WineQualityLevel, Mode=TwoWay}" ItemsSource="{Binding WineQualityLevelSource, Mode=TwoWay}"
Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
TextSearch.TextPath="Name"
SelectionChanged="WineQualityLevelInput_SelectionChanged" KeyUp="Input_KeyUp">
<ComboBox.ItemTemplateSelector>
@ -424,7 +445,8 @@
</ComboBox.ItemTemplateSelector>
</ComboBox>
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
<CheckBox x:Name="AbgewertetInput" IsChecked="{Binding Abgewertet, Mode=TwoWay}"
Content="Abgewertet" IsEnabled="False"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
</Grid>
</GroupBox>
@ -437,16 +459,19 @@
</Grid.ColumnDefinitions>
<Label Content="Gewicht:" Margin="10,10,10,10"/>
<ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" TextChanged="WeightInput_TextChanged"
<ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="WeightInput_TextChanged"
Grid.Column="1" Width="70" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<CheckBox x:Name="ManualWeighingInput" Content="Handwiegung" IsEnabled="False"
<CheckBox x:Name="ManualWeighingInput" IsChecked="{Binding IsManualWeighing, Mode=TwoWay}"
Content="Handwiegung" IsEnabled="False"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="GerebeltGewogenInput" Content="Netto (gerebelt gewogen)"
<CheckBox x:Name="GerebeltGewogenInput" IsChecked="{Binding IsNetWeightValue, Mode=TwoWay}"
Content="Gerebelt gewogen" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed"/>
Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed" Indeterminate="GerebeltGewogenInput_Changed"/>
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
Click="WeighingButton_Click"
@ -475,30 +500,34 @@
</Grid.ColumnDefinitions>
<Label Content="Zu-/Abschläge:" Margin="10,10,0,10"/>
<ctrl:CheckComboBox x:Name="ModifiersInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
<ctrl:CheckComboBox x:Name="ModifiersInput" SelectedItems="{Binding Modifiers}" ItemsSource="{Binding ModifiersSource, Mode=TwoWay}"
Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
ItemTemplate="{StaticResource ModifierTemplate}" Delimiter=", " AllItemsSelectedContent="Alle"
SelectionChanged="ModifiersInput_SelectionChanged"/>
<Label Content="Anmerkung:" Margin="10,40,0,10"/>
<TextBox x:Name="PartCommentInput" Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"
<TextBox x:Name="PartCommentInput" Text="{Binding PartComment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="Temperatur:" Margin="10,70,0,10"/>
<ctrl:UnitTextBox x:Name="TemperatureInput" Unit="°C"
<ctrl:UnitTextBox x:Name="TemperatureInput" Unit="°C" Text="{Binding TemperatureString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"
Grid.Column="1" Margin="0,70,10,10" VerticalAlignment="Top"/>
<Label Content="Säure:" Margin="10,100,0,10"/>
<ctrl:UnitTextBox x:Name="AcidInput" Unit="g/l"
<ctrl:UnitTextBox x:Name="AcidInput" Unit="g/l" Text="{Binding AcidString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"
Grid.Column="1" Margin="0,100,10,10" VerticalAlignment="Top"/>
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
<CheckBox x:Name="LesewagenInput" IsChecked="{Binding IsLesewagen, Mode=TwoWay}"
Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed" Click="HandPickedInput_Changed"/>
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/>
<CheckBox x:Name="HandPickedInput" IsChecked="{Binding IsHandPicked, Mode=TwoWay}"
Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed" Click="HandPickedInput_Changed"/>
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed" Indeterminate="HandPickedInput_Changed"/>
</Grid>
</GroupBox>
@ -543,7 +572,8 @@
</Grid.ColumnDefinitions>
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
<ComboBox x:Name="WineOriginInput" SelectedItem="{Binding WineOrigin, Mode=TwoWay}" ItemsSource="{Binding WineOriginSource, Mode=TwoWay}"
Margin="0,10,10,10" Grid.Column="1"
TextSearch.TextPath="Name">
<ComboBox.ItemTemplateSelector>
<ctrl:WineOriginTemplateSelector/>
@ -551,17 +581,20 @@
</ComboBox>
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
<ComboBox x:Name="WineKgInput" SelectedItem="{Binding WineKgObj, Mode=TwoWay}" ItemsSource="{Binding WineKgSource, Mode=TwoWay}"
Margin="0,40,10,10" Grid.Column="1"
DisplayMemberPath="Name"
SelectionChanged="WineKgInput_SelectionChanged"/>
<Label Content="Ried:" Margin="10,70,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineRdInput" Margin="0,70,10,10" Grid.Column="1"
<ComboBox x:Name="WineRdInput" SelectedItem="{Binding WineRdObj, Mode=TwoWay}" ItemsSource="{Binding WineRdSource, Mode=TwoWay}"
Margin="0,70,10,10" Grid.Column="1"
DisplayMemberPath="Name"/>
<CheckBox x:Name="GebundenInput" Content="Gebunden" Margin="10,105,0,0" Grid.Column="0" Grid.ColumnSpan="2" IsThreeState="True"
<CheckBox x:Name="GebundenInput" IsChecked="{Binding IsGebunden, Mode=TwoWay}"
Content="Gebunden" Margin="10,105,0,0" Grid.Column="0" Grid.ColumnSpan="2" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Indeterminate="CheckBox_Changed"/>
</Grid>
</GroupBox>
</Grid>
@ -571,11 +604,11 @@
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*" MaxWidth="250"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*" MaxWidth="180"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
@ -585,46 +618,32 @@
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock x:Name="StatusMembers" Text="Mitglieder: -"/>
<TextBlock ToolTip="{Binding StatusMembers}">
Mitglieder: <Run Text="{Binding StatusMembers}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock x:Name="StatusDeliveries" Text="Lieferungen: -"/>
<TextBlock>
Lieferungen: <Run Text="{Binding StatusDeliveries}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock x:Name="StatusVarieties" Text="Sorten: -"/>
<TextBlock ToolTip="{Binding StatusVarieties}">
Sorten: <Run Text="{Binding StatusVarieties}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6">
<TextBlock x:Name="StatusWeight" Text="Gewicht: -">
<TextBlock.ToolTip>
<Grid x:Name="StatusWeightToolTip">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
</Grid>
</TextBlock.ToolTip>
<TextBlock ToolTip="{Binding StatusWeightToolTip}">
Gewicht: <Run Text="{Binding StatusWeight}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="7"/>
<StatusBarItem Grid.Column="8">
<TextBlock x:Name="StatusGradation" Text="Gradation: -">
<TextBlock.ToolTip>
<Grid x:Name="StatusGradationToolTip">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="35"/>
</Grid.ColumnDefinitions>
</Grid>
</TextBlock.ToolTip>
<TextBlock ToolTip="{Binding StatusGradationToolTip}">
Gradation: <Run Text="{Binding StatusGradation}"/>
</TextBlock>
</StatusBarItem>
</StatusBar>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
<local:ContextWindow
<local:ContextWindow
x:Class="Elwig.Windows.MailWindow"
AutomationProperties.AutomationId="MailWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

View File

@ -1,4 +1,4 @@
using Elwig.Documents;
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
@ -103,7 +103,7 @@ namespace Elwig.Windows {
public MailWindow(int? year = null) {
InitializeComponent();
using (var ctx = new AppDbContext()) {
Year = year ?? ctx.Seasons.OrderBy(s => s.Year).LastOrDefault()!.Year;
Year = year ?? ctx.Seasons.OrderBy(s => s.Year).LastOrDefault()?.Year ?? Utils.Today.Year;
Title = $"Rundschreiben - Lese {Year} - Elwig";
}

View File

@ -19,9 +19,8 @@
<Grid>
<Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White">
<MenuItem Header="Datenbank">
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click"/>
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False"/>
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click"/>
<MenuItem x:Name="Menu_Database_Sync" Header="Daten synchronisieren" Click="Menu_Database_Sync_Click"/>
<Separator/>
<MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click"/>
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click"/>
@ -30,7 +29,9 @@
<MenuItem Header="Über"/>
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click"/>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click"/>
<MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Config_Click"/>
<Separator/>
<MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_Click"/>
<MenuItem x:Name="Menu_Help_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Directory_Click"/>
</MenuItem>
</Menu>
@ -66,6 +67,15 @@
<Button x:Name="RegistrationButton" Content="Anmeldungen" IsEnabled="False"
Margin="205,250,0,0"/>
<Button x:Name="DownloadButton" Click="DownloadButton_Click"
Margin="310,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30"
Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16"
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
<Button x:Name="UploadButton" Click="UploadButton_Click"
Margin="375,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30"
Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16"
ToolTip="Lieferungen dieser Zweigstelle hochladen"/>
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
Expanded="SeasonFinish_Expanded" Collapsed="SeasonFinish_Collapsed"
HorizontalAlignment="Center" Width="407" Margin="0,290,0,0" VerticalAlignment="Top">

View File

@ -11,6 +11,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
@ -27,7 +28,8 @@ namespace Elwig.Windows {
if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
Menu_Database_Sync.IsEnabled = App.Config.SyncUrl != null;
DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
@ -62,8 +64,21 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null;
}
private void Menu_Help_Directory_Click(object sender, RoutedEventArgs evt) {
try {
Process.Start("explorer.exe", App.DataPath);
} catch { }
}
private void Menu_Help_Config_Click(object sender, RoutedEventArgs evt) {
Process.Start("explorer.exe", App.DataPath);
try {
Process.Start(new ProcessStartInfo {
FileName = "notepad.exe",
Arguments = App.ConfigPath,
Verb = "runas",
UseShellExecute = true,
});
} catch { }
}
private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) {
@ -80,11 +95,25 @@ namespace Elwig.Windows {
// TODO Menu_Database_Export_Click
}
private void Menu_Database_Import_Click(object sender, RoutedEventArgs evt) {
// TODO Menu_Database_Import_Click
private async void Menu_Database_Import_Click(object sender, RoutedEventArgs evt) {
try {
var d = new OpenFileDialog() {
Title = "Export-Datei auswählen - Elwig",
DefaultExt = ".elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Multiselect = true,
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
await ElwigData.Import(d.FileNames, ElwigData.ImportMode.Interactively);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private async void Menu_Database_Sync_Click(object sender, RoutedEventArgs evt) {
private async void DownloadButton_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
@ -94,20 +123,17 @@ namespace Elwig.Windows {
.Select(f => new {
Name = f!["name"]!.AsValue().GetValue<string>(),
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
ZwstId = f!["zwstid"]?.AsValue().GetValue<string>(),
DeliveryNum = f!["meta"]?["deliveries"]?["count"]!.AsValue().GetValue<int>(),
DeliveryPartNum = f!["meta"]?["deliveries"]?["parts"]!.AsValue().GetValue<int>(),
Filters = f!["meta"]?["deliveries"]?["filters"]!.AsArray().Select(i => i!.AsValue().GetValue<string>()).ToArray(),
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
Device = f!["meta"]?["device"]!.AsValue().GetValue<string>(),
Url = f!["url"]!.AsValue().GetValue<string>(),
Size = f!["size"]!.AsValue().GetValue<long>(),
})
.Where(f => f.DeliveryNum > 0 && f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
.Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
.ToList();
var imported = await ElwigData.GetImportedFiles();
var import = files
.Where(f => f.Filters != null && f.Filters.Length > 0 && f.Device != Environment.MachineName && !imported.Contains(f.Name))
.Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
.ToList();
var paths = new List<string>();
using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) {
@ -118,13 +144,48 @@ namespace Elwig.Windows {
paths.Add(filename);
}
}
await ElwigData.Import(paths, false);
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 (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private async void UploadButton_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
using var ctx = new AppDbContext();
var deliveries = await ctx.Deliveries
.Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
.Include(d => d.Parts)
.ThenInclude(p => p.PartModifiers)
.OrderBy(d => d.DateString)
.ThenBy(d => d.TimeString)
.ThenBy(d => d.LsNr)
.AsSplitQuery()
.ToListAsync();
if (deliveries.Count == 0) {
MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
} else {
await ElwigData.Export(path, deliveries, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
MessageBox.Show($"Hochladen von {deliveries.Count} Lieferungen erfolgreich!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) {
var w = new MemberAdminWindow();
w.Show();

View File

@ -4,8 +4,12 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150"
Loaded="Window_Loaded">
<Window.DataContext>
<vm:MemberAdminViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
@ -47,19 +51,19 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Kontaktieren">
<MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="False"
<MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="{Binding MemberHasEmail}"
Click="Menu_Contact_Email_Click"/>
<MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="False"
<MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_Contact_Letterhead_Click"/>
</MenuItem>
<MenuItem Header="Stammdatenblatt">
<MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="False"
<MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Show_Click" InputGestureText="Strg+P"/>
<MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="False"
<MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_SavePdf_Click"/>
<MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="False"
<MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Print_Click" InputGestureText="Strg+Shift+P"/>
<MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="False"
<MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="{Binding MemberCanSendEmail}"
Click="Menu_MemberDataSheet_Email_Click"/>
</MenuItem>
<MenuItem Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation">
@ -110,18 +114,32 @@
<MenuItem x:Name="Menu_List_PrintAll" Header="...mit allen drucken"
Click="Menu_List_PrintAll_Click"/>
<Separator/>
<MenuItem x:Name="Menu_List_OrderMgNr" Header="...nach MgNr. sortieren" IsCheckable="True" IsChecked="True"
<MenuItem x:Name="Menu_List_OrderMgNr" Header="...nach MgNr. sortieren"
IsCheckable="True" IsChecked="{Binding MemberListOrderByMgNr, Mode=TwoWay}"
Click="Menu_List_Order_Click"/>
<MenuItem x:Name="Menu_List_OrderName" Header="...nach Nachname, Vorname sortieren" IsCheckable="True"
<MenuItem x:Name="Menu_List_OrderName" Header="...nach Nachname, Vorname sortieren"
IsCheckable="True" IsChecked="{Binding MemberListOrderByName, Mode=TwoWay}"
Click="Menu_List_Order_Click"/>
<MenuItem x:Name="Menu_List_OrderOrt" Header="...nach Stamm-KG, Name sortieren" IsCheckable="True"
<MenuItem x:Name="Menu_List_OrderOrt" Header="...nach Stamm-KG, Name sortieren"
IsCheckable="True" IsChecked="{Binding MemberListOrderByOrt, Mode=TwoWay}"
Click="Menu_List_Order_Click"/>
</MenuItem>
<MenuItem Header="Export">
<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"
Click="Menu_Export_UploadFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_Export_ExportAll" Header="...von allen Mitgliedern speichern..."
Click="Menu_Export_ExportAll_Click"/>
<MenuItem x:Name="Menu_Export_UploadAll" Header="...von allen Mitgliedern hochladen"
Click="Menu_Export_UploadAll_Click"/>
</MenuItem>
</Menu>
<Grid Grid.Row="1" Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="39"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
@ -131,7 +149,8 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,7,145,0" IsReadOnly="False"
<TextBox x:Name="SearchInput" Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding EnableSearchInputs}"
Grid.ColumnSpan="3" Margin="5,5,140,0"
TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
@ -151,15 +170,19 @@
<Bold>Volllieferant</Bold>: voll[lieferant], !Voll[lieferant] (nicht-Volllieferant)<LineBreak/>
<Bold>Funktionär</Bold>: Funkt[ionär], !funkt[ionär] (nicht-Funktionär)<LineBreak/>
<Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/>
<Bold>Kontaktdaten</Bold>: email (mind. 1 E-Mail-Adr.), telnr (mind. 1 Tel.-Nr.), !email (keine E-Mail-Adr.), !telnr (keine Tel.-Nr.)<LineBreak/>
<Bold>Kontaktart</Bold>: kontakt:email, kontakt:post, !kontakt:email, !kontakt:post<LineBreak/>
<Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen")
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<CheckBox x:Name="ActiveMemberInput" Content="Nur aktive anzeigen"
IsChecked="{Binding ShowOnlyActiveMembers, Mode=TwoWay}" IsEnabled="{Binding EnableSearchInputs}"
Checked="ActiveMemberInput_Changed" Unchecked="ActiveMemberInput_Changed"
HorizontalAlignment="Right" Margin="0,12,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<DataGrid x:Name="MemberList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
ItemsSource="{Binding Members, Mode=TwoWay}" SelectedItem="{Binding SelectedMember, Mode=TwoWay}"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="MemberList_SelectionChanged"
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
@ -184,21 +207,21 @@
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewMemberButton" Content="Neu"
<Button x:Name="NewMemberButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="NewMemberButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditMemberButton" Content="Bearbeiten" IsEnabled="False"
<Button x:Name="EditMemberButton" 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="EditMemberButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteMemberButton" Content="Löschen" IsEnabled="False"
<Button x:Name="DeleteMemberButton" 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="DeleteMemberButton_Click">
<Button.ToolTip>
@ -206,21 +229,21 @@
</Button.ToolTip>
</Button>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden"
<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="Hidden"
<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="Hidden" IsCancel="True"
<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>
@ -254,51 +277,62 @@
</Grid.ColumnDefinitions>
<Label Content="MgNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
<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"/>
<Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/>
<TextBox x:Name="PredecessorMgNrInput" Margin="0,10,10,0" Width="48" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
<TextBox x:Name="PredecessorMgNrInput" Text="{Binding PredecessorMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Width="48" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="PredecessorMgNrInput_TextChanged" LostFocus="PredecessorMgNrInput_LostFocus"/>
<Button x:Name="MemberReferenceButton" Grid.Column="3" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="53,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" IsEnabled="False" ToolTip="Zu Vorgänger springen"
Margin="53,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" IsEnabled="{Binding EnableMemberReferenceButton}" ToolTip="Zu Vorgänger springen"
Click="MemberReferenceButton_Click"/>
<Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/>
<TextBox x:Name="PrefixInput" Margin="0,40,10,0" Grid.Column="3"
<TextBox x:Name="PrefixInput" Text="{Binding Prefix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
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" Margin="0,40,0,0" Grid.Column="1"
<TextBox x:Name="GivenNameInput" Text="{Binding GivenName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
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" Margin="0,70,0,0" Grid.Column="1"
<TextBox x:Name="FamilyNameInput" Text="{Binding FamilyName, 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" Margin="0,70,10,0" Grid.Column="3"
<TextBox x:Name="SuffixInput" Text="{Binding Suffix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="BirthdayInput" Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left"
<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"/>
<Label Content="Alter:" Margin="85,100,0,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<TextBlock x:Name="Age" Text="-" Margin="119,104,0,0" Grid.Column="1" Grid.ColumnSpan="3" TextWrapping="NoWrap" VerticalAlignment="Top"/>
<TextBlock x:Name="Age" Text="{Binding Age, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="119,104,0,0" Grid.Column="1" Grid.ColumnSpan="3" TextWrapping="NoWrap" VerticalAlignment="Top"/>
<CheckBox x:Name="DeceasedInput" Content="Verstorben" IsEnabled="False"
<CheckBox x:Name="DeceasedInput" Content="Verstorben" IsChecked="{Binding IsDeceased, Mode=TwoWay}"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="3" HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top" IsChecked="False"/>
Grid.Column="3" HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top"/>
<Label Content="Adresse:" Margin="10,130,0,0"/>
<TextBox x:Name="AddressInput" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"
<TextBox x:Name="AddressInput" Text="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"
TextChanged="TextBox_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Text="{Binding PlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="OrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
<ComboBox x:Name="OrtInput" SelectedItem="{Binding Ort, Mode=TwoWay}" ItemsSource="{Binding OrtSource, Mode=TwoWay}"
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
@ -311,93 +345,138 @@
</Grid.ColumnDefinitions>
<Label x:Name="EmailAddress1Label" Content="E-Mail-Adresse:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress1Input" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress1Input" Text="{Binding EmailAddresses[0], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress2Label" Content="E-Mail-Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress2Input" Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress2Input" Text="{Binding EmailAddresses[1], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress3Label" Content="E-Mail-Adresse:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress3Input" Margin="0,70,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress3Input" Text="{Binding EmailAddresses[2], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress4Label" Content="E-Mail-Adresse:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress4Input" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress4Input" Text="{Binding EmailAddresses[3], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress5Label" Content="E-Mail-Adresse:" Margin="10,130,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress5Input" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress5Input" Text="{Binding EmailAddresses[4], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress6Label" Content="E-Mail-Adresse:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress6Input" Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress6Input" Text="{Binding EmailAddresses[5], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress7Label" Content="E-Mail-Adresse:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress7Input" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress7Input" Text="{Binding EmailAddresses[6], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress8Label" Content="E-Mail-Adresse:" Margin="10,220,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress8Input" Margin="0,220,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress8Input" Text="{Binding EmailAddresses[7], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,220,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress9Label" Content="E-Mail-Adresse:" Margin="10,250,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress9Input" Margin="0,250,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="EmailAddress9Input" Text="{Binding EmailAddresses[8], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,250,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[0].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr1Input" Text="{Binding PhoneNrs[0].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr1CommentInput" Margin="0,70,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr1CommentInput" Text="{Binding PhoneNrs[0].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value" Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr2Input" Margin="0,100,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[1].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr2Input" Text="{Binding PhoneNrs[1].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr2CommentInput" Margin="0,100,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr2CommentInput" Text="{Binding PhoneNrs[1].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value" Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr3Input" Margin="0,130,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[2].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr3Input" Text="{Binding PhoneNrs[2].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr3CommentInput" Margin="0,130,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr3CommentInput" Text="{Binding PhoneNrs[2].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value" Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr4Input" Margin="0,160,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[3].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr4Input" Text="{Binding PhoneNrs[3].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr4CommentInput" Margin="0,160,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr4CommentInput" Text="{Binding PhoneNrs[3].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value" Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr5Input" Margin="0,190,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[4].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr5Input" Text="{Binding PhoneNrs[4].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr5CommentInput" Margin="0,190,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr5CommentInput" Text="{Binding PhoneNrs[4].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value" Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr6Input" Margin="0,220,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[5].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr6Input" Text="{Binding PhoneNrs[5].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,220,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr6CommentInput" Margin="0,220,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr6CommentInput" Text="{Binding PhoneNrs[5].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,220,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value" Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr7Input" Margin="0,250,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[6].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr7Input" Text="{Binding PhoneNrs[6].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,250,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr7CommentInput" Margin="0,250,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr7CommentInput" Text="{Binding PhoneNrs[6].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,250,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value" Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr8Input" Margin="0,280,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[7].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr8Input" Text="{Binding PhoneNrs[7].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,280,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr8CommentInput" Margin="0,280,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr8CommentInput" Text="{Binding PhoneNrs[7].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,280,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value" Margin="6,310,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr9Input" Margin="0,310,5,0" Grid.Column="1"
<ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value"
SelectedIndex="{Binding PhoneNrs[8].Type, Mode=TwoWay}" ItemsSource="{Binding PhoneNrTypes}"
Margin="6,310,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr9Input" Text="{Binding PhoneNrs[8].Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,310,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr9CommentInput" Margin="0,280,10,0" Grid.Column="2"
<TextBox x:Name="PhoneNr9CommentInput" Text="{Binding PhoneNrs[8].Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,280,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
@ -409,11 +488,13 @@
</Grid.ColumnDefinitions>
<Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1" Width="290" HorizontalAlignment="Left"
<TextBox x:Name="IbanInput" Text="{Binding Iban, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" Width="290" HorizontalAlignment="Left"
TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/>
<Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1" Width="150" HorizontalAlignment="Left"
<TextBox x:Name="BicInput" Text="{Binding Bic, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Width="150" HorizontalAlignment="Left"
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
</Grid>
</GroupBox>
@ -426,21 +507,24 @@
</Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="UstIdNrInput" Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left"
<TextBox x:Name="UstIdNrInput" Text="{Binding UstIdNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
<TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsEnabled="False"
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="OrganicInput" Content="Bio" IsEnabled="False"
<CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Button x:Name="OrganicButton" Content="easy-cert.com" Height="25" FontSize="12" IsEnabled="False"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/>
<Button x:Name="OrganicButton" Content="easy-cert.com" IsEnabled="{Binding IsMemberSelected}"
Height="25" FontSize="12"
Click="OrganicButton_Click"
Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,40,0,0"/>
</Grid>
@ -453,17 +537,21 @@
</Grid.ColumnDefinitions>
<Label Content="Name:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingNameInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="BillingNameInput" Text="{Binding BillingName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingAddressInput" Margin="0,40,10,0" Grid.Column="1"
<TextBox x:Name="BillingAddressInput" Text="{Binding BillingAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
<TextBox x:Name="BillingPlzInput" Text="{Binding BillingPlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="BillingOrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
<ComboBox x:Name="BillingOrtInput" SelectedItem="{Binding BillingOrt, Mode=TwoWay}" ItemsSource="{Binding BillingOrtSource, Mode=TwoWay}"
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
Margin="47,70,10,0" Grid.Column="1"/>
</Grid>
</GroupBox>
@ -476,60 +564,69 @@
</Grid.ColumnDefinitions>
<Label Content="Eintritt:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EntryDateInput" Margin="0,10,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
<TextBox x:Name="EntryDateInput" Text="{Binding EntryDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Austritt:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="ExitDateInput" Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
<TextBox x:Name="ExitDateInput" Text="{Binding ExitDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BusinessSharesInput" Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
<TextBox x:Name="BusinessSharesInput" Text="{Binding BusinessSharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="AccountingNrInput" Margin="0,100,10,0" Grid.Column="1"
<TextBox x:Name="AccountingNrInput" Text="{Binding AccountingNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/>
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsEnabled="False"
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsChecked="{Binding IsActive, Mode=TwoWay}" IsEnabled="False"
Checked="ActiveInput_Changed" Unchecked="ActiveInput_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsEnabled="False"
<CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsChecked="{Binding IsVollLieferant, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/>
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="FunkionärInput" Content="Funktionär" IsEnabled="False"
<CheckBox x:Name="FunkionärInput" Content="Funktionär" IsChecked="{Binding IsFunktionär, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top" IsChecked="False"/>
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top"/>
<Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
<ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/>
<ComboBox x:Name="DefaultKgInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
<ComboBox x:Name="DefaultKgInput" SelectedItem="{Binding DefaultKg, Mode=TwoWay}" ItemsSource="{Binding DefaultKgSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name"
Margin="0,160,40,10" Grid.Column="1" Grid.ColumnSpan="2"/>
<Button x:Name="KgDetailsButton" Content="&#xE712;" FontFamily="Segoe MDL2 Assets" FontSize="14" Padding="0,1,0,0"
Click="KgDetailsButton_Click"
Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,160,10,10"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2"
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
<Label Content="Kontaktart:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/>
<CheckBox x:Name="ContactPostalInput" Content="Post" IsEnabled="False"
<CheckBox x:Name="ContactPostalInput" Content="Post" IsChecked="{Binding ContactViaPost, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsChecked="{Binding ContactViaEmail, Mode=TwoWay}" IsEnabled="False"
Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed"
HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
<Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}"
Click="DeliveryButton_Click"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" Click="AreaCommitmentButton_Click" IsEnabled="False"
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" IsEnabled="{Binding IsMemberSelected}"
Click="AreaCommitmentButton_Click"
HorizontalAlignment="Right" Margin="10,10,10,5" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
@ -544,33 +641,43 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock Name="StatusMembers" Text="Mitglieder: -"/>
<TextBlock ToolTip="{Binding StatusMembersToolTip}">
Mitglieder: <Run Text="{Binding StatusMembers}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock Name="StatusBusinessShares" Text="Geschäftsanteile: -"/>
<TextBlock ToolTip="{Binding StatusBusinessSharesToolTip}">
Geschäftsanteile: <Run Text="{Binding StatusBusinessShares}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock Name="StatusAreaCommitment" Text="Gebundene Fläche: -"/>
<TextBlock ToolTip="{Binding StatusAreaCommitmentToolTip}">
Gebundene Fläche (<Run Text="{Binding StatusAreaCommitmentInfo}"/>): <Run Text="{Binding StatusAreaCommitment}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6">
<TextBlock Name="StatusDeliveriesLastSeason" Text="Lieferungen (letzte Saison): -"/>
<TextBlock ToolTip="{Binding StatusDeliveriesThisSeasonToolTip}">
Lieferungen (<Run Text="{Binding StatusDeliveriesThisSeasonInfo}"/>): <Run Text="{Binding StatusDeliveriesThisSeason}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="7"/>
<StatusBarItem Grid.Column="8">
<TextBlock Name="StatusDeliveriesThisSeason" Text="Lieferungen (aktuelle Saison): -"/>
<TextBlock ToolTip="{Binding StatusDeliveriesLastSeasonToolTip}">
Lieferungen (<Run Text="{Binding StatusDeliveriesLastSeasonInfo}"/>): <Run Text="{Binding StatusDeliveriesLastSeason}"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
<local:ContextWindow
<local:ContextWindow
x:Class="Elwig.Windows.OriginHierarchyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@ -1,4 +1,4 @@
using Elwig.Helpers;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;

Some files were not shown because too many files have changed in this diff Show More