diff --git a/Elwig/App.xaml.cs b/Elwig/App.xaml.cs index 55b130f..8dc6160 100644 --- a/Elwig/App.xaml.cs +++ b/Elwig/App.xaml.cs @@ -292,6 +292,10 @@ namespace Elwig { return w; } + public static DeliveryAncmtAdminWindow FocusDeliveryAncmt() { + return FocusWindow(() => new()); + } + public static DeliveryScheduleAdminWindow FocusDeliverySchedule() { return FocusWindow(() => new()); } diff --git a/Elwig/Models/Entities/DeliverySchedule.cs b/Elwig/Models/Entities/DeliverySchedule.cs index 43ce315..5ba9f16 100644 --- a/Elwig/Models/Entities/DeliverySchedule.cs +++ b/Elwig/Models/Entities/DeliverySchedule.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; namespace Elwig.Models.Entities { [Table("delivery_schedule"), PrimaryKey("Year", "DsNr")] @@ -30,6 +31,10 @@ namespace Elwig.Models.Entities { [Column("max_weight")] public int? MaxWeight { get; set; } + [NotMapped] + public int AnnouncedWeight => Announcements.Sum(a => a.Weight); + [NotMapped] + public double? Percent => (double)AnnouncedWeight / MaxWeight * 100; [Column("ancmt_from")] public long? AncmtFromUnix { get; set; } @@ -56,6 +61,9 @@ namespace Elwig.Models.Entities { [InverseProperty(nameof(DeliveryScheduleWineVar.Schedule))] public virtual ICollection Varieties { get; private set; } = null!; + [InverseProperty(nameof(DeliveryAncmt.Schedule))] + public virtual ICollection Announcements { get; private set; } = null!; + public int SearchScore(IEnumerable keywords) { return Utils.GetSearchScore([Description], keywords); } diff --git a/Elwig/Services/DeliveryAncmtService.cs b/Elwig/Services/DeliveryAncmtService.cs new file mode 100644 index 0000000..c672df7 --- /dev/null +++ b/Elwig/Services/DeliveryAncmtService.cs @@ -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, IQueryable, List)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) { + List filterNames = []; + IQueryable 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(); + var filterNotVar = new List(); + var filterMgNr = new List(); + + 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); + } + } +} diff --git a/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs b/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs new file mode 100644 index 0000000..4e5cbf6 --- /dev/null +++ b/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs @@ -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 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 _deliveryAncmts = []; + [ObservableProperty] + private DeliverySchedule? _selectedDeliverySchedule; + [ObservableProperty] + private IEnumerable _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 _memberSource = []; + [ObservableProperty] + private string? _memberAddress; + + [ObservableProperty] + private DeliverySchedule? _deliverySchedule; + [ObservableProperty] + private IEnumerable _deliveryScheduleSource = []; + [ObservableProperty] + private string? _sortId; + [ObservableProperty] + private WineVar? _wineVariety; + [ObservableProperty] + private IEnumerable _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; + } +} diff --git a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml new file mode 100644 index 0000000..aee0938 --- /dev/null +++ b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Strg+F + Traubenanmeldungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar. + Groß- und Kleinschreibung ist in den meisten Fällen egal. + + Filtern nach: + Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ... + Rot/Weiß: z.B. r, Rot, w, weiß, ... + Mitglied: z.B. 1234, 987, ... + + + + + + + + + + + + + + + kg + / kg + (%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +