diff --git a/src/StarfaceOutlookSync/Services/SyncEngine.cs b/src/StarfaceOutlookSync/Services/SyncEngine.cs index cb96917c..94b81a0e 100644 --- a/src/StarfaceOutlookSync/Services/SyncEngine.cs +++ b/src/StarfaceOutlookSync/Services/SyncEngine.cs @@ -17,61 +17,87 @@ namespace StarfaceOutlookSync.Services /// /// Findet einen passenden Kontakt in der Kandidatenliste. - /// Matching-Reihenfolge: E-Mail, dann Vorname+Nachname+Firma, dann Vorname+Nachname. + /// Strenges Matching: Felder die auf einer Seite gefuellt sind muessen + /// auf der anderen auch gefuellt (und gleich) sein. + /// Ein leeres Feld auf einer Seite und ein gefuelltes auf der anderen + /// bedeutet: verschiedene Kontakte. /// private static UnifiedContact FindMatch(UnifiedContact contact, List candidates) { if (candidates == null || candidates.Count == 0) return null; - // 1. Exakte E-Mail - if (!string.IsNullOrEmpty(contact.Email)) + foreach (var c in candidates) { - var byEmail = candidates.FirstOrDefault(c => - !string.IsNullOrEmpty(c.Email) && - c.Email.Equals(contact.Email, StringComparison.OrdinalIgnoreCase)); - if (byEmail != null) return byEmail; - } - - // 2. Vorname + Nachname + Firma (staerkstes Match ohne E-Mail) - if ((!string.IsNullOrEmpty(contact.FirstName) || !string.IsNullOrEmpty(contact.LastName)) - && !string.IsNullOrEmpty(contact.Company)) - { - var byNameCompany = candidates.FirstOrDefault(c => - c.FirstName.Equals(contact.FirstName, StringComparison.OrdinalIgnoreCase) && - c.LastName.Equals(contact.LastName, StringComparison.OrdinalIgnoreCase) && - c.Company.Equals(contact.Company, StringComparison.OrdinalIgnoreCase)); - if (byNameCompany != null) return byNameCompany; - } - - // 3. Vorname + Nachname (ohne Firma) - if (!string.IsNullOrEmpty(contact.FirstName) || !string.IsNullOrEmpty(contact.LastName)) - { - var byName = candidates.FirstOrDefault(c => - c.FirstName.Equals(contact.FirstName, StringComparison.OrdinalIgnoreCase) && - c.LastName.Equals(contact.LastName, StringComparison.OrdinalIgnoreCase) && - (!string.IsNullOrEmpty(c.FirstName) || !string.IsNullOrEmpty(c.LastName))); - if (byName != null) return byName; - } - - // 4. Telefonnummer (Buero oder Mobil) - if (!string.IsNullOrEmpty(contact.PhoneWork)) - { - var byPhone = candidates.FirstOrDefault(c => - !string.IsNullOrEmpty(c.PhoneWork) && - NormalizePhone(c.PhoneWork) == NormalizePhone(contact.PhoneWork)); - if (byPhone != null) return byPhone; - } - if (!string.IsNullOrEmpty(contact.PhoneMobile)) - { - var byMobile = candidates.FirstOrDefault(c => - !string.IsNullOrEmpty(c.PhoneMobile) && - NormalizePhone(c.PhoneMobile) == NormalizePhone(contact.PhoneMobile)); - if (byMobile != null) return byMobile; + if (IsMatch(contact, c)) + return c; } return null; } + private static bool IsMatch(UnifiedContact a, UnifiedContact b) + { + // Mindestens ein identifizierendes Feld muss vorhanden sein + bool hasName = !string.IsNullOrEmpty(a.FirstName) || !string.IsNullOrEmpty(a.LastName); + bool hasEmail = !string.IsNullOrEmpty(a.Email); + bool hasPhone = !string.IsNullOrEmpty(a.PhoneWork) || !string.IsNullOrEmpty(a.PhoneMobile); + + if (!hasName && !hasEmail && !hasPhone) return false; + + // E-Mail: wenn auf beiden Seiten vorhanden, muss sie gleich sein + // Wenn nur auf einer Seite vorhanden -> kein Match + if (!FieldsCompatible(a.Email, b.Email)) return false; + + // Name: wenn auf einer Seite vorhanden, muss er gleich sein + if (!FieldsCompatible(a.FirstName, b.FirstName)) return false; + if (!FieldsCompatible(a.LastName, b.LastName)) return false; + + // Firma: wenn auf einer Seite vorhanden, muss sie gleich sein + // Leere Firma vs. gefuellte Firma = verschiedene Kontakte + if (!FieldsCompatible(a.Company, b.Company)) return false; + + // Telefon: wenn auf einer Seite vorhanden, muss sie gleich sein + if (!PhoneFieldsCompatible(a.PhoneWork, b.PhoneWork)) return false; + if (!PhoneFieldsCompatible(a.PhoneMobile, b.PhoneMobile)) return false; + + // Mindestens ein starkes Match muss vorhanden sein + bool emailMatch = !string.IsNullOrEmpty(a.Email) && !string.IsNullOrEmpty(b.Email) + && a.Email.Equals(b.Email, StringComparison.OrdinalIgnoreCase); + bool nameMatch = hasName + && a.FirstName.Equals(b.FirstName, StringComparison.OrdinalIgnoreCase) + && a.LastName.Equals(b.LastName, StringComparison.OrdinalIgnoreCase) + && (!string.IsNullOrEmpty(a.FirstName) || !string.IsNullOrEmpty(a.LastName)); + bool phoneMatch = !string.IsNullOrEmpty(a.PhoneWork) && !string.IsNullOrEmpty(b.PhoneWork) + && NormalizePhone(a.PhoneWork) == NormalizePhone(b.PhoneWork); + + return emailMatch || nameMatch || phoneMatch; + } + + /// + /// Prueft ob zwei Felder kompatibel sind. + /// Beide leer = kompatibel. Beide gleich = kompatibel. + /// Eins leer, eins gefuellt = NICHT kompatibel (verschiedene Kontakte). + /// + private static bool FieldsCompatible(string a, string b) + { + bool aEmpty = string.IsNullOrEmpty(a); + bool bEmpty = string.IsNullOrEmpty(b); + + if (aEmpty && bEmpty) return true; + if (aEmpty != bEmpty) return false; // Einer leer, anderer nicht + return a.Equals(b, StringComparison.OrdinalIgnoreCase); + } + + private static bool PhoneFieldsCompatible(string a, string b) + { + bool aEmpty = string.IsNullOrEmpty(a); + bool bEmpty = string.IsNullOrEmpty(b); + + if (aEmpty && bEmpty) return true; + if (aEmpty != bEmpty) return false; + return NormalizePhone(a) == NormalizePhone(b); + } + private static string NormalizePhone(string phone) { if (string.IsNullOrEmpty(phone)) return "";