# Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.5.1) **Kontext:** Neuanfang für die Branche **Robotik & Facility Management**. **Ziel:** Ablösung von Google Sheets/CLI durch eine Web-App ("Company Explorer") mit SQLite-Backend. ## 1. Strategische Neuausrichtung | Bereich | Alt (Legacy) | Neu (Robotics Edition) | | :--- | :--- | :--- | | **Daten-Basis** | Google Sheets | **SQLite** (Lokal, performant, filterbar). | | **Ziel-Daten** | Allgemein / Kundenservice | **Robotics-Signale** (SPA-Bereich? Intralogistik? Werkschutz?). | | **Branchen** | KI-Vorschlag (Freitext) | **Strict Mode:** Mapping auf feste CRM-Liste (z.B. "Hotellerie", "Maschinenbau"). | | **Texterstellung** | Pain/Gain Matrix (Service) | **Pain/Gain Matrix (Robotics)**. "Übersetzung" des alten Wissens auf Roboter. | | **Analytics** | Techniker-ML-Modell | **Deaktiviert**. Vorerst keine Relevanz. | | **Operations** | D365 Sync (Broken) | **Excel-Import & Deduplizierung**. Fokus auf Matching externer Listen gegen Bestand. | ## 2. Architektur & Komponenten-Mapping Das System wird in `company-explorer/` neu aufgebaut. Wir lösen Abhängigkeiten zur Root `helpers.py` auf. ### A. Core Backend (`backend/`) | Komponente | Aufgabe & Neue Logik | Prio | | :--- | :--- | :--- | | **Database** | Ersetzt `GoogleSheetHandler`. Speichert Firmen & "Enrichment Blobs". | 1 | | **Importer** | Ersetzt `SyncManager`. Importiert Excel-Dumps (CRM) und Event-Listen. | 1 | | **Deduplicator** | Ersetzt `company_deduplicator.py`. **Kern-Feature:** Checkt Event-Listen gegen DB. Muss "intelligent" matchen (Name + Ort + Web). | 1 | | **Scraper (Base)** | Extrahiert Text von Websites. Basis für alle Analysen. | 1 | | **Signal Detector** | **NEU.** Analysiert Website-Text auf Roboter-Potential.
*Logik:* Wenn Branche = Hotel & Keyword = "Wellness" -> Potential: Reinigungsroboter. | 1 | | **Classifier** | Brancheneinstufung. **Strict Mode:** Prüft gegen `config/allowed_industries.json`. | 2 | | **Marketing Engine** | Ersetzt `generate_marketing_text.py`. Nutzt neue `marketing_wissen_robotics.yaml`. | 3 | ### B. Frontend (`frontend/`) - React * **View 1: Der "Explorer":** DataGrid aller Firmen. Filterbar nach "Roboter-Potential" und Status. * **View 2: Der "Inspector":** Detailansicht einer Firma. Zeigt gefundene Signale ("Hat SPA Bereich"). Manuelle Korrektur-Möglichkeit. * **View 3: "List Matcher":** Upload einer Excel-Liste -> Anzeige von Duplikaten -> Button "Neue importieren". ## 3. Umgang mit Shared Code (`helpers.py` & Co.) Wir kapseln das neue Projekt vollständig ab ("Fork & Clean"). * **Quelle:** `helpers.py` (Root) * **Ziel:** `company-explorer/backend/lib/core_utils.py` * **Aktion:** Wir kopieren nur: * OpenAI/Gemini Wrapper (Retry Logic). * Text Cleaning (`clean_text`, `normalize_string`). * URL Normalization. * **Quelle:** Andere Gemini Apps (`duckdns`, `gtm-architect`, `market-intel`) * **Aktion:** Wir betrachten diese als Referenz. Nützliche Logik (z.B. die "Grit"-Prompts aus `market-intel`) wird explizit in die neuen Service-Module kopiert. ## 4. Datenstruktur (SQLite Schema) ### Tabelle `companies` (Stammdaten) * `id` (PK) * `name` (String) * `website` (String) * `crm_id` (String, nullable - Link zum D365) * `industry_crm` (String - Die "erlaubte" Branche) * `city` (String) * `country` (String - Standard: "DE" oder aus Impressum) * `status` (Enum: NEW, IMPORTED, ENRICHED, QUALIFIED) ### Tabelle `signals` (Roboter-Potential) * `company_id` (FK) * `signal_type` (z.B. "has_spa", "has_large_warehouse", "has_security_needs") * `confidence` (Float) * `proof_text` (Snippet von der Website) ### Tabelle `contacts` (Ansprechpartner) * `id` (PK) * `account_id` (FK -> companies.id) * `gender` (Selection: "männlich", "weiblich") * `title` (Text, z.B. "Dr.") * `first_name` (Text) * `last_name` (Text) * `email` (Email) * `job_title` (Text - Visitenkarte) * `language` (Selection: "De", "En") * `role` (Selection: "Operativer Entscheider", "Infrastruktur-Verantwortlicher", "Wirtschaftlicher Entscheider", "Innovations-Treiber") * `status` (Selection: Siehe Prozess-Status) * `is_primary` (Boolean - Nur einer pro Account) ### Tabelle `industries` (Branchen-Fokus) * `id` (PK) * `name` (String, Unique) * `description` (Text - Abgrenzung/Definition) * `is_focus` (Boolean) * `primary_category_id` (FK -> robotics_categories.id) ### Tabelle `job_role_mappings` (Rollen-Logik) * `id` (PK) * `pattern` (String - Regex oder Text-Pattern für Jobtitles) * `role` (String - Zielrolle im Verkaufsprozess) ### Tabelle `duplicates_log` * Speichert Ergebnisse von Listen-Abgleichen ("Upload X enthielt 20 bekannte Firmen"). ## 5. Phasenplan Umsetzung 1. **Housekeeping:** Archivierung des Legacy-Codes (`_legacy_gsheets_system`). 2. **Setup:** Init `company-explorer` (Backend + Frontend Skeleton). 3. **Foundation:** DB-Schema + "List Matcher" (Deduplizierung ist Prio A für Operations). 4. **Enrichment:** Implementierung des Scrapers + Signal Detector (Robotics). 5. **UI:** React Interface für die Daten. 6. **CRM-Features:** Contacts Management & Marketing Automation Status. ## 6. Spezifikation: Contacts & Marketing Status (v0.5.0) *(Hinzugefügt am 15.01.2026)* **Konzept:** Contacts stehen in 1:n Beziehung zu Accounts. Accounts können einen "Primary Contact" haben. **Datenfelder:** * **Geschlecht:** Selection (männlich / weiblich) * **Vorname:** Text * **Nachname:** Text * **E-Mail:** Type: E-Mail * **Jobtitle:** Text (Titel auf der Visitenkarte) * **Sprache:** Selection (De / En) **Rollen (Funktion im Verkaufsprozess):** * Operativer Entscheider * Infrastruktur-Verantwortlicher * Wirtschaftlicher Entscheider * Innovations-Treiber **Status (Marketing Automation):** * *Manuell:* * Soft Denied (freundliche Absage) * Bounced (E-Mail invalide) * Redirect (ist nicht verantwortlich) * Interested (ist interessiert) * Hard denied (nicht mehr kontaktieren) * *Automatisch:* * Init (Kontakt soll in die Automation hineinlaufen) * 1st Step (Kontakt hat die erste Nachricht erhalten) * 2nd Step (Kontakt hat die zweite Nachricht erhalten) * Not replied (Kontakt hat die dritte Nachricht erhalten und nicht geantwortet) **Branchen-Fokus (Settings):** * **Name:** Eindeutiger Name der Branche (CRM-Mapping). * **Beschreibung:** Textuelle Abgrenzung, was zu dieser Branche gehört. * **Is Focus:** Markiert Branchen, die prioritär bearbeitet werden. * **Primäre Produktkategorie:** Zuordnung einer Robotics-Kategorie (z.B. Hotel -> Cleaning). **Job-Rollen Mapping (Settings):** * **Pattern:** Text-Muster (z.B. "Technischer Leiter", "CTO"), das in Jobtitles gesucht wird. * **Zugeordnete Rolle:** Die funktionale Interpretation (z.B. Operativer Entscheider). ## 7. Historie & Fixes (Jan 2026) * **[UPGRADE] v0.6.0: Notion Single Source of Truth (Jan 19, 2026)** * **Notion SSoT:** Umstellung der Branchenverwaltung (`Industries`) und Robotik-Kategorien auf Notion. Lokale Änderungen im Web-Interface sind für synchronisierte Felder deaktiviert, um die Datenintegrität zu wahren. * **Dynamische Klassifizierung:** Der `ClassificationService` lädt die `allowed_industries` nun direkt aus der Datenbank, die wiederum via Sync-Skript aus Notion befüllt wird. * **Erweiterte Datenmodelle:** Die Datenbank wurde um Felder wie `whale_threshold`, `min_requirement`, `scraper_keywords` und `industry_group` erweitert. * **Sync-Automation:** Bereitstellung von `backend/scripts/sync_notion_industries.py` zur manuellen oder automatisierten Aktualisierung des lokalen Datenbestands. * **[UPGRADE] v0.5.1: Robustness, UI Fixes & Wikipedia Hardening** * **[FIX] Critical DB Schema Mismatch (Jan 15, 2026):** * **Problem:** Die Anwendung stürzte beim Zugriff auf Firmendetails mit `OperationalError: no such column: wiki_verified_empty` ab. * **Ursache:** Eine nicht committete Code-Änderung hatte das DB-Modell in `database.py` erweitert, die physische Datenbank-Datei (`companies_v3_final.db`) war jedoch nicht migriert worden und dazu komplett leer/korrupt. * **Lösung:** Um die Anwendung schnell wieder lauffähig zu bekommen, wurde in `config.py` der `DATABASE_URL` auf einen neuen Dateinamen (`companies_v3_fixed_2.db`) geändert. Dies zwang die App, beim Start eine neue, leere Datenbank mit dem korrekten, aktuellen Schema zu erstellen. Auf eine Datenmigration aus der alten, leeren Datei wurde verzichtet. * **Standort-Fix (4B AG):** Die Backend-Logik wurde an entscheidenden Stellen (`run_analysis_task`, `override_impressum_url`) mit detailliertem Logging versehen und korrigiert, um sicherzustellen, dass `city` und `country` aus Impressums-Daten zuverlässig in die Haupt-Firmentabelle (`companies`) übernommen werden. Dies löst das Problem, dass Standorte im Inspector, aber nicht in der Übersicht angezeigt wurden. * **Wikipedia "Verified Empty":** * **Backend:** Implementierung einer `wiki_verified_empty` Flag in der Datenbank, um Firmen ohne Wikipedia-Eintrag dauerhaft zu markieren. Der `DiscoveryService` überspringt diese Einträge nun. * **Frontend:** Ein neuer Button im Inspector erlaubt das manuelle Setzen dieses Status. * **Robuste Wikipedia-Suche:** Die Namens-Normalisierungslogik aus dem Legacy-System wurde vollständig in den `DiscoveryService` reintegriert. Dies ermöglicht eine deutlich höhere Trefferquote bei Firmennamen mit unterschiedlichen Rechtsformen (z.B. "Therme Erding Service GmbH" -> "Therme Erding"). * **UI-Fix (Sort & View):** Die Frontend-Tabellen (`CompanyTable`, `ContactsTable`) wurden grundlegend überarbeitet, um die zuvor fehlenden **Sortier-Dropdowns** und **Grid/List-View-Toggles** korrekt und zuverlässig anzuzeigen. Die Standard-Sortierung ist nun "Alphabetisch". * **[UPGRADE] v0.5.0: Contacts, Settings & UI Overhaul** * **Contacts Management:** * Implementierung einer globalen Kontakt-Liste (`ContactsTable`) mit Such- und Filterfunktionen. * Detail-Bearbeitung von Kontakten direkt im Inspector (Click-to-Edit). * Bulk-Import-Funktion für Kontakte (CSV-basiert) mit automatischer Firmen-Erstellung und Dubletten-Prüfung (E-Mail). * Erweiterte Felder: Akademischer Titel, differenzierte Rollen (Operativ, Strategisch etc.) und Marketing-Status. * **UI Modernisierung:** * **Light Mode:** Vollständige Unterstützung für Hell/Dunkel-Modus mit Toggle im Header. * **Grid View:** Umstellung der Firmen-Liste auf eine kartenbasierte Ansicht (analog zu Kontakten). * **Responsive Design:** Optimierung des Inspectors und der Navigation für mobile Endgeräte. * **Erweiterte Settings:** * Neue Konfigurations-Tabs für **Branchen** (Industries) und **Job-Rollen**. * CRUD-Operationen für Branchen (inkl. Auto-Increment bei Namensgleichheit). * **Bugfixes:** * Korrektur des API-Pfads für manuelle Impressum-Updates. * Stabilisierung der Datenbank-Logik bei Unique-Constraints. * Optimierung der Anzeige von "Unknown, DE" in der Firmenliste (wird nun ausgeblendet, solange keine Stadt bekannt ist). * **[UPGRADE] v0.4.0: Export & Manual Impressum** * **JSON Export:** Erweiterung der Detailansicht um einen "Export JSON"-Button, der alle Unternehmensdaten (inkl. Anreicherungen und Signale) herunterlädt. * **Zeitstempel:** Anzeige des Erstellungsdatums für jeden Anreicherungsdatensatz (Wikipedia, AI Dossier, Impressum) in der Detailansicht. * **Manuelle Impressum-URL:** Möglichkeit zur manuellen Eingabe einer Impressum-URL in der Detailansicht, um die Extraktion von Firmendaten zu erzwingen. * **Frontend-Fix:** Behebung eines Build-Fehlers (`Unexpected token`) in `Inspector.tsx` durch Entfernung eines duplizierten JSX-Blocks. * **[UPGRADE] v2.6.2: Report Completeness & Edit Mode** * **Edit Hard Facts:** Neue Funktion in Phase 1 ("Edit Raw Data") erlaubt die manuelle Korrektur der extrahierten technischen JSON-Daten. * **Report-Update:** Phase 5 Prompt wurde angepasst, um explizit die Ergebnisse aus Phase 2 (ICPs & Data Proxies) im finalen Report aufzuführen. * **Backend-Fix:** Korrektur eines Fehlers beim Speichern von JSON-Daten, der auftrat, wenn Datenbank-Inhalte als Strings vorlagen. * **[UPGRADE] v2.6.1: Stability & UI Improvements** * **White Screen Fix:** Robuste Absicherung des Frontends gegen `undefined`-Werte beim Laden älterer Sitzungen (`optional chaining`). * **Session Browser:** Komplettes Redesign der Sitzungsübersicht zu einer übersichtlichen Listenansicht mit Icons (Reinigung/Service/Transport/Security). * **URL-Anzeige:** Die Quell-URL wird nun als dedizierter Link angezeigt und das Projekt automatisch basierend auf dem erkannten Produktnamen umbenannt. * **[UPGRADE] v2.6: Rich Session Browser** * **Neues UI:** Die textbasierte Liste für "Letzte Sitzungen" wurde durch eine dedizierte, kartenbasierte UI (`SessionBrowser.tsx`) ersetzt. * **Angereicherte Daten:** Jede Sitzungskarte zeigt nun den Produktnamen, die Produktkategorie (mit Icon), eine Kurzbeschreibung und einen Thumbnail-Platzhalter an. * **Backend-Anpassung:** Die Datenbankabfrage (`gtm_db_manager.py`) wurde erweitert, um diese Metadaten direkt aus der JSON-Spalte zu extrahieren und an das Frontend zu liefern. * **Verbesserte UX:** Deutlich verbesserte Übersichtlichkeit und schnellere Identifikation von vergangenen Analysen. * **[UPGRADE] v2.5: Hard Fact Extraction** * **Phase 1 Erweiterung:** Implementierung eines sekundären Extraktions-Schritts für "Hard Facts" (Specs). * **Strukturiertes Daten-Schema:** Integration von `templates/json_struktur_roboplanet.txt`. * **Normalisierung:** Automatische Standardisierung von Einheiten (Minuten, cm, kg, m²/h). * **Frontend Update:** Neue UI-Komponente zur Anzeige der technischen Daten (Core Data, Layer, Extended Features). * **Sidebar & Header:** Update auf "ROBOPLANET v2.5". * **[UPGRADE] v2.4:** * Dokumentation der Kern-Engine (`helpers.py`) mit Dual SDK & Hybrid Image Generation. * Aktualisierung der Architektur-Übersicht und Komponenten-Beschreibungen. * Versionierung an den aktuellen Code-Stand (`v2.4.0`) angepasst. * **[UPGRADE] v2.3:** * Einführung der Session History (Datenbank-basiert). * Implementierung von Markdown-Cleaning (Stripping von Code-Blocks). * Prompt-Optimierung für tabellarische Markdown-Ausgaben in Phase 5. * Markdown-File Import Feature. ## 8. Eingesetzte Prompts (Account-Analyse) Dieser Abschnitt dokumentiert die Prompts, die im Backend des **Company Explorers** zur automatisierten Analyse von Unternehmensdaten eingesetzt werden. ### 8.1 Impressum Extraktion (aus `services/scraping.py`) Dient der Extraktion strukturierter Stammdaten aus dem rohen Text der Impressums-Seite. **Prompt:** ```python prompt = f""" Extract the official company details from this German 'Impressum' text. Return JSON ONLY. Keys: 'legal_name', 'street', 'zip', 'city', 'country_code', 'email', 'phone', 'ceo_name', 'vat_id'. 'country_code' should be the two-letter ISO code (e.g., "DE", "CH", "AT"). If a field is missing, use null. Text: {raw_text} """ ``` **Variablen:** * **`raw_text`**: Der bereinigte HTML-Text der gefundenen Impressums-URL (max. 10.000 Zeichen). --- ### 8.2 Robotics Potential Analyse (aus `services/classification.py`) Der Kern-Prompt zur Bewertung des Automatisierungspotenzials. Er fasst das Geschäftsmodell zusammen, prüft auf physische Infrastruktur und bewertet spezifische Robotik-Anwendungsfälle. **Prompt:** ```python prompt = f""" You are a Senior B2B Market Analyst for 'Roboplanet', a specialized robotics distributor. Your task is to analyze the target company based on their website text and create a concise **Dossier**. --- TARGET COMPANY --- Name: {company_name} Website Content (Excerpt): {website_text[:20000]} --- ALLOWED INDUSTRIES (STRICT) --- You MUST assign the company to exactly ONE of these industries. If unsure, choose the closest match or "Sonstige". {json.dumps(self.allowed_industries, ensure_ascii=False)} --- ANALYSIS PART 1: BUSINESS MODEL --- 1. Identify the core products/services. 2. Summarize in 2-3 German sentences: What do they do and for whom? (Target: "business_model") --- ANALYSIS PART 2: INFRASTRUCTURE & POTENTIAL (Chain of Thought) --- 1. **Infrastructure Scan:** Look for evidence of physical assets like *Factories, Large Warehouses, Production Lines, Campuses, Hospitals*. 2. **Provider vs. User Check:** - Does the company USE this infrastructure (Potential Customer)? - Or do they SELL products for it (Competitor/Partner)? - *Example:* "Cleaning" -> Do they sell soap (Provider) or do they have a 50,000sqm factory (User)? 3. **Evidence Extraction:** Extract 1-2 key sentences from the text proving this infrastructure. (Target: "infrastructure_evidence") --- ANALYSIS PART 3: SCORING (0-100) --- Based on the identified infrastructure, score the potential for these categories: {category_guidance} --- OUTPUT FORMAT (JSON ONLY) --- {{ "industry": "String (from list)", "business_model": "2-3 sentences summary (German)", "infrastructure_evidence": "1-2 key sentences proving physical assets (German)", "potentials": {{ "cleaning": {{ "score": 0-100, "reason": "Reasoning based on infrastructure." }},\ "transport": {{ "score": 0-100, "reason": "Reasoning based on logistics volume." }},\ "security": {{ "score": 0-100, "reason": "Reasoning based on perimeter/assets." }},\ "service": {{ "score": 0-100, "reason": "Reasoning based on guest interaction." }}\ }}\ }} """ ``` **Variablen:** * **`company_name`**: Offizieller Name des Zielunternehmens zur korrekten Identifikation im Dossier. * **`website_text`**: Der gescrapte Text der Hauptseite (max. 20.000 Zeichen), der als primäre Informationsquelle dient. * **`allowed_industries`**: Eine JSON-Liste der gültigen Branchen. Diese wird dynamisch aus der Datenbanktabelle `industries` geladen (synchronisiert aus Notion). Erzwingt ein sauberes CRM-Mapping. * **`category_guidance`**: Dynamisch generierte Definitionen und Scoring-Anweisungen für die Robotik-Kategorien. Ermöglicht die Anpassung der KI-Logik über Notion/Settings ohne Code-Änderung. ## 9. Notion Integration (Single Source of Truth) Das System nutzt Notion als zentrales Steuerungselement für strategische Definitionen. ### 9.1 Datenfluss 1. **Definition:** Branchen und Robotik-Kategorien werden in Notion gepflegt (Whale Thresholds, Keywords, Definitionen). 2. **Synchronisation:** Das Skript `sync_notion_industries.py` zieht die Daten via API und führt einen Upsert in die lokale SQLite-Datenbank aus. 3. **App-Nutzung:** Das Web-Interface zeigt diese Daten schreibgeschützt an. Der `ClassificationService` nutzt sie als "System-Anweisung" für das LLM. ### 9.2 Technische Details * **Notion Token:** Muss in `/app/notion_token.txt` (Container-Pfad) hinterlegt sein. * **DB-Mapping:** Die Zuordnung erfolgt primär über die `notion_id`, sekundär über den Namen, um Dubletten bei der Migration zu vermeiden.