diff --git a/Elwig/Helpers/Validator.cs b/Elwig/Helpers/Validator.cs index 99173ca..68c9422 100644 --- a/Elwig/Helpers/Validator.cs +++ b/Elwig/Helpers/Validator.cs @@ -9,14 +9,14 @@ namespace Elwig.Helpers { private static readonly Dictionary PHONE_NRS = new() { { "43", new string[][] { - Array.Empty(), - new string[] { "57", "59" }, - new string[] { + [], + ["57", "59"], + [ "50", "517", "718", "804", "720", "780", "800", "802", "810", "820", "821", "828", "900", "901", "930", "931", "939", "650", "651", "652", "653", "655", "657", "659", "660", "661", "663", "664", "665", "666", "667", "668", "669", "67", "68", "69" - } + ] } }, { "49", Array.Empty() }, { "48", Array.Empty() }, @@ -162,7 +162,7 @@ namespace Elwig.Helpers { if (text.StartsWith("+43 ")) { var nr = text[4..]; var vws = PHONE_NRS["43"]; - if (!text.EndsWith(" ") && v >= 4 && v - 4 < vws.Length && vws[v - 4].Any(vw => nr.StartsWith(vw))) { + if (!text.EndsWith(' ') && v >= 4 && v - 4 < vws.Length && vws[v - 4].Any(vw => nr.StartsWith(vw))) { text += ' '; } else if (nr == "1") { text += ' '; @@ -320,18 +320,24 @@ namespace Elwig.Helpers { return new(true, null); } else if (input.Text.Length != 7) { return new(false, "Betriebsnummer zu kurz"); + } else if (!CheckLfbisNr(input.Text)) { + return new(false, "Prüfsumme der Betriebsnummer ist falsch"); + } else { + return new(true, null); } + } + + public static bool CheckLfbisNr(string nr) { + if (nr.Length != 7 || !nr.All(char.IsAsciiDigit)) + return false; // https://statistik.at/fileadmin/shared/QM/Standarddokumentationen/RW/std_r_land-forstw_register.pdf#page=41 int s = 0, v; for (int i = 0; i < 6; i++) - s += (input.Text[i] - '0') * (7 - i); + s += (nr[i] - '0') * (7 - i); v = (11 - (s % 11)) % 10; - if (v != (input.Text[6] - '0')) - return new(false, "Prüfsumme der Betriebsnummer ist falsch"); - - return new(true, null); + return v == (nr[6] - '0'); } public static ValidationResult CheckUstIdNr(TextBox input, bool required) { @@ -373,17 +379,11 @@ namespace Elwig.Helpers { return required ? new(false, "UID ist nicht optional") : new(true, null); if (text.StartsWith("AT")) { - if (text.Length != 11 || text[2] != 'U') + if (text.Length != 11 || text[2] != 'U') { return new(false, "UID ist ungültig"); - - // http://www.pruefziffernberechnung.de/U/USt-IdNr.shtml - int s = 0, v = 0; - for (int i = 0; i < 7; i++) - s += ((text[i + 3] - '0') * (i % 2 + 1)).ToString().Select(ch => ch - '0').Sum(); - v = (96 - s) % 10; - - if (v != (text[10] - '0')) + } else if (!CheckUstIdNr(text)) { return new(false, "Prüfsumme der UID ist falsch"); + } } else { return new(false, "Not implemented yet"); } @@ -391,6 +391,26 @@ namespace Elwig.Helpers { return new(true, null); } + public static bool CheckUstIdNr(string nr) { + if (nr.Length < 4 || nr.Length > 14 || !nr.All(char.IsAsciiLetterOrDigit)) + return false; + + if (nr.StartsWith("AT")) { + if (nr.Length != 11 || nr[2] != 'U') + return false; + + // http://www.pruefziffernberechnung.de/U/USt-IdNr.shtml + int s = 0, v = 0; + for (int i = 0; i < 7; i++) + s += ((nr[i + 3] - '0') * (i % 2 + 1)).ToString().Select(ch => ch - '0').Sum(); + v = (96 - s) % 10; + + return v == (nr[10] - '0'); + } else { + return false; + } + } + public static ValidationResult CheckMgNr(TextBox input, bool required, AppDbContext ctx) { var res = CheckInteger(input, required); if (!res.IsValid) { diff --git a/Elwig/Models/Entities/Member.cs b/Elwig/Models/Entities/Member.cs index aa7addd..52428a2 100644 --- a/Elwig/Models/Entities/Member.cs +++ b/Elwig/Models/Entities/Member.cs @@ -181,7 +181,6 @@ namespace Elwig.Models.Entities { public int SearchScore(IEnumerable keywords) { return Utils.GetSearchScore(new string?[] { - MgNr.ToString(), FamilyName, MiddleName, GivenName, BillingAddress?.Name, Comment, diff --git a/Elwig/Windows/MemberAdminWindow.xaml.cs b/Elwig/Windows/MemberAdminWindow.xaml.cs index e198b3f..754c968 100644 --- a/Elwig/Windows/MemberAdminWindow.xaml.cs +++ b/Elwig/Windows/MemberAdminWindow.xaml.cs @@ -69,14 +69,98 @@ namespace Elwig.Windows { private async Task RefreshMemberListQuery(bool updateSort = false) { IQueryable memberQuery = Context.Members; - if (ActiveMemberInput.IsChecked == true) - memberQuery = memberQuery.Where(m => m.IsActive); + if (ActiveMemberInput.IsChecked == true) memberQuery = memberQuery.Where(m => m.IsActive); + + var filterMgNr = new List(); + var filterZwst = new List(); + var filterKgNr = new List(); + var filterLfbisNr = new List(); + var filterUstIdNr = new List(); + var filterAreaCom = new List(); + + var filter = TextFilter.ToList(); + if (filter.Count > 0) { + var branches = await Context.Branches.ToListAsync(); + var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m); + var kgs = await Context.WbKgs.ToDictionaryAsync(k => k.AtKg.Name.ToLower(), k => k.AtKg); + var areaComs = await Context.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--); + } 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--); + } else if (e.Equals("bio", StringComparison.CurrentCultureIgnoreCase)) { + memberQuery = memberQuery.Where(m => m.IsOrganic); + filter.RemoveAt(i--); + } else if (e.Equals("!bio", StringComparison.CurrentCultureIgnoreCase)) { + memberQuery = memberQuery.Where(m => !m.IsOrganic); + filter.RemoveAt(i--); + } else if (e.Length >= 4 && e.Length <= 13 && "volllieferant".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) { + memberQuery = memberQuery.Where(m => m.IsVollLieferant); + filter.RemoveAt(i--); + } 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--); + } else if (e.Length >= 5 && e.Length <= 11 && "buchführend".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) { + memberQuery = memberQuery.Where(m => m.IsBuchführend); + filter.RemoveAt(i--); + } 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--); + } else if (e.Length >= 8 && e.Length <= 12 && "pauschaliert".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) { + memberQuery = memberQuery.Where(m => !m.IsBuchführend); + filter.RemoveAt(i--); + } 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--); + } else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) { + filterMgNr.Add(int.Parse(e)); + filter.RemoveAt(i--); + } else if (kgs.TryGetValue(e, out var kg)) { + filterKgNr.Add(kg.KgNr); + filter.RemoveAt(i--); + } else if (e.StartsWith("zwst:")) { + try { + filterZwst.Add(branches.Where(b => b.Name.StartsWith(e[5..], StringComparison.CurrentCultureIgnoreCase)).Single().ZwstId); + filter.RemoveAt(i--); + } 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--); + } else if (areaComs.ContainsKey(e.ToUpper())) { + filterAreaCom.Add(e.ToUpper()); + filter.RemoveAt(i--); + } else if (Validator.CheckLfbisNr(e)) { + filterLfbisNr.Add(e); + filter.RemoveAt(i--); + } else if (Validator.CheckUstIdNr(e.ToUpper())) { + filterUstIdNr.Add(e.ToUpper()); + filter.RemoveAt(i--); + } 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.Where(c => c.YearFrom <= Utils.CurrentYear && (c.YearTo ?? int.MaxValue) >= Utils.CurrentYear).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)); + } List members = await memberQuery.ToListAsync(); - if (TextFilter.Count > 0) { + if (filter.Count > 0 && members.Count > 0) { var dict = members.AsParallel() - .ToDictionary(m => m, m => m.SearchScore(TextFilter)) + .ToDictionary(m => m, m => m.SearchScore(filter)) .OrderByDescending(a => a.Value) .ThenBy(a => a.Key.FamilyName) .ThenBy(a => a.Key.GivenName);