[#14] Windows: Add DeliveryAncmtWindow
All checks were successful
Test / Run tests (push) Successful in 2m5s

This commit is contained in:
2024-08-09 22:02:54 +02:00
parent 804a17911c
commit b091bd0ec3
7 changed files with 939 additions and 1 deletions

View File

@ -292,6 +292,10 @@ namespace Elwig {
return w; return w;
} }
public static DeliveryAncmtAdminWindow FocusDeliveryAncmt() {
return FocusWindow<DeliveryAncmtAdminWindow>(() => new());
}
public static DeliveryScheduleAdminWindow FocusDeliverySchedule() { public static DeliveryScheduleAdminWindow FocusDeliverySchedule() {
return FocusWindow<DeliveryScheduleAdminWindow>(() => new()); return FocusWindow<DeliveryScheduleAdminWindow>(() => new());
} }

View File

@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models.Entities { namespace Elwig.Models.Entities {
[Table("delivery_schedule"), PrimaryKey("Year", "DsNr")] [Table("delivery_schedule"), PrimaryKey("Year", "DsNr")]
@ -30,6 +31,10 @@ namespace Elwig.Models.Entities {
[Column("max_weight")] [Column("max_weight")]
public int? MaxWeight { get; set; } public int? MaxWeight { get; set; }
[NotMapped]
public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
[NotMapped]
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
[Column("ancmt_from")] [Column("ancmt_from")]
public long? AncmtFromUnix { get; set; } public long? AncmtFromUnix { get; set; }
@ -56,6 +61,9 @@ namespace Elwig.Models.Entities {
[InverseProperty(nameof(DeliveryScheduleWineVar.Schedule))] [InverseProperty(nameof(DeliveryScheduleWineVar.Schedule))]
public virtual ICollection<DeliveryScheduleWineVar> Varieties { get; private set; } = null!; public virtual ICollection<DeliveryScheduleWineVar> Varieties { get; private set; } = null!;
[InverseProperty(nameof(DeliveryAncmt.Schedule))]
public virtual ICollection<DeliveryAncmt> Announcements { get; private set; } = null!;
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore([Description], keywords); return Utils.GetSearchScore([Description], keywords);
} }

View File

@ -0,0 +1,118 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace Elwig.Services {
public static class DeliveryAncmtService {
public static void InitInputs(this DeliveryAncmtAdminViewModel vm) {
if (vm.SelectedDeliverySchedule is DeliverySchedule s)
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, s.Year, s.DsNr);
}
public static void ClearInputs(this DeliveryAncmtAdminViewModel vm) {
}
public static async Task FillInputs(this DeliveryAncmtAdminViewModel vm, DeliveryAncmt a) {
vm.MgNr = a.MgNr;
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr);
vm.SortId = a.SortId;
vm.Weight = a.Weight;
}
public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = [];
IQueryable<DeliveryAncmt> deliveryAncmtQuery = ctx.DeliveryAnnouncements;
if (vm.SelectedDeliverySchedule is DeliverySchedule s) {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == s.Year && a.DsNr == s.DsNr);
filterNames.Add($"{s.Date:dd.MM.yyyy} - {s.Branch.Name} - {s.Description}");
} else {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == vm.FilterSeason);
filterNames.Add($"{vm.FilterSeason}");
}
var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterMgNr = new List<int>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
if (e.ToLower() is "r" or "rot") {
filterVar.AddRange(var.Values.Where(v => v.IsRed).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Rotweinsorten");
} else if (e.ToLower() is "w" or "weiß" or "weiss") {
filterVar.AddRange(var.Values.Where(v => v.IsWhite).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Weißweinsorten");
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper());
filter.RemoveAt(i--);
filterNames.Add(var[e.ToUpper()].Name);
} else if (e.Length == 3 && e[0] == '!' && var.ContainsKey(e[1..].ToUpper())) {
filterNotVar.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (e.All(char.IsAsciiDigit) && mgnr.TryGetValue(e, out var member)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);
filterNames.Add(member.AdministrativeName);
} else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
filter[i] = e[1..^1];
} else if (e.Length <= 2) {
filter.RemoveAt(i--);
}
}
if (filterMgNr.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterMgNr.Contains(a.MgNr));
if (filterVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterVar.Contains(a.SortId));
if (filterNotVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => !filterNotVar.Contains(a.SortId));
}
return (filterNames, deliveryAncmtQuery, filter);
}
public static async Task<(int, int, int, string)> UpdateDeliveryAncmt(this DeliveryAncmtAdminViewModel vm, int? oldYear, int? oldDsNr, int? oldMgNr, string? oldSortId, string? oldType) {
int year = vm.DeliverySchedule!.Year;
int dsnr = vm.DeliverySchedule!.DsNr;
int newMgNr = vm.MgNr!.Value;
string newSortId = vm.SortId!;
using (var ctx = new AppDbContext()) {
var a = new DeliveryAncmt {
Year = oldYear ?? year,
DsNr = oldDsNr ?? dsnr,
MgNr = oldMgNr ?? newMgNr,
SortId = oldSortId ?? newSortId,
Weight = vm.Weight!.Value,
Type = oldType ?? "manual",
};
if (oldDsNr != null) {
ctx.Update(a);
} else {
ctx.Add(a);
}
await ctx.SaveChangesAsync();
if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) {
await ctx.Database.ExecuteSqlRawAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = '{newSortId}' WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, '{a.SortId}')");
}
}
await App.HintContextChange();
return (year, dsnr, newMgNr, newSortId);
}
}
}

View File

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

View File

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

View File

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

View File

@ -214,7 +214,7 @@ namespace Elwig.Windows {
} }
private void DeliveryAncmtButton_Click(object sender, RoutedEventArgs evt) { private void DeliveryAncmtButton_Click(object sender, RoutedEventArgs evt) {
App.FocusDeliverySchedule(); App.FocusDeliveryAncmt();
} }
private void BaseDataButton_Click(object sender, RoutedEventArgs evt) { private void BaseDataButton_Click(object sender, RoutedEventArgs evt) {