Helpers: Add Normalizer to normalize street names in addresses
This commit is contained in:
@@ -170,16 +170,25 @@ namespace Elwig.Helpers {
|
||||
SavedChanges += OnSavedChanges;
|
||||
}
|
||||
|
||||
private static void CreateFunctions(SqliteConnection cnx) {
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => {
|
||||
return value == null ? null : Regex.IsMatch(value, pattern);
|
||||
}, true);
|
||||
cnx.CreateFunction<string?, string?, string?>("NORMALIZE_ADDRESS", (address, locality) => {
|
||||
return string.IsNullOrWhiteSpace(address) ? null : Normalizer.NormalizeAddress(address, locality);
|
||||
}, true);
|
||||
}
|
||||
|
||||
public static SqliteConnection Connect(string? connectionString = null) {
|
||||
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
CreateFunctions(cnx);
|
||||
cnx.Open();
|
||||
return cnx;
|
||||
}
|
||||
|
||||
public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) {
|
||||
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
CreateFunctions(cnx);
|
||||
await cnx.OpenAsync();
|
||||
return cnx;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static partial class Normalizer {
|
||||
|
||||
[GeneratedRegex(@"(?=(?:(?:bundes)?straße|gasse|weg|platz|allee|ring|zeile?|steig|hof|markt|kai|ufer|brücke|tor|stiege|promenade|gang|siedlung)\b)|[\s-]+", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
|
||||
private static partial Regex GeneratedStreetNameWordsRegex();
|
||||
private static readonly Regex StreetNameWordsRegex = GeneratedStreetNameWordsRegex();
|
||||
|
||||
private static readonly HashSet<string> StreetTypes = [
|
||||
"Straße", "Gasse", "Weg", "Platz", "Allee", "Ring", "Zeile", "Zeil", "Steig", "Hof",
|
||||
"Markt", "Kai", "Ufer", "Brücke", "Tor", "Stiege", "Promenade", "Gang", "Siedlung"];
|
||||
|
||||
private static readonly HashSet<string> StreetArticles = ["der", "die", "das", "des", "dem", "den"];
|
||||
|
||||
private static readonly HashSet<string> StreetPrepositions = [
|
||||
"an", "am", "auf", "aus", "bei", "beim", "hinter", "hinterm", "in", "im", "ins", "nach",
|
||||
"neben", "über", "unter", "von", "vom", "van", "vor", "zu", "zum", "zur", "zwischen",
|
||||
"durch", "mit"];
|
||||
|
||||
private static readonly HashSet<string> StreetNumbers = [ "Zwei", "Drei", "Vier", "Fünf", "Sechs", "Sieben" ];
|
||||
|
||||
private static readonly HashSet<string> StreetAdjectives = [
|
||||
"alter", "alte", "alten", "altem", "altes",
|
||||
"äußere", "äußere", "äußeren", "äußerem", "äußeres",
|
||||
// "breiter", "breite", "breiten", "breitem", "breites",
|
||||
"dritter", "dritte", "dritten", "drittem", "drittes",
|
||||
"erster", "erste", "ersten", "erstem", "erstes",
|
||||
"großer", "große", "großen", "großem", "großes",
|
||||
"hoher", "hohe", "hohen", "hohem", "hohes",
|
||||
"innerer", "innere", "inneren", "innerem", "inneres",
|
||||
"kleiner", "kleine", "kleinen", "kleinem", "kleines",
|
||||
"kurzer", "kurze", "kurzen", "kurzem", "kurzes",
|
||||
"langer", "lange", "langen", "langem", "langes",
|
||||
"linker", "linke", "linken", "linkem", "linkes",
|
||||
"mittlerer", "mittlere", "mittleren", "mittlerem", "mittleres",
|
||||
"neuer", "neue", "neuen", "neuem", "neues",
|
||||
"oberer", "obere", "oberen", "oberem", "oberes",
|
||||
"rechter", "rechte", "rechten", "rechtem", "rechtes",
|
||||
"schmaler", "schmale", "schmalen", "schmalem", "schmales",
|
||||
"schöner", "schöne", "schönen", "schönem", "schönes",
|
||||
"unterer", "untere", "unteren", "unterem", "unteres",
|
||||
"hinterer", "hintere", "hinteren", "hinterem", "hinteres",
|
||||
"vorderer", "vordere", "vorderen", "vorderem", "vorderes",
|
||||
"runder", "runde", "runden", "rundem", "rundes",
|
||||
"kalter", "kalte", "kalten", "kaltem", "kaltes"];
|
||||
|
||||
private static readonly HashSet<string> StreetLastNames = [
|
||||
"Habsburger", "Lothringer", "Leitzenberger", "Luyderer", "Preiner", "Radinger", "Seefelder", "Staribacher", "Gschmeidler",
|
||||
"Hannover", "Münster", "Salzgitter", "Speyer", "Trier"];
|
||||
|
||||
private static readonly HashSet<string> StreetPersonTitles = [
|
||||
"St.", "Familie",
|
||||
"Dr.", "DDr.", "Hofrat", "Baurat", "Kommerzialrat", "Dir.", "Ing.", "Prof.", "Mag.", "DI", "OSR", "Präsident",
|
||||
"Landeshauptmann", "LH", "Bürgermeister", "Bgm.", "Minister",
|
||||
"Kaiser", "Kaiserin", "König", "Königin", "Erzherzog", "Erzherzogin", "Herzog", "Fürst", "Graf", "Gräfin", "Markgraf", "Kronprinz", "Prinz", "Ritter", "Freiherr", "Lord",
|
||||
"Abt", "Bischof", "Propst", "Dechant", "Kardinal", "Monsignore", "Pfarrer", "Pater", "Prälat",
|
||||
"Oberst"];
|
||||
|
||||
private static readonly HashSet<string> StreetGivenNames = [
|
||||
"Adalbert", "Adam", "Adele", "Adelheid", "Adolf", "Adolph", "Adrian", "Albert", "Albrecht", "Alexander", "Alfons", "Alfred", "Alois", "Aloisia",
|
||||
"Andreas", "Andrea", "Andre", "Anna", "Anton", "Arnold", "Arthur", "August", "Augustin", "Augustine", "Agnes", "Angelika", "Ada", "Adelheid", "Anni", "Anita",
|
||||
"Barbara", "Beate", "Beatrix", "Benedikt", "Bernhard", "Berta", "Bertha", "Berthold", "Brüder", "Bruno", "Brigitte",
|
||||
"Camillo", "Carl", "Caspar", "Catharina", "Christian", "Christine", "Christof", "Christoph", "Clara", "Clemens", "Conrad", "Claus", "Camillo", "Claudia",
|
||||
"Daniel", "Dietmar", "Dietrich",
|
||||
"Edgar", "Edi", "Edith", "Edmund", "Egon", "Elsa", "Eduard", "Emmerich", "Erhard", "Erich", "Ernst", "Erwin", "Eugen", "Ewald", "Eberhard",
|
||||
"Elisabeth", "Emanuel", "Emerich", "Emil", "Emma", "Engelbert", "Elias",
|
||||
"Franz", "Ferdinand", "Friedrich", "Felix", "Florian", "Fritz", "Franziska", "Fred",
|
||||
"Georg", "Gregor", "Gustav", "Gabriel", "Gebrüder", "Gabriele", "Gerhard", "Gerhart", "Gertrude", "Gottfried", "Gottlieb", "Grete",
|
||||
"Günther", "Günter",
|
||||
"Hans", "Hanns", "Heinrich", "Heinz", "Helmut", "Helmuth", "Hermann", "Herta", "Herbert", "Hubert", "Harald", "Horst", "Helene", "Helga",
|
||||
"Hieronymus", "Hilde", "Hugo",
|
||||
"Isabelle", "Ignaz", "Ilse", "Ingeborg", "Isidor",
|
||||
"Josef", "Josefine", "Joseph", "Johann", "Johanna", "Johan", "Johannes", "Hannes", "Harry", "Jakob", "Jacob", "Jan", "Julian", "Julius", "Julia", "Jürgen",
|
||||
"Karl", "Kasimir", "Konrad", "Klemens", "Klara", "Katharina", "Kurt", "Kaspar", "Klaus", "Koloman", "Konstantin", "Killian",
|
||||
"Leopold", "Leo", "Lorenz", "Lothar", "Louise", "Ludmilla", "Ludwig", "Lukas", "Ladislaus", "Lambert", "Laurenz", "Liese",
|
||||
"Lise", "Lilly", "Lina", "Lothar", "Lotte", "Luise", "Leonhard", "Lotte", "Martha",
|
||||
"Maria", "Marie", "Michael", "Martin", "Max", "Maximilian", "Manuel", "Manuela", "Moritz", "Marianne", "Manfred", "Marc",
|
||||
"Mathias", "Matthias", "Matthäus",
|
||||
"Margarete", "Margarethe",
|
||||
"Nico", "Niko", "Niklas", "Niclas", "Nikolaus", "Norbert",
|
||||
"Otto", "Ottokar", "Oskar", "Olga", "Oswald", "Othmar",
|
||||
"Paul", "Pauline", "Patrick", "Pepi", "Peter", "Philipp", "Philip", "Petrus",
|
||||
"Robert", "Rudolf", "Richard", "Ruprecht", "Raffael", "Rolf", "Roman", "Rosa", "Rosalia", "Rupert", "Raimund",
|
||||
"Sigmund", "Siegfried", "Simon", "Stanislaus", "Stefan", "Stephan", "Sepp", "Samuel", "Sebastian", "Susi",
|
||||
"Theodor", "Thomas", "Theresia", "Thaddäus", "Theo", "Theophil", "Tina", "Titus", "Toni", "Trude",
|
||||
"Ullrich", "Ulrich", "Udo",
|
||||
"Viktor", "Vinzenz", "Victor",
|
||||
"Wolfgang", "Willibald", "Wilhelm", "Willhelm", "Wilhelmine", "Walter", "Walther", "Wenzel", "Werner", "Willi",
|
||||
"Zacharias"];
|
||||
|
||||
private static readonly HashSet<string> LocalityNames = [
|
||||
"Groß-Enzersdorf",
|
||||
"Groß Burgstall", "Klein Eberharts", "Klein-Engersdorf", "Groß-Enzersdorf", "Klein-Erlauf",
|
||||
"Groß Gerungs", "Klein Gundholz", "Klein-Harras", "Klein-Heinrichschlag", "Groß-Höbarten",
|
||||
"Klein-Jetzelsdorf", "Klein-Mariazell", "Groß Meinharts", "Klein-Meiseldorf", "Groß-Neusiedl", "Klein-Neusiedl", "Klein-Pöchlarn",
|
||||
"Groß-Radischen", "Klein-Radischen", "Groß Raßberg", "Klein Reinprechts", "Klein-Reinprechtsdorf", "Groß-Reipersdorf", "Klein-Ruprechts",
|
||||
"Groß-Schollach", "Klein-Schollach", "Groß-Schweinbarth", "Groß-Siegharts", "Klein-Siegharts", "Groß Sierning",
|
||||
"Klein-Ulrichschlag", "Klein Weinberg", "Klein Wetzles", "Klein-Wien",
|
||||
];
|
||||
|
||||
private static bool IsGivenName(this string word) {
|
||||
return StreetGivenNames.Contains(word);
|
||||
}
|
||||
|
||||
private static bool IsFamilyName(this string word) {
|
||||
if (StreetLastNames.Contains(word))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsPersonalName(this string word) {
|
||||
return word.IsGivenName() || word.IsFamilyName();
|
||||
}
|
||||
|
||||
private static bool IsGeographicalDerivativeName(this string word) {
|
||||
if (!word.EndsWith("er") || word.IsPersonalName() || word == "Weißer")
|
||||
return false;
|
||||
return word.StartsWith("St. ") || word.StartsWith("Ober ") || word.StartsWith("Unter ") || word.StartsWith("Maria") ||
|
||||
word.EndsWith("sdorfer") || word.EndsWith("ndorfer") || word.EndsWith("ldorfer") ||
|
||||
word.EndsWith("felder") ||
|
||||
word.EndsWith("sthaler") || word.EndsWith("nthaler") || word.EndsWith("lthaler") ||
|
||||
word.EndsWith("dauer") || word.EndsWith("lauer") || word.EndsWith("mauer") || word.EndsWith("nauer") || word.EndsWith("ßauer") ||
|
||||
word.EndsWith("lbacher") || word.EndsWith("nbacher") || word.EndsWith("ßbacher") ||
|
||||
word.EndsWith("ringer") || word.EndsWith("singer") ||
|
||||
word.EndsWith("sbrunner") || word.EndsWith("nbrunner") || word.EndsWith("lbrunner") || word.EndsWith("abrunner") || word.EndsWith("ingbrunner") ||
|
||||
word.EndsWith("sburger") || word.EndsWith("nburger") || word.EndsWith("mburger") || word.EndsWith("lburger") || word.EndsWith("neuburger") ||
|
||||
word.EndsWith("berger") ||
|
||||
word.EndsWith("winkler") ||
|
||||
word.EndsWith("lsteiner") || word.EndsWith("nsteiner") ||
|
||||
word.EndsWith("städter") ||
|
||||
word.EndsWith("stättner") || word.EndsWith("stettner") ||
|
||||
word.EndsWith("brücker") ||
|
||||
word.EndsWith("further") || word.EndsWith("furter") ||
|
||||
word.EndsWith("aaer") ||
|
||||
word.EndsWith("seer") || word.EndsWith("leer") ||
|
||||
word.EndsWith("tzner") ||
|
||||
word.EndsWith("zeller") ||
|
||||
word.EndsWith("rther") ||
|
||||
word.EndsWith("sdner") ||
|
||||
word.EndsWith("gerner") || word.EndsWith("berner") ||
|
||||
word.EndsWith("frieder") ||
|
||||
word.EndsWith("litzer") || word.EndsWith("mitzer") || word.EndsWith("eutzer") ||
|
||||
word.EndsWith("loner") ||
|
||||
word.EndsWith("skirchner") || word.EndsWith("nkirchner") || word.EndsWith("ingkirchner") ||
|
||||
word.EndsWith("ziger") ||
|
||||
word.EndsWith("ließer") ||
|
||||
word.EndsWith("gerer") ||
|
||||
word.EndsWith("kreuzer") ||
|
||||
word == "Wiener" || word == "Brünner" || word == "Znaimer" || word == "Triester" || word == "Prager" || word == "Berliner" || word == "Basler" ||
|
||||
word == "Retzer" || word == "Tullner" || word == "Badner" ||
|
||||
word == "Gruber" || word == "Sulzer" || word == "Harraser" || word == "Dörfleser" || word == "Neusiedler" ||
|
||||
word == "Dornauer" || word == "Siebenhauser" || word == "Alser" ||
|
||||
word == "Bockenheimer";
|
||||
}
|
||||
|
||||
public static string NormalizeAddress(string address, string? locality = null) {
|
||||
var idx = address.IndexOf(',');
|
||||
if (idx >= 0) address = address[..idx];
|
||||
var (name, nr) = Utils.SplitAddress(address);
|
||||
return (NormalizeStreetName(name, locality) + " " + nr).Trim();
|
||||
}
|
||||
|
||||
public static string NormalizeStreetName(string name, string? locality = null) {
|
||||
name = name.Trim().ToLowerInvariant();
|
||||
if (name == "") {
|
||||
return "";
|
||||
} else if (name == "nr.") {
|
||||
if (locality == null)
|
||||
return "Nr.";
|
||||
name = locality.Trim().ToLowerInvariant();
|
||||
}
|
||||
|
||||
name = name.Replace("hautp", "haupt")
|
||||
.Replace("leop.", "leopold ").Replace("ferd.", "ferdinand ").Replace("gottfr.", "gottfried ")
|
||||
.Replace("heinr.", "heinrich").Replace("joh.", "johann ").Replace("jos.", "josef ")
|
||||
.Replace("msgr.", "monsignore ").Replace("pf.", "pfarrer ")
|
||||
.Replace("präs.", "präsident ");
|
||||
name = name.Replace("str.", "straße").Replace("strasse", "straße")
|
||||
.Replace("bruecke", "brücke").Replace("pl.", "platz")
|
||||
.Replace("gr.", "groß").Replace("kl.", "klein")
|
||||
.Replace("sankt-", "st.").Replace("sankt ", "st.")
|
||||
.Replace("wr.", "wiener ")
|
||||
.Replace(".", ". ");
|
||||
List<string> words = [.. StreetNameWordsRegex.Split(name)
|
||||
.Where(w => !string.IsNullOrWhiteSpace(w))
|
||||
.Select(w => StreetArticles.Contains(w) || StreetPrepositions.Contains(w) ? w : string.Concat(w[0..1].ToUpper(), w.AsSpan(1)))
|
||||
.Select(w => w == "Ddr." ? "DDr." : w == "Lh" || w == "Osr" ? w.ToUpper() : w)
|
||||
];
|
||||
if (words[0] == "Di") words[0] = "DI";
|
||||
|
||||
var temp = "|" + string.Join('|', words) + "|";
|
||||
temp = temp.Replace("|Dipl.|Ing.|", "|DI|").Replace("|Dipl|Ing.|", "|DI|")
|
||||
.Replace("|Kaiser|Ebersdorf|", "|Kaiser-Ebersdorf|")
|
||||
.Replace("|Kalk|Deutz|", "|Kalk-Deutz|").Replace("|Kalk|Deutzer|", "|Kalk-Deutzer|")
|
||||
.Replace("|Hohe|Wand|", "|Hohe Wand|").Replace("|Hohen|Wand|", "|Hohen Wand|").Replace("|Hohewand|", "|Hohe Wand|")
|
||||
.Replace("|Alten|Markt|", "|Altenmarkt|")
|
||||
.Replace("|Kirch|Hof|", "|Kirchhof|").Replace("|Bisc|Hof|", "|Bischof|")
|
||||
.Replace("|Wiener|Neustadt|", "|Wiener Neustadt|").Replace("|Wiener|Neustädter|", "|Wiener Neustädter|")
|
||||
.Replace("|St.|Pölten|", "|St. Pölten|").Replace("|St.|Pöltner|", "|St. Pöltner|")
|
||||
.Replace("|Gross|", "|Groß|")
|
||||
.Replace("|Nr|", "|Nr.|").Replace("|Und|", "|und|");
|
||||
words = [.. temp.Trim('|').Split('|')];
|
||||
|
||||
if (words.Count > 1 && (words[0] == "Groß" || words[0] == "Klein") && !StreetTypes.Contains(words[1])) {
|
||||
var w = words[0] + words[1].ToLowerInvariant();
|
||||
words.RemoveRange(0, 2);
|
||||
words.Insert(0, w);
|
||||
}
|
||||
if (words[0].StartsWith("Groß") || words[0].StartsWith("Klein")) {
|
||||
var repl = LocalityNames.FirstOrDefault(n => n.Replace(" ", "").Replace("-", "").Equals(words[0], StringComparison.InvariantCultureIgnoreCase));
|
||||
if (repl != null) words[0] = repl;
|
||||
}
|
||||
|
||||
if (words.Count > 2 && words[^2] == "in") {
|
||||
// remove suffix "in ..."
|
||||
words.RemoveRange(words.Count - 2, 2);
|
||||
}
|
||||
|
||||
char sep = name.Contains('-') ? '-' : ' ';
|
||||
if (words.Count <= 1) {
|
||||
// single word
|
||||
} else if (StreetTypes.Contains(words[0]) && !StreetTypes.Contains(words[^1])) {
|
||||
// "Straße der ..." / "Straße des ..."
|
||||
sep = ' ';
|
||||
} else if (StreetTypes.Contains(words[^1])) {
|
||||
// "... Straße" / "...straße"
|
||||
var type = words[^1];
|
||||
var isCompound = true;
|
||||
var isAdjective = StreetAdjectives.Contains(words[^2].ToLowerInvariant());
|
||||
var isGeographical = false;
|
||||
var isPerson = false;
|
||||
|
||||
var geo = words.FindLastIndex(IsGeographicalDerivativeName);
|
||||
if (geo >= 0 && geo < words.Count - 2 && words[^2] != "Haupt" && words[^2] != "Bundes" && words[^2] != "Keller") {
|
||||
isPerson = true;
|
||||
isGeographical = true;
|
||||
} else if (words.Any(w => w.Contains(' ') && !w.IsGeographicalDerivativeName())) {
|
||||
isPerson = true;
|
||||
isGeographical = true;
|
||||
} else if (StreetNumbers.Contains(words[0])) {
|
||||
isPerson = true;
|
||||
isGeographical = true;
|
||||
} else if (!StreetPrepositions.Contains(words[0]) && !StreetArticles.Contains(words[0]) && words.FindIndex(StreetPrepositions.Contains) > 0) {
|
||||
isPerson = true;
|
||||
isGeographical = true;
|
||||
}
|
||||
|
||||
if (words.Count(w => w.Contains('.')) >= 2 || words.Contains("van") || words.Contains("von") || words.Contains("St.") ||
|
||||
StreetPersonTitles.Contains(words[0]) || words.Any(w => w.IsGivenName()))
|
||||
{
|
||||
isPerson = true;
|
||||
}
|
||||
|
||||
if (!isPerson && isAdjective || StreetArticles.Contains(words[^2]) || StreetPrepositions.Contains(words[^2])) {
|
||||
isCompound = false;
|
||||
}
|
||||
|
||||
if (!isPerson && words[^2].EndsWith("er") && !StreetLastNames.Contains(words[^2])) {
|
||||
isGeographical = IsGeographicalDerivativeName(words[^2]);
|
||||
} else if (words.Count >= 3 && words.Contains("Bad")) {
|
||||
isPerson = true;
|
||||
isGeographical = true;
|
||||
}
|
||||
|
||||
sep = isPerson ? '-' : ' ';
|
||||
|
||||
if (isPerson && words.Count == 2 && !isGeographical) {
|
||||
isPerson = false;
|
||||
isCompound = true;
|
||||
}
|
||||
if (isGeographical || isPerson) {
|
||||
isCompound = false;
|
||||
}
|
||||
if (isCompound) {
|
||||
var w = words[^2] + type.ToLowerInvariant();
|
||||
words.RemoveRange(words.Count - 2, 2);
|
||||
words.Add(w);
|
||||
}
|
||||
|
||||
if (isPerson) {
|
||||
words = [.. words.Select(w => w.Replace(' ', '-'))];
|
||||
}
|
||||
} else {
|
||||
// unknown
|
||||
}
|
||||
|
||||
if (sep == '-' && StreetPrepositions.Contains(words[0]) && words[0] != "von" && words[0] != "van") {
|
||||
sep = ' ';
|
||||
int i;
|
||||
for (i = 0; StreetPrepositions.Contains(words[i]) && i < words.Count; i++);
|
||||
var word = string.Join('-', words.Skip(i));
|
||||
words = [.. words.Take(i).Append(word)];
|
||||
}
|
||||
|
||||
var norm = string.Join(sep, words);
|
||||
return string.Concat(norm[0..1].ToUpperInvariant(), norm.AsSpan(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -711,5 +711,25 @@ namespace Elwig.Helpers {
|
||||
|
||||
return new(true, null);
|
||||
}
|
||||
|
||||
public static ValidationResult CheckAddress(TextBox input, bool required, string? locality = null) {
|
||||
var text = input.Text;
|
||||
var (name, nr) = Utils.SplitAddress(text);
|
||||
if (!string.IsNullOrWhiteSpace(nr)) {
|
||||
var street = Normalizer.NormalizeStreetName(name.Replace(",", ""), locality);
|
||||
var idx = input.CaretIndex;
|
||||
input.Text = $"{street} {nr}".Trim();
|
||||
if (idx >= (text.Length - nr.Length)) {
|
||||
input.CaretIndex = street.Length + (idx - (text.Length - nr.Length)) + 1;
|
||||
} else if (idx > 0) {
|
||||
input.CaretIndex = idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (text.Length == 0)
|
||||
return required ? new(false, "Adresse ist nicht optional") : new(true, null);
|
||||
|
||||
return new(true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,12 @@ ALTER TABLE wine_cultivation ADD COLUMN organic INTEGER NOT NULL CHECK (orga
|
||||
ALTER TABLE member_history RENAME COLUMN signed TO signed_member;
|
||||
ALTER TABLE member_history ADD COLUMN signed_company INTEGER NOT NULL CHECK (signed_company IN (TRUE, FALSE)) DEFAULT FALSE;
|
||||
|
||||
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -11, 10)) FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr WHERE a.name LIKE '%AT-BIO-___)';
|
||||
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -10, 10)) FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr WHERE a.name LIKE '%AT-BIO-___';
|
||||
|
||||
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||
|
||||
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -11, 10))
|
||||
FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr
|
||||
WHERE m.mgnr = member.mgnr AND a.name LIKE '%AT-BIO-___)';
|
||||
UPDATE member SET org_auth_code = UPPER(suffix) WHERE suffix LIKE 'AT-BIO-___';
|
||||
|
||||
UPDATE wine_cultivation SET organic = TRUE WHERE name LIKE '%bio%' OR name LIKE '%org%' OR description LIKE '%AT-BIO-%';
|
||||
@@ -18,5 +22,18 @@ UPDATE wine_cultivation SET description = 'Biologische Produktion' WHERE organic
|
||||
|
||||
UPDATE client_parameter SET param = 'CLIENT_ORGANIC_AUTHORITYCODE' WHERE param = 'CLIENT_ORGANIC_AUTHORITY';
|
||||
|
||||
|
||||
UPDATE member_telephone_number SET comment = NULL WHERE comment = '';
|
||||
UPDATE member_email_address SET comment = NULL WHERE comment = '';
|
||||
|
||||
UPDATE member SET address = 'Untere Weißgerberstraße' WHERE address = 'U. Weißgasse Straße';
|
||||
UPDATE member SET address = 'Edgar-Penzig-Franz-Straße' WHERE address = 'Edgar-Penzing-Franz-Straße';
|
||||
|
||||
UPDATE member
|
||||
SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest))
|
||||
WHERE address != NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest));
|
||||
UPDATE member_billing_address
|
||||
SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member_billing_address.postal_dest))
|
||||
WHERE address != NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member_billing_address.postal_dest));
|
||||
|
||||
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||
|
||||
@@ -434,7 +434,7 @@ namespace Elwig.Windows {
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
|
||||
protected void TextBox_TextChanged(object sender, TextChangedEventArgs? evt) {
|
||||
var input = (Control)sender;
|
||||
var tb = input as TextBox;
|
||||
if (SenderIsRequired(input) && tb?.Text.Length == 0) {
|
||||
@@ -603,5 +603,13 @@ namespace Elwig.Windows {
|
||||
protected void AreaComTypeDetailsButton_Click(object sender, RoutedEventArgs? evt) {
|
||||
App.FocusBaseDataAreaComType();
|
||||
}
|
||||
|
||||
protected void AddressInput_TextChanged(object sender, TextChangedEventArgs? evt) {
|
||||
if ((IsCreating || IsEditing) && sender is TextBox b) {
|
||||
InputTextChanged(b, (i, r) => Validator.CheckAddress(i, r));
|
||||
} else {
|
||||
TextBox_TextChanged(sender, evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<Label Content="Adresse:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,160,0,10"/>
|
||||
<TextBox x:Name="ClientAddressInput"
|
||||
TextChanged="TextBox_TextChanged"
|
||||
TextChanged="AddressInput_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="0,160,10,10"/>
|
||||
|
||||
<Label Content="PLZ/Ort:"
|
||||
|
||||
@@ -371,7 +371,7 @@
|
||||
<Label Content="Adresse:" Margin="10,130,0,0"/>
|
||||
<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"/>
|
||||
TextChanged="AddressInput_TextChanged"/>
|
||||
|
||||
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Text="{Binding PlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
@@ -379,6 +379,7 @@
|
||||
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
|
||||
<ComboBox x:Name="OrtInput" SelectedItem="{Binding Ort, Mode=TwoWay}" ItemsSource="{Binding OrtSource, Mode=TwoWay}"
|
||||
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
|
||||
SelectionChanged="OrtInput_SelectionChanged"
|
||||
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
@@ -603,7 +604,7 @@
|
||||
<Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="BillingAddressInput" Text="{Binding BillingAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="0,40,10,0" Grid.Column="1"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
TextChanged="AddressInput_TextChanged"/>
|
||||
|
||||
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="BillingPlzInput" Text="{Binding BillingPlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
@@ -611,6 +612,7 @@
|
||||
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
|
||||
<ComboBox x:Name="BillingOrtInput" SelectedItem="{Binding BillingOrt, Mode=TwoWay}" ItemsSource="{Binding BillingOrtSource, Mode=TwoWay}"
|
||||
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
|
||||
SelectionChanged="OrtInput_SelectionChanged"
|
||||
Margin="47,70,10,0" Grid.Column="1"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -1008,5 +1008,23 @@ namespace Elwig.Windows {
|
||||
ValidateRequiredInputs();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
new protected void AddressInput_TextChanged(object sender, TextChangedEventArgs? evt) {
|
||||
if ((IsCreating || IsEditing) && sender is TextBox b) {
|
||||
InputTextChanged(b, (i, r) => Validator.CheckAddress(i, r, b == BillingAddressInput ? ViewModel.BillingOrt?.Ort.Name : ViewModel.Ort?.Ort.Name));
|
||||
} else {
|
||||
TextBox_TextChanged(sender, evt);
|
||||
}
|
||||
}
|
||||
|
||||
private void OrtInput_SelectionChanged(object sender, SelectionChangedEventArgs? evt) {
|
||||
if (!IsCreating && !IsEditing)
|
||||
return;
|
||||
if (sender == BillingOrtInput) {
|
||||
InputTextChanged(BillingAddressInput, (i, r) => Validator.CheckAddress(i, r, ViewModel.BillingOrt?.Ort.Name));
|
||||
} else {
|
||||
InputTextChanged(AddressInput, (i, r) => Validator.CheckAddress(i, r, ViewModel.Ort?.Ort.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user