feat(kontakte): Vorbereich "Aus Mails" mit Dublettenabgleich
Der Postfachabgleich hat gefundene Personen bisher direkt in den Kontaktbestand geschrieben. Damit wächst eine gepflegte Liste ungefragt mit jeder Adresse aus jeder Mail - genau daran ist das Altsystem gescheitert. Funde landen jetzt in einem Vorbereich (crm_leads.mail_pool = 1), der als Unterpunkt "Aus Mails" unter dem Menüpunkt Kontakte aufklappt. Der Kontaktbestand bleibt unberührt, bis dort jemand entscheidet; in Opportunities, Kalender und Netzwerk tauchen die Funde nicht auf. Eine Spalte statt einer zweiten Tabelle, weil crm_mail_links und crm_mail_contact_candidates per Fremdschlüssel auf crm_leads zeigen - so behält der Vorbereich seine Mailbezüge. Übernehmen kennt zwei Wege: - neue Person: Kopie mit mail_pool = 0, die Mailverknüpfungen wandern mit - Dublette (Adresse, sonst voller Name): gold markiert, abweichende Felder mit altem und neuem Wert; die Feldauswahl schreibt sie in den vorhandenen Kontakt. Vorausgewählt sind nur Lücken - ein Wert aus einer alten Signatur soll nichts Gepflegtes stillschweigend ersetzen. db.js verschiebt beim ersten Start die vorhandenen Kontakte mit source = 'Mail' in den Vorbereich, einmalig über den Merker leads_mail_pool_migrated in crm_settings. Die Sidebar-Gruppe ist dabei generisch geworden (MenuGroup) - Kontakte und Scanner benutzen jetzt denselben Code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8090d0aae1
commit
ea55acdb5c
+10
-1
@@ -41,6 +41,8 @@ erDiagram
|
||||
varchar last_name
|
||||
varchar company
|
||||
enum status "Neu .. Verloren"
|
||||
varchar source "z.B. 'Mail' bei Herkunft Postfach"
|
||||
tinyint mail_pool "1 = Vorbereich 'Aus Mails', noch kein Kontakt"
|
||||
longtext tags "JSON-Array als Text"
|
||||
longtext avatar
|
||||
int owner_id FK
|
||||
@@ -84,8 +86,15 @@ erDiagram
|
||||
}
|
||||
```
|
||||
|
||||
Drei Eigenheiten, die man kennen muss:
|
||||
Vier Eigenheiten, die man kennen muss:
|
||||
|
||||
- **`crm_leads` fuehrt zwei Bestaende.** `mail_pool = 0` ist der gepflegte
|
||||
Kontaktbestand, `mail_pool = 1` der Vorbereich „Aus Mails" mit den Funden des
|
||||
Postfachabgleichs. Nur der Kontaktbestand erscheint in Kontaktliste,
|
||||
Opportunities, Kalender und Netzwerk; der Vorbereich hat seine eigene Ansicht.
|
||||
Eine Spalte statt einer zweiten Tabelle, damit `crm_mail_links` und
|
||||
`crm_mail_contact_candidates` fuer beide Bestaende gelten
|
||||
([11 Postfachanbindung](11-postfach.md)).
|
||||
- **`tags` ist Text, kein JSON-Typ.** Bewusst, damit es auf aelteren
|
||||
MariaDB-Versionen ohne JSON-Funktionen laeuft. Das Backend serialisiert.
|
||||
- **`crm_lead_connections` speichert das Paar sortiert** (kleinere Id in
|
||||
|
||||
+21
-6
@@ -12,6 +12,8 @@ flowchart TD
|
||||
K --> O["Opportunities<br/>(Startansicht)"]
|
||||
K --> C["Kalender"]
|
||||
K --> L["Kontakte"]
|
||||
L --> L1["Alle Kontakte"]
|
||||
L --> L2["Aus Mails<br/>Vorbereich, Zaehler im Menue"]
|
||||
K --> M["Mails"]
|
||||
M --> M1["Postfach"]
|
||||
M --> M2["Kontakt-Vorschlaege"]
|
||||
@@ -31,9 +33,14 @@ flowchart TD
|
||||
*Postfach* und *Kontakt-Vorschlaege* sind Reiter **innerhalb** der Ansicht
|
||||
*Mails*, keine Menuepunkte - sie gehoeren zusammen und wechseln oft hin und her.
|
||||
|
||||
Der Menuepunkt *Scanner* klappt seine Unterpunkte auf; *Scanner* selbst ist das
|
||||
Dashboard. Bei eingeklappter Leiste (Symbolbreite) erscheinen nur die Symbole,
|
||||
die Unterpunkte kommen mit den Beschriftungen zurueck.
|
||||
Die Menuepunkte *Kontakte* und *Scanner* klappen ihre Unterpunkte auf; der
|
||||
Kopfeintrag ist dabei selbst eine Ansicht (Kontaktliste bzw. Scanner-Dashboard).
|
||||
Bei eingeklappter Leiste (Symbolbreite) erscheinen nur die Symbole, die
|
||||
Unterpunkte kommen mit den Beschriftungen zurueck. Beide Gruppen entstehen aus
|
||||
derselben Struktur `MenuGroup` in `Sidebar.tsx`.
|
||||
|
||||
*Aus Mails* traegt einen Zaehler mit der Anzahl der offenen Funde - er kommt aus
|
||||
`App.tsx`, das die Kontakte ohnehin geladen hat.
|
||||
|
||||
## Zustand und Datenfluss
|
||||
|
||||
@@ -55,7 +62,8 @@ flowchart TB
|
||||
APP --> DATA
|
||||
DATA --> V1["Opportunities"]
|
||||
DATA --> V2["Kalender"]
|
||||
DATA --> V3["Kontakte"]
|
||||
DATA --> V3["Kontakte<br/>mail_pool = false"]
|
||||
DATA --> V5["Aus Mails<br/>mail_pool = true + Kontakte zum Abgleich"]
|
||||
DATA --> V4["Mails<br/>leads + opportunities fuer den Zuordnungsdialog"]
|
||||
NAV --> DF["DictationField<br/>springt nach Whisper"]
|
||||
NAV --> ML["Mails<br/>springt zum Kontakt einer Zuordnung"]
|
||||
@@ -71,6 +79,12 @@ weil sie blaettern, suchen und filtern und dabei nicht die ganze Liste im Speich
|
||||
halten sollen. Kontakte und Opportunities bekommt die Mailansicht dagegen als
|
||||
Props: der Zuordnungsdialog braucht sie beide zum Vorschlagen.
|
||||
|
||||
`/api/leads` liefert beide Bestaende in einem Aufruf; `App.tsx` teilt sie in
|
||||
`leads` (Kontakte) und `mailPoolLeads` (Vorbereich „Aus Mails") auf. Alle
|
||||
anderen Ansichten bekommen ausschliesslich `leads` - ein Fund aus einer Mail soll
|
||||
in keiner Auswahlliste auftauchen. Die Ansicht *Aus Mails* braucht beides: sie
|
||||
vergleicht jeden Fund gegen den Kontaktbestand und markiert Dubletten.
|
||||
|
||||
`goTo('Kontakte', { leadId })` ist der Sprung aus einer Mailzuordnung zum
|
||||
Kontakt. `App.tsx` haelt die Id in `focusLeadId`, die Kontaktansicht oeffnet den
|
||||
Kontakt und meldet mit `onFocusHandled()` zurueck - der Effekt haengt an `leads`
|
||||
@@ -97,11 +111,12 @@ src/
|
||||
audio.ts Aufnahme zu 16-kHz-Mono-PCM
|
||||
imageCompress.ts Bilder vor dem Upload verkleinern
|
||||
components/
|
||||
Sidebar.tsx Menue inkl. aufklappbarer Scanner-Gruppe
|
||||
Sidebar.tsx Menue inkl. aufklappbarer Gruppen (Kontakte, Scanner)
|
||||
Header.tsx Titel, Untertitel, Dark-Mode-Schalter
|
||||
opportunities/ Kachel, Liste, Pipeline (Drag & Drop), Formular
|
||||
calendar/ Monat/Woche/Tag, Termine (Drag & Drop), @-Mentions
|
||||
leads/ Kachel, Liste, Netzwerkdiagramm (d3-force)
|
||||
leads/ Kachel, Liste, Netzwerkdiagramm (d3-force),
|
||||
Vorbereich "Aus Mails" mit Dublettenabgleich
|
||||
mail/ Postfachliste, Detail, Zuordnung, Kontakt-Vorschlaege
|
||||
scanner/ Dashboard, Unpassend, Suchagenten, Boersen-Konten
|
||||
settings/ Reiter: Nutzerverwaltung, Konto, Postfach, Scanner, Whisper
|
||||
|
||||
@@ -128,8 +128,42 @@ erstellen* zwei Kontakte anklicken, um eine Verbindung anzulegen; `Esc` bricht a
|
||||
Suche und Statusfilter wirken auch hier.
|
||||
|
||||
Sobald Kontakte aus dem Postfach uebernommen wurden, erscheint neben dem
|
||||
Statusfilter ein **Herkunftsfilter**: *Aus Mails* zeigt nur die automatisch
|
||||
entstandenen, *Selbst angelegt* nur die von Hand erfassten.
|
||||
Statusfilter ein **Herkunftsfilter**: *Aus Mails uebernommen* zeigt nur die aus
|
||||
dem Postfach stammenden, *Selbst angelegt* nur die von Hand erfassten.
|
||||
|
||||
### Aus Mails
|
||||
|
||||
Der Menuepunkt *Kontakte* klappt zwei Unterpunkte auf: *Alle Kontakte* und
|
||||
*Aus Mails*. Der Zaehler am Menuepunkt sagt, wie viele Funde warten.
|
||||
|
||||
In *Aus Mails* stehen die Personen, die der Postfachabgleich in Mails gefunden
|
||||
hat. **Sie sind noch keine Kontakte**: sie erscheinen nicht in der Kontaktliste,
|
||||
nicht in Opportunities und nicht im Kalender. Jede Karte zeigt alle gefundenen
|
||||
Angaben und darunter, aus welcher Mail sie stammen.
|
||||
|
||||
Zwei Faelle:
|
||||
|
||||
* **Weisse Karte** - die Person ist neu. *In Kontakte übernehmen* legt sie als
|
||||
Kontakt an, mit Herkunft „Mail".
|
||||
* **Gold markierte Karte** - es gibt sie in den Kontakten schon, erkannt an der
|
||||
E-Mail-Adresse oder am Namen. Gold hinterlegte Felder weichen ab; darunter steht
|
||||
durchgestrichen, was bisher im Kontakt stand. *Kontakt aktualisieren* oeffnet
|
||||
die Feldauswahl: angehakt sind zunaechst nur die Luecken, also Felder, die im
|
||||
Kontakt leer sind. Ein Wert, der etwas Gepflegtes ersetzen wuerde, ist als
|
||||
*ueberschreibt* gekennzeichnet und muss bewusst angehakt werden - die Angabe
|
||||
kann aus einer alten Signatur stammen. *Neu anlegen* erzeugt trotz Dublette
|
||||
einen zweiten Kontakt, etwa wenn es zwei Personen mit demselben Namen gibt.
|
||||
|
||||
Weitere Bedienelemente:
|
||||
|
||||
* **Alle neuen übernehmen** legt alle weissen Karten auf einmal als Kontakte an;
|
||||
gold markierte bleiben unberuehrt.
|
||||
* **Eintrag hier behalten** - normalerweise verschwindet ein Eintrag aus *Aus
|
||||
Mails*, sobald er als Kontakt gefuehrt wird. Mit dem Haken bleibt er stehen.
|
||||
* Das **Papierkorbsymbol** entfernt einen Fund. Die Mails bleiben erhalten; taucht
|
||||
die Person in einer neuen Mail auf, erscheint sie hier wieder.
|
||||
* Der Filter waehlt zwischen allen Eintraegen, nur neuen Personen und nur
|
||||
Dubletten.
|
||||
|
||||
## Mails
|
||||
|
||||
@@ -172,7 +206,8 @@ CRM bleibt sie erhalten, damit Zuordnungen nicht verloren gehen.
|
||||
|
||||
Aus Absender, Empfaengerliste und Signatur jeder Mail liest die Anwendung
|
||||
Kontaktdaten. Wer eine echte Mailadresse hat und aus Absender oder
|
||||
Empfaengerliste stammt, wird **automatisch** Kontakt. Alles Unsichere landet
|
||||
Empfaengerliste stammt, landet **automatisch** im Vorbereich
|
||||
[Aus Mails](#aus-mails) - nicht direkt in den Kontakten. Alles Unsichere landet
|
||||
hier - typischerweise Personen, die nur mit Telefonnummer im Text genannt werden.
|
||||
|
||||
Der Balken rechts ist die Zuversicht: je voller und gruener, je verlaesslicher die
|
||||
@@ -181,7 +216,9 @@ Angaben. Das Kennzeichen darunter sagt, woher sie stammen (*Absender*,
|
||||
|
||||
Zwei Knoepfe:
|
||||
|
||||
* **Als Kontakt anlegen** - es entsteht ein neuer Kontakt mit Herkunft „Mail".
|
||||
* **Als Kontakt anlegen** - es entsteht sofort ein richtiger Kontakt mit Herkunft
|
||||
„Mail", nicht nur ein Eintrag in *Aus Mails*: hier hat ja schon jemand
|
||||
hingesehen und entschieden.
|
||||
* **Ergaenzen** (statt „anlegen", wenn **Bereits vorhanden** dransteht) - der
|
||||
vorhandene Kontakt wird um die fehlenden Angaben erweitert. Was schon gepflegt
|
||||
ist, bleibt unangetastet.
|
||||
@@ -342,4 +379,4 @@ nicht zum Benutzerkonto.
|
||||
| Keine Mails sichtbar | steht oben ein Hinweisbalken? Sonst Admin bitten, in *Einstellungen → Postfach* die Verbindung zu testen |
|
||||
| „Erstabgleich laeuft noch" | aeltere Mails werden in mehreren Durchlaeufen nachgeholt, die Liste waechst noch |
|
||||
| Bilder in einer Mail fehlen | *Externe Bilder laden* klicken - sie sind absichtlich gesperrt |
|
||||
| Kontakt-Vorschlaege bleiben leer | Normalfall: sichere Vorschlaege werden automatisch uebernommen und tauchen hier nicht auf |
|
||||
| Kontakt-Vorschlaege bleiben leer | Normalfall: sichere Vorschlaege wandern automatisch nach *Kontakte › Aus Mails* und tauchen hier nicht auf |
|
||||
|
||||
@@ -66,7 +66,7 @@ Alle unter `router.use(requireAuth)`.
|
||||
| --- | --- | --- |
|
||||
| `auth.js` | `POST /login`, `GET /me`, `PATCH /me`, `POST /change-password` | `/login` offen, Rest angemeldet |
|
||||
| `users.js` | `GET /directory`; danach `requireAdmin`: `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id` | `/directory` fuer alle, Rest Admin |
|
||||
| `leads.js` | `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id` | jeder Angemeldete |
|
||||
| `leads.js` | `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id`; Vorbereich: `GET /mail-pool/sources`, `POST /mail-pool/promote`, `POST /:id/promote` | jeder Angemeldete |
|
||||
| `connections.js` | `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id` | jeder Angemeldete |
|
||||
| `events.js` | `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id` | anlegen jeder; aendern/loeschen nur Ersteller oder Admin |
|
||||
| `opportunities.js` | `GET /`, `POST /`, `PATCH /:id`, `DELETE /:id` | aendern/loeschen nur Ersteller, Zustaendiger oder Admin |
|
||||
@@ -107,8 +107,8 @@ Postfachanbindung, fachlich in [11 Postfachanbindung](11-postfach.md).
|
||||
| `graph.js` | Token mit Zwischenspeicher, `graphRequest()` mit Wiederholung bei 429/5xx und Beachtung von `Retry-After`, `testConnection()` |
|
||||
| `html.js` | `htmlToText()` (Zeilen bleiben erhalten, Linkziele werden hinter den Linktext gesetzt), `sanitizeHtml()` |
|
||||
| `extract.js` | `extractCandidates()`, `parsePersonName()`, `parseSignature()`, `AUTO_ADOPT_MIN_CONFIDENCE` |
|
||||
| `store.js` | Zustandszeile, `upsertMessage()`, `insertCandidates()`, `linkMessage()` |
|
||||
| `contacts.js` | `adoptCandidate()`: anlegen oder vorhandenen Kontakt ergaenzen, `markCandidate()` |
|
||||
| `store.js` | Zustandszeile, `upsertMessage()`, `insertCandidates()`, `linkMessage()`, `findLeadByEmail()` je Bestand |
|
||||
| `contacts.js` | `adoptCandidate()`: Eintrag in „Aus Mails" (`target: 'pool'`) oder richtiger Kontakt (`'contacts'`), sonst ergaenzen; `markCandidate()` |
|
||||
| `sync.js` | ein Abgleichslauf, Delta-Seiten, Einzelsperre, Fehlerbehandlung |
|
||||
| `scheduler.js` | Minutentakt, Faelligkeit aus `last_run_at`, kein Zeitfenster |
|
||||
|
||||
@@ -184,6 +184,7 @@ Zusagen, auf die sich der Rest verlaesst:
|
||||
| `setUnauthorizedHandler` | wird bei 401 gerufen; `AuthProvider` haengt sich hier ein |
|
||||
| `ApiError` | Fehler mit `status` |
|
||||
| `authApi`, `usersApi`, `leadsApi`, `opportunitiesApi`, `eventsApi`, `connectionsApi` | typisierte Endpunkte |
|
||||
| `leadsApi.mailPoolSources` / `.promote` / `.promoteMany` | Vorbereich „Aus Mails": Herkunft lesen, einzeln bzw. gesammelt uebernehmen |
|
||||
|
||||
Zwei Feinheiten: Der `Content-Type` wird **nicht** gesetzt, wenn der Body
|
||||
`FormData` ist. Und ein Netzwerkfehler wird zu `ApiError(status 0)` mit
|
||||
@@ -240,6 +241,9 @@ Warum genau diese Konstanten festgelegt sind, steht in
|
||||
| Datei | Inhalt |
|
||||
| --- | --- |
|
||||
| `components/leads/leadUi.tsx` | `inputClass`, `labelClass`, `StatusBadge`, `Avatar`, Statusfarben |
|
||||
| `components/leads/mailPool.ts` | `findMailPoolMatch()`, `diffFields()`, `isConflict()`, `MAIL_POOL_FIELDS` - Dublettenabgleich des Vorbereichs |
|
||||
| `components/leads/MailPoolLeads.tsx` | Ansicht „Aus Mails": Goldmarkierung, Abweichungen, Uebernehmen, Sammeluebernahme |
|
||||
| `components/leads/MailPoolMergeDialog.tsx` | Feldauswahl beim Ergaenzen eines vorhandenen Kontakts |
|
||||
| `components/scanner/scannerUi.tsx` | `cardClass`, Buttonklassen, `MatchBadge`, `MatchStatusBadge`, `PortalStatusPill`, `formatDateTime`, `projectTimestamp` |
|
||||
| `components/mail/mailUi.tsx` | `Badge`, `ConfidenceBar`, Herkunfts- und Statusbeschriftungen, `formatAddresses`, `avatarColor` |
|
||||
| `components/mail/MailBody.tsx` | Mailinhalt im `<iframe sandbox="">`, externe Bilder per CSP gesperrt |
|
||||
|
||||
+6
-4
@@ -13,7 +13,9 @@ Quelltext.
|
||||
| **App-only** | Zugriff auf Microsoft 365 im Namen der Anwendung, nicht eines Benutzers (OAuth-Ablauf *Client Credentials*). Braucht Anwendungsberechtigungen und Administratorzustimmung, aber keinen Login. |
|
||||
| **Cookie-Import** | Uebertragen einer angemeldeten Browsersitzung auf den Scraper, per JSON-Export der Erweiterung *Cookie-Editor*. Landet als `session_state` am Portal. |
|
||||
| **Delta Query** | Graph-Abfrage, die beim naechsten Aufruf nur noch die Aenderungen liefert. Der Fortschritt steckt in einem Verweis (`delta_link`), der in `crm_mail_sync_state` liegt. |
|
||||
| **Kontakt** / Lead | Person im Netzwerk: Firma, Position, Status, Verbindungen. Tabelle `crm_leads`. Im Code durchgaengig `lead`. |
|
||||
| **Kontakt** / Lead | Person im Netzwerk: Firma, Position, Status, Verbindungen. Tabelle `crm_leads` mit `mail_pool = 0`. Im Code durchgaengig `lead`. |
|
||||
| **Aus Mails** / Vorbereich | Zeilen in `crm_leads` mit `mail_pool = 1`: Personen, die der Postfachabgleich gefunden hat. Eigener Menuepunkt unter *Kontakte*, taucht in keiner Auswahlliste auf. Erst das Uebernehmen macht daraus einen Kontakt. |
|
||||
| **Dublette** (Aus Mails) | Ein Fund, zu dem es in den Kontakten schon eine Person gibt - erkannt an der E-Mail-Adresse, sonst am vollen Namen. In der Liste **gold** markiert, abweichende Felder hervorgehoben. |
|
||||
| **Kontakt-Vorschlag** | Aus einer Mail gelesene Person, die noch kein Kontakt ist. Tabelle `crm_mail_contact_candidates`. Wird uebernommen, ergaenzt oder verworfen. |
|
||||
| **Match** / Treffer | Die Bewertung **eines** Projekts fuer **einen** Agenten: Prozentwert, Pro-Liste, Contra-Liste, Status. Tabelle `matches`. Ein Projekt erzeugt so viele Treffer, wie es aktive Agenten gibt. |
|
||||
| **Opportunity** | Geschaeft in Arbeit: Phase, Tagessatz, Laufzeit, Wahrscheinlichkeit, Ansprechpartner. Tabelle `crm_opportunities`. |
|
||||
@@ -31,7 +33,7 @@ Quelltext.
|
||||
| **Whisper** | Das Spracherkennungsmodell fuer das Diktat. Laeuft im Browser, nicht auf dem Server. |
|
||||
| **Zugriffsrichtlinie** | `ApplicationAccessPolicy` in Exchange Online. Begrenzt eine App-only-Berechtigung auf einzelne Postfaecher - ohne sie gilt sie tenant-weit. |
|
||||
| **Zustaendiger** / Owner | Benutzer, dem ein Kontakt oder eine Opportunity zugeordnet ist. Darf sie aendern, auch wenn er sie nicht angelegt hat. |
|
||||
| **Zuversicht** / Confidence | Wert von 0 bis 100 an einem Kontakt-Vorschlag. Ab 70 und mit echter Mailadresse wird automatisch uebernommen. |
|
||||
| **Zuversicht** / Confidence | Wert von 0 bis 100 an einem Kontakt-Vorschlag. Ab 70 und mit echter Mailadresse landet er automatisch im Vorbereich *Aus Mails*. |
|
||||
|
||||
## Herkunft eines Kontakt-Vorschlags
|
||||
|
||||
@@ -47,8 +49,8 @@ Quelltext.
|
||||
| Wert | in der Oberflaeche | Bedeutung |
|
||||
| --- | --- | --- |
|
||||
| `offen` | Offen | wartet auf eine Entscheidung |
|
||||
| `uebernommen` | Uebernommen | es wurde ein Kontakt angelegt |
|
||||
| `dublette` | Ergaenzt | Adresse war vorhanden, der Kontakt wurde erweitert |
|
||||
| `uebernommen` | Uebernommen | es entstand ein Eintrag - im Automatiklauf in *Aus Mails*, von Hand ein Kontakt |
|
||||
| `dublette` | Ergaenzt | die Adresse war im Zielbestand vorhanden, der Eintrag wurde erweitert |
|
||||
| `verworfen` | Verworfen | von Hand abgelehnt |
|
||||
|
||||
## Statuswerte im Scanner
|
||||
|
||||
+61
-6
@@ -192,8 +192,8 @@ flowchart TD
|
||||
F -->|"Person"| C["Vorschlag mit Zuversicht"]
|
||||
|
||||
C --> Z{"E-Mail vorhanden<br/>und Zuversicht ≥ 70?"}
|
||||
Z -->|ja, Automatik an| N["Kontakt anlegen<br/>source = 'Mail'"]
|
||||
Z -->|Adresse existiert| G["vorhandenen Kontakt ergaenzen"]
|
||||
Z -->|ja, Automatik an| N["Eintrag in 'Aus Mails'<br/>mail_pool = 1"]
|
||||
Z -->|"Adresse dort schon"| G["Eintrag ergaenzen"]
|
||||
Z -->|nein| R["bleibt zur Durchsicht"]
|
||||
```
|
||||
|
||||
@@ -216,9 +216,51 @@ Zwei Entscheidungen, die die Trefferqualitaet tragen:
|
||||
`k.mueller@…` wird der Vorname aus der Adresse genommen, der Nachname aber aus
|
||||
dem Anzeigenamen - dort stehen die Umlaute richtig.
|
||||
|
||||
Uebernommene Kontakte tragen `crm_leads.source = 'Mail'`. Das ist die
|
||||
„Unterkategorie": die Kontaktliste hat dafuer einen Herkunftsfilter, ein eigenes
|
||||
Feld war nicht noetig.
|
||||
## Vorbereich „Aus Mails"
|
||||
|
||||
Der Hintergrundlauf schreibt **nicht** in den Kontaktbestand. Was er findet,
|
||||
landet in einem Vorbereich: `crm_leads.mail_pool = 1`. Die Kontaktansicht zeigt
|
||||
nur `mail_pool = 0`, der Vorbereich steht als Unterpunkt *Aus Mails* unter dem
|
||||
Menuepunkt *Kontakte*.
|
||||
|
||||
Der Grund ist Pflegbarkeit: die Kontaktliste ist eine gepflegte Liste, an der
|
||||
Opportunities, Kalender und Netzwerk haengen. Ein Abgleich, der ungefragt jede
|
||||
Adresse aus jeder Mail hineinschreibt, macht sie unbrauchbar - genau daran ist
|
||||
das Altsystem gescheitert. Deshalb sind Funde aus Mails Rohmaterial, bis jemand
|
||||
entscheidet.
|
||||
|
||||
Eine Spalte statt einer zweiten Tabelle, weil `crm_mail_links` und
|
||||
`crm_mail_contact_candidates` per Fremdschluessel auf `crm_leads` zeigen: der
|
||||
Vorbereich behaelt damit seine Mailbezuege, ohne dass es zwei Wege gibt, eine
|
||||
Person mit einer Mail zu verbinden.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P["Aus Mails<br/>mail_pool = 1"] --> D{"Person schon<br/>in den Kontakten?"}
|
||||
D -->|nein| NEU["In Kontakte uebernehmen<br/>Kopie mit mail_pool = 0"]
|
||||
D -->|"ja - Adresse oder Name"| GOLD["gold markiert<br/>Abweichungen hervorgehoben"]
|
||||
GOLD --> M["Felder einzeln in den<br/>vorhandenen Kontakt uebernehmen"]
|
||||
GOLD --> N2["trotzdem neu anlegen"]
|
||||
P --> W["entfernen"]
|
||||
```
|
||||
|
||||
Der Abgleich gegen die Kontakte laeuft im Browser (`mailPool.ts`): erst ueber die
|
||||
E-Mail-Adresse, sonst ueber den vollen Namen. Telefonnummern werden dabei ueber
|
||||
ihre Ziffern verglichen, damit „0221 123-4" und „02211234" nicht als Abweichung
|
||||
erscheinen.
|
||||
|
||||
Beim Uebernehmen sind nur die **Luecken** vorausgewaehlt - Felder, in denen der
|
||||
Kontakt noch nichts hat. Werte, die einen gepflegten Eintrag ersetzen wuerden,
|
||||
sind als *ueberschreibt* gekennzeichnet und muessen bewusst angehakt werden: der
|
||||
Mailfund kann aus einer alten Signatur stammen.
|
||||
|
||||
Uebernommene Kontakte tragen weiterhin `crm_leads.source = 'Mail'` - die
|
||||
Kontaktliste hat dafuer den Herkunftsfilter *Aus Mails uebernommen*.
|
||||
|
||||
Bestandsumstellung: `db.js` verschiebt beim ersten Start alle vorhandenen
|
||||
Kontakte mit `source = 'Mail'` in den Vorbereich. Das laeuft genau einmal,
|
||||
festgehalten im Merker `leads_mail_pool_migrated` in `crm_settings` - sonst
|
||||
wanderten uebernommene Kontakte bei jedem Neustart zurueck.
|
||||
|
||||
## Anzeige des Mailtexts
|
||||
|
||||
@@ -251,8 +293,21 @@ Alle unter `/api/mail`, alle hinter `requireAuth`.
|
||||
| POST | `/messages/:id/links` | alle | an Kontakt und/oder Opportunity haengen |
|
||||
| DELETE | `/links/:id` | alle | Zuordnung loesen |
|
||||
| GET | `/candidates` | alle | Kontakt-Vorschlaege, `status` waehlbar |
|
||||
| POST | `/candidates/:id/adopt` | alle | Kontakt anlegen bzw. ergaenzen |
|
||||
| POST | `/candidates/:id/adopt` | alle | Kontakt anlegen bzw. ergaenzen; `target: 'pool'` legt in „Aus Mails" ab |
|
||||
| POST | `/candidates/:id/reject` | alle | Vorschlag verwerfen |
|
||||
|
||||
Der Vorbereich haengt an den Kontakten und hat seine Endpunkte deshalb unter
|
||||
`/api/leads`:
|
||||
|
||||
| Methode | Pfad | Zweck |
|
||||
| --- | --- | --- |
|
||||
| GET | `/leads/mail-pool/sources` | Herkunft je Eintrag: Mailanzahl, letzter Betreff, Zuversicht |
|
||||
| POST | `/leads/mail-pool/promote` | mehrere Eintraege als neue Kontakte anlegen |
|
||||
| POST | `/leads/:id/promote` | einen Eintrag uebernehmen; mit `target_lead_id` und `fields` in einen vorhandenen Kontakt |
|
||||
|
||||
`keep_pool: true` behaelt den Eintrag in „Aus Mails"; ohne das verschwindet er
|
||||
dort, sobald er als Kontakt gefuehrt wird. In beiden Faellen wandern die
|
||||
Mailverknuepfungen mit an den Zielkontakt.
|
||||
| GET | `/settings` | admin | verstellbare Einstellungen |
|
||||
| POST | `/settings` | admin | Einstellungen speichern |
|
||||
| POST | `/test` | admin | Anmeldung **und** Postfachzugriff pruefen |
|
||||
|
||||
Reference in New Issue
Block a user