feat: add lead qualification workflow

This commit is contained in:
2026-06-04 16:09:47 +02:00
parent 15d8bfeb66
commit 59824b7336
19 changed files with 2833 additions and 78 deletions

View File

@@ -7,6 +7,7 @@ import {
canStartAgentRun,
isStalePendingAgentRun,
getLeadDiscoveryContactStatus,
getLeadDiscoveryPriority,
} from "../lib/lead-discovery-run";
test("agent run guard ignores stale pending runs but blocks active runs", () => {
@@ -62,19 +63,51 @@ test("lead discovery counters preserve audit and outreach counters", () => {
test("lead discovery contact status separates leads without any contact route", () => {
assert.equal(
getLeadDiscoveryContactStatus({ websiteDomain: null, phone: null }),
getLeadDiscoveryContactStatus({ usableEmail: null }),
"missing_contact",
);
assert.equal(
getLeadDiscoveryContactStatus({ websiteDomain: "example.de", phone: null }),
getLeadDiscoveryContactStatus({ usableEmail: "info@example.de" }),
"new",
);
assert.equal(
getLeadDiscoveryContactStatus({ websiteDomain: null, phone: "030 123" }),
"new",
getLeadDiscoveryContactStatus({ usableEmail: null }),
"missing_contact",
);
});
test("lead discovery lead record marks contact missing when no usable email exists", () => {
const record = buildLeadDiscoveryLeadRecord({
campaignId: "campaign-1",
runId: "run-1",
niche: "Restaurant",
postalCode: "10115",
now: 1717480000000,
candidate: {
placeId: "place-2",
businessName: "Kontaktlos GmbH",
address: "Hauptstraße 2",
websiteUrl: "https://www.beispiel.de",
websiteDomain: "example.de",
phone: "+49 30 123",
rating: 3.9,
userRatingCount: 9,
businessStatus: "OPERATIONAL",
googleTypes: ["consulting"],
googlePrimaryType: "consulting",
googleMapsUrl: "https://maps.google.com/place-2",
sourceProvider: "google_places",
sourceFetchedAt: 1717480001000,
contactEmails: [{ email: "Herr.Bewerber@Beispiel.de", isBusinessContactAddress: false }],
},
});
assert.equal(record.contactStatus, "missing_contact");
assert.equal(record.phone, "+49 30 123");
assert.equal(record.websiteDomain, "example.de");
assert.equal(record.email, undefined);
});
test("lead discovery lead record keeps raw website url and normalized domain", () => {
const record = buildLeadDiscoveryLeadRecord({
campaignId: "campaign-1",
@@ -106,3 +139,113 @@ test("lead discovery lead record keeps raw website url and normalized domain", (
assert.equal(record.googleUserRatingCount, 12);
assert.equal(record.sourceFetchedAt, 1717480001000);
});
test("lead discovery lead record stores valid email and sets contactStatus to new", () => {
const record = buildLeadDiscoveryLeadRecord({
campaignId: "campaign-1",
runId: "run-1",
niche: "Restaurant",
postalCode: "10115",
now: 1717480000000,
candidate: {
placeId: "place-3",
businessName: "Beispiel GmbH",
address: "Hauptstraße 1",
websiteUrl: "https://www.example.de/path",
websiteDomain: "example.de",
phone: "+49 30 123",
rating: 4.5,
userRatingCount: 12,
businessStatus: "OPERATIONAL",
googleTypes: ["restaurant"],
googlePrimaryType: "restaurant",
googleMapsUrl: "https://maps.google.com/place-3",
sourceProvider: "google_places",
sourceFetchedAt: 1717480001000,
contactEmails: [
{
email: "Herr@Beispiel.de",
isBusinessContactAddress: false,
},
{
email: "info@beispiel.de",
isBusinessContactAddress: false,
},
],
},
});
assert.equal(record.contactStatus, "new");
assert.equal(record.email, "info@beispiel.de");
assert.equal(record.contactPerson, undefined);
});
test("lead discovery lead record stores normalized matching fields", () => {
const record = buildLeadDiscoveryLeadRecord({
campaignId: "campaign-1",
runId: "run-1",
niche: "Restaurant",
postalCode: "10115",
now: 1717480000000,
candidate: {
placeId: "place-4",
businessName: "Muster GmbH",
address: "Hauptstraße 1 60311 Berlin",
websiteUrl: "https://www.example.de/",
websiteDomain: "Example.de",
phone: "+49 30 123 456",
rating: 4.5,
userRatingCount: 12,
businessStatus: "OPERATIONAL",
googleTypes: ["restaurant"],
googlePrimaryType: "restaurant",
googleMapsUrl: "https://maps.google.com/place-4",
sourceProvider: "google_places",
sourceFetchedAt: 1717480001000,
email: "Info@Example.de",
contactEmails: [
{
email: "Info@Example.de",
isBusinessContactAddress: false,
},
],
},
});
assert.equal(record.normalizedEmail, "info@example.de");
assert.equal(record.normalizedPhone, "4930123456");
assert.equal(record.normalizedCompanyName, "muster gmbh");
assert.equal(record.normalizedAddress, "hauptstraße 1 60311 berlin");
});
test("lead discovery priority helper classifies blocked, deferred, and low-potential leads", () => {
assert.deepEqual(getLeadDiscoveryPriority({ isBlacklisted: true }), {
priority: "blocked",
reason: "Lead ist auf der Sperrliste.",
});
assert.deepEqual(getLeadDiscoveryPriority({ isDuplicate: true }), {
priority: "defer",
reason: "Dublettenprüfung oder Reviewpause.",
});
assert.deepEqual(getLeadDiscoveryPriority({ hasWebsite: false }), {
priority: "high",
reason: "Kein Website-Indikator vorhanden.",
});
assert.deepEqual(getLeadDiscoveryPriority({ hasWebsite: true, hasWebsiteSignal: true }), {
priority: "low",
reason: "Website vorhanden: geringer Kontaktaufwand.",
});
assert.deepEqual(getLeadDiscoveryPriority({ hasWebsite: true, hasWebsiteSignal: false }), {
priority: "medium",
reason: "Standardpriorität.",
});
assert.deepEqual(getLeadDiscoveryPriority({ hasWebsite: true }), {
priority: "medium",
reason: "Standardpriorität.",
});
});