companies: Tippfehler und Zusätze im Trockenlauf als dieselbe Firma erkennen

Die Live-Läufe vom 11.09. mit Schwellwert 2, 3 und 4 haben zwei Schwächen der
Klassifizierung gezeigt, die vor 1a.2 behoben sein müssen — dort wird sie
dauerhaft in die Datenbank geschrieben.

Erstens stand JS Research mit 204 Firmen an zweiter Stelle der Agenturliste:
eine einzelne Firma, deren Name siebzehnmal leicht anders geschrieben war
("JS Research", "S Research UG", ...). Ebenso Dr. Müller / Dr. Mueller /
Dr. D. Müller und die Praxis Prof. Dhom mit Datumszusätzen im Namen. Ein
Tippfehler ist kein zweiter Kunde. Normalisierte Namen werden jetzt zu
Ähnlichkeits-Clustern zusammengefasst: geringe Levenshtein-Distanz oder ein
gemeinsamer Anfang aus drei Wörtern gilt unter EINER Firmen-E-Mail als dieselbe
Einheit. Echte Agenturen mit verschiedenen Kundennamen bleiben davon unberührt.

Zweitens galt Welcon Europe (Ltd. & Co. KG neben GmbH & Co. KG) als Agentur,
weil die Rechtsformen erst nach dem Entfernen der Satzzeichen abgeglichen
wurden — "gmbh & co kg" passte dann nie auf "gmbh co kg". Rechtsformen werden
jetzt als einzelne Wörter vom Ende her gestrippt, bis keines mehr passt; das
deckt alle Kombinationen ab, ohne sie einzeln aufzuführen.

Auf dem Bestand steigen die automatisch zusammenführbaren Namensvarianten
damit von 723 auf 1603 Adressen, die Sammeladressen sinken von 1425 auf 1082 —
was jetzt dort steht, sind durchweg Agenturen oder Mehrkunden-Absender.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-11 08:55:04 +00:00
parent 8df6b9e3a9
commit 7c40a0cd98
2 changed files with 129 additions and 14 deletions

View file

@ -48,6 +48,51 @@ test('a differing legal form is the same company', function () {
expect(mergeClasses()['namensvariante']['faellt_weg'])->toBe(1);
});
test('a legal form with ampersand is stripped like any other', function () {
/** @var TestCase $this */
mergeCompany('info@welcon.de', 'Welcon Europe Ltd. & Co. KG');
mergeCompany('info@welcon.de', 'Welcon Europe GmbH & Co. KG');
// Im Live-Lauf vom 11.09. stand Welcon als „Agentur" — weil „& Co. KG"
// nach dem Entfernen der Satzzeichen auf keine Rechtsform mehr passte.
expect(mergeClasses()['namensvariante']['faellt_weg'])->toBe(1);
});
test('typos and small additions do not make a company an agency', function () {
/** @var TestCase $this */
mergeCompany('info@js-research.de', 'JS Research');
mergeCompany('info@js-research.de', 'JS Research UG (haftungsbeschränkt)');
mergeCompany('info@js-research.de', 'S Research UG (haftungsbeschränkt)');
mergeCompany('info@js-research.de', 'JS Research');
$classes = mergeClasses(3);
// JS Research stand mit 204 Firmen an zweiter Stelle der Agenturliste —
// eine einzelne Firma, deren Name siebzehnmal leicht anders geschrieben
// wurde. Ein Tippfehler ist kein zweiter Kunde.
expect($classes['namensvariante']['adressen'])->toBe(1)
->and($classes['sammeladresse']['adressen'])->toBe(0);
});
test('a shared three-word prefix under one address is the same entity', function () {
/** @var TestCase $this */
mergeCompany('praxis@prof-dhom.de', 'Praxis Prof. Dr. Dhom & Kollegen MVZ GmbH (Juli 2016)');
mergeCompany('praxis@prof-dhom.de', 'Praxis Prof. Dr. Dhom & Partner');
mergeCompany('praxis@prof-dhom.de', 'Praxis Prof. Dr. Dhom');
expect(mergeClasses(3)['namensvariante']['adressen'])->toBe(1);
});
test('genuinely different clients under one agency address stay distinct', function () {
/** @var TestCase $this */
foreach (['Auto Hein', 'Ihr Personalberater', 'Designwerkstatt Coburg'] as $name) {
mergeCompany('presse@agentur-zb.de', $name);
}
// Die Ähnlichkeitsregel darf echte Agenturen nicht wegnormalisieren.
expect(mergeClasses(3)['sammeladresse']['adressen'])->toBe(1);
});
test('two genuinely different names stay below the threshold as a proposal', function () {
/** @var TestCase $this */
mergeCompany('sandra@buschgroup.com', 'Busch Group');
@ -77,8 +122,8 @@ test('the threshold decides where a collection address begins', function () {
test('many names without a single publication are flagged, not merged', function () {
/** @var TestCase $this */
for ($i = 1; $i <= 30; $i++) {
mergeCompany('yahoo@yahoo.com', "Firma {$i}");
foreach (range(1, 30) as $i) {
mergeCompany('yahoo@yahoo.com', fake()->unique()->company().' '.fake()->unique()->word());
}
$classes = mergeClasses();
@ -93,8 +138,11 @@ test('many names without a single publication are flagged, not merged', function
test('a large but working agency is not mistaken for a placeholder', function () {
/** @var TestCase $this */
for ($i = 1; $i <= 30; $i++) {
mergeCompany('pr@agentur.ag', "Kunde {$i}", published: true);
// Echte, verschiedene Kundennamen — „Kunde 1" bis „Kunde 30" lägen
// einander so nah, dass die Ähnlichkeitsregel sie zu Recht als eine
// Firma mit Filialnummern läse.
foreach (range(1, 30) as $i) {
mergeCompany('pr@agentur.ag', fake()->unique()->company().' '.fake()->unique()->word(), published: true);
}
// Die Namensanzahl allein trennt nicht. Erst die Veröffentlichung tut es.