feat(cli): init-db setzt CRM-Geschäftsdaten zurück und legt neue Musterdaten an

init-db löscht vor dem Seeding sämtliche CRM-Geschäftsdaten (Kunden,
Kontakte, Projekte, Kontaktadressen, Gesprächsnotizen, Projekt-Zuordnungen)
in FK-sicherer Reihenfolge und legt danach die Musterdaten komplett neu an.
Die Tag-Taxonomie bleibt als Stammdaten erhalten.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
gg committed 2026-09-27 17:15:31 +02:00
1 parent f6588dec79
commit 70f6101761
1 file changed
+567 -165
+567 -165
View File
@@ -2,17 +2,23 @@
Flask CLI-Befehle zur Datenbank-Initialisierung und zum Reset.
Befehle (aufgerufen via Flask CLI):
flask --app wsgi:app init-db -- Schema + Musterdaten anlegen
flask --app wsgi:app init-db -- Schema anlegen/migrieren + Musterdaten
flask --app wsgi:app reset-db -- alles bis auf SysAdmin löschen
init-db ist idempotent: bereits vorhandene Rollen/Kunden werden nicht
doppelt angelegt. reset-db fragt standardmäßig zur Bestätigung; mit --yes
wird die Abfrage übersprungen.
init-db legt das Schema an bzw. migriert es und setzt danach die
CRM-Geschäftsdaten zurück: Sämtliche Kunden samt aller abhängigen Daten
(Kontakte, Projekte, Kontaktadressen, Gesprächsnotizen, Projekt-Zuordnungen)
werden gelöscht und die Musterdaten werden komplett neu angelegt. Die
Tag-Taxonomie (tags) bleibt erhalten, da es sich um Stammdaten handelt.
reset-db fragt standardmäßig zur Bestätigung; mit --yes wird die Abfrage
übersprungen.
:author: Mistral Vibe <vibe@mistral.ai>
:date: 2026-09-02
:version: 1.0
:version: 1.1
"""
from datetime import date, timedelta
import click
from flask import Flask
@@ -33,10 +39,10 @@ def register_cli_commands(app: Flask):
click.echo("Initialisiere kbp_crm.db ...")
from crm.model import checkdb as crm_checkdb
crm_checkdb.checkupdates()
_seed_muster_kunden()
_seed_muster_projekte()
_seed_muster_kontaktadressen()
_seed_muster_calllogs()
click.echo("Lösche vorhandene CRM-Geschäftsdaten ...")
_purge_crm_daten()
click.echo("Lege Musterdaten an ...")
_seed_musterdaten()
click.echo("Fertig.")
@@ -83,180 +89,576 @@ def _seed_app_roles():
session.commit()
def _seed_muster_kunden():
def _purge_crm_daten():
"""
Legt die drei Muster-Kunden an, falls die kunden-Tabelle noch leer ist.
Löscht sämtliche CRM-Geschäftsdaten vor dem Neuanlegen der Musterdaten.
Die Löschreihenfolge beachtet die Fremdschlüsselbeziehungen: erst die
n:m-Verknüpfungen der Gesprächsnotizen, dann die Notizen selbst, danach
die Kontaktadressen, die Projekt-Nutzer-Zuordnungen, die Projekte, die
Kontakte und zuletzt die Kunden. Die Tag-Taxonomie (tags) bleibt
erhalten, da Tags Stammdaten und keine Musterdaten sind.
"""
from crm.model import Kunde
session = dbapp.session
if session.query(Kunde).count() > 0:
return
muster = [
Kunde(name="Musterfirma GmbH", strasse="Musterstraße 1",
plz="12345", ort="Musterstadt", land="Deutschland"),
Kunde(name="Beispiel AG", strasse="Beispielweg 2",
plz="54321", ort="Beispielstadt", land="Deutschland"),
Kunde(name="Test Unternehmen", strasse="Testallee 3",
plz="67890", ort="Testdorf", land="Österreich"),
]
for kunde in muster:
session.add(kunde)
session.commit()
def _seed_muster_projekte():
"""
Legt für jeden Muster-Kunden 1 bis 3 Muster-Projekte an, falls die
projekte-Tabelle noch leer ist.
"""
import random
from crm.model import Kunde, Projekt
session = dbapp.session
if session.query(Projekt).count() > 0:
return
kunden = session.query(Kunde).order_by(Kunde.id).all()
vorlagen = [
("Website-Relaunch", "Neugestaltung der Unternehmenswebsite"),
("E-Commerce Plattform", "Online-Shop Entwicklung"),
("CRM-System", "Kundenbeziehungsmanagement"),
("Mobile App", "iOS- und Android-Entwicklung"),
("SEO-Optimierung", "Suchmaschinenoptimierung"),
]
verantwortliche = [
"Max Mustermann", "Anna Schmidt", "Peter Müller",
"Lisa Bauer", "Thomas Becker",
]
rng = random.Random(42)
vorlagen_idx = 0
for kunde in kunden:
anzahl = rng.randint(1, 3)
for _ in range(anzahl):
name, zweck = vorlagen[vorlagen_idx % len(vorlagen)]
vorlagen_idx += 1
projekt = Projekt()
projekt.kunden_id = kunde.id
projekt.projektname = name
projekt.zweck = zweck
projekt.verantwortlicher = rng.choice(verantwortliche)
projekt.startdatum = "2024-0{monat}-1{tag}".format(
monat=rng.randint(1, 9), tag=rng.randint(0, 9)
from crm.model import (
CallLogTag, CallLog, Kontaktadresse, ProjektUser,
Projekt, Kontakt, Kunde,
)
session.add(projekt)
session = dbapp.session
session.query(CallLogTag).delete(synchronize_session=False)
session.query(CallLog).delete(synchronize_session=False)
session.query(Kontaktadresse).delete(synchronize_session=False)
session.query(ProjektUser).delete(synchronize_session=False)
session.query(Projekt).delete(synchronize_session=False)
session.query(Kontakt).delete(synchronize_session=False)
session.query(Kunde).delete(synchronize_session=False)
session.commit()
def _seed_muster_kontaktadressen():
"""
Legt für jedes Muster-Projekt 1 bis 3 Kontaktadressen an, falls die
kontaktadressen-Tabelle noch leer ist.
"""
import random
from crm.model import Projekt, Kontaktadresse
session = dbapp.session
if session.query(Kontaktadresse).count() > 0:
return
projekte = session.query(Projekt).order_by(Projekt.id).all()
vorlagen = [
("Herr", "Thomas Becker", "Becker Webdesign GmbH", "Geschäftsführer"),
("Frau", "Sandra Klein", "Klein Marketing", "Marketingleiterin"),
("Herr", "Michael Wagner", "Wagner Online Shop", "IT-Leiter"),
("Frau", "Julia Hoffmann", "Hoffmann Consulting", "Beraterin"),
("Herr", "Stefan Roth", "Roth IT Solutions", "Technischer Leiter"),
# ---------------------------------------------------------------------------
# Musterdaten: drei Kunden, jeder mit eigenen Kontakten und je drei
# Telefon-Akquise-Projekten. Die Daten sind bewusst so aufgebaut, dass
# jeder Kontakte- und Kontaktadressen-Name genau einmal in der gesamten
# Datenbank vorkommt und kein Projektname bei zwei Kunden existiert.
# Die Kontaktadressen sind die Akquise-Ziele (angerufene Firmen), die
# Gesprächsnotizen werden daraus automatisch abgeleitet.
# ---------------------------------------------------------------------------
_MUSTERDATEN = [
{
"name": "Nordwerk Metallbau GmbH & Co. KG",
"strasse": "Hamburger Str. 118",
"plz": "21079",
"ort": "Hamburg",
"land": "Deutschland",
"kontakte": [
{
"anrede": "Herr", "name": "Dipl.-Ing. Markus Haverkamp",
"position": "Einkaufsleiter",
"telefon": "+49 40 790 214 66",
"email": "haverkamp@nordwerk-metallbau.de",
"notiz": "Entscheider für Rahmenverträge; telefonisch am besten nach 15 Uhr erreichbar.",
},
{
"anrede": "Frau", "name": "Katrin Ohmsen",
"position": "Vertriebsleitung",
"telefon": "+49 40 790 214 12",
"email": "ohmsen@nordwerk-metallbau.de",
"notiz": "Steuert die laufenden Kampagnen mit uns; erwartet jeden Freitag ein kurzes Zwischen-Reporting.",
},
],
"projekte": [
{
"projektname": "Akquise Stahlträgerprogramm 2026 – Dachstühle und Hallenbau",
"zweck": "Telefonische Neukundenansprache bei Bauunternehmen in Norddeutschland, um das überarbeitete Stahlträgerprogramm zu positionieren. Ziel: 400 geführte Gespräche bis Jahresende, davon 60 Angebotsanfragen.",
"verantwortlicher": "Miriam Falkner",
"startdatum": date(2026, 8, 3),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Torben Block",
"firma": "Block Bauunternehmung GmbH",
"position": "Einkaufsleiter",
"telefon": "+49 40 6899 2140",
"email": "t.block@block-bau-hamburg.de",
"strasse": "Wandsbeker Chaussee 210", "plz": "22089",
"ort": "Hamburg", "land": "Deutschland",
"notiz": "Tauscht die Rahmenverträge jährlich aus; entscheidet gemeinsam mit der Kalkulation.",
},
{
"anrede": "Frau", "name": "Silke Reimers",
"firma": "Reimers Dachbaustoffe Nord",
"position": "Leiterin Kalkulation",
"telefon": "+49 431 556 872",
"email": "s.reimers@reimers-dachbaustoffe.de",
"strasse": "Kieler Str. 31", "plz": "24116",
"ort": "Kiel", "land": "Deutschland",
"notiz": "Plant mehrere Hallenprojekte für das kommende Jahr.",
},
{
"anrede": "Herr", "name": "Gero Timmermann",
"firma": "Timmermann Hallenbau",
"position": "Geschäftsführer",
"telefon": "+49 511 987 4432",
"email": "g.timmermann@timmermann-hallenbau.de",
"strasse": "Am Lindener Hafen 12", "plz": "30453",
"ort": "Hannover", "land": "Deutschland",
"notiz": "Hat großes Interesse an Schnellmontage-Systemen signalisiert.",
},
],
},
{
"projektname": "Neukundengewinnung Zulieferer Ostösterreich",
"zweck": "Kaltakquise bei Stahl- und Metallzulieferern in Wien, Niederösterreich und Oberösterreich zur Erschließung neuer Lieferbeziehungen. Ziel: 250 Kontakte, davon 40 qualifizierte Terminvorschläge für den Außendienst.",
"verantwortlicher": "Dominik Reiss",
"startdatum": date(2026, 8, 17),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Wolfgang Gruber",
"firma": "Gruber Stahlhandel",
"position": "Vertriebsleiter",
"telefon": "+43 1 982 76 11",
"email": "w.gruber@gruber-stahlhandel.at",
"strasse": "Laxenburger Str. 84", "plz": "1100",
"ort": "Wien", "land": "Österreich",
"notiz": "Bezieht Trägerprofile bislang aus Deutschland; offen für österreichische Bezugsquellen.",
},
{
"anrede": "Frau", "name": "Martina Ehgartner",
"firma": "Ehgartner Metallwerke",
"position": "Einkaufsleitung",
"telefon": "+43 732 455 210",
"email": "m.ehgartner@ehgartner-metallwerke.at",
"strasse": "Wiener Str. 145", "plz": "4020",
"ort": "Linz", "land": "Österreich",
"notiz": "Kauft halbjährlich größere Kontingente; Verträge laufen über die Zentrale.",
},
{
"anrede": "Herr", "name": "Kurt Strubreiter",
"firma": "Strubreiter Blechtechnik",
"position": "Inhaber",
"telefon": "+43 2628 741 33",
"email": "office@strubreiter-blechtechnik.at",
"strasse": "Betriebsstr. 7", "plz": "2700",
"ort": "Wiener Neustadt", "land": "Österreich",
"notiz": "Familienbetrieb; entscheidet kurzfristig, wenn die Konditionen stimmen.",
},
],
},
{
"projektname": "Cross-Selling Schutzgitter und Tore im Bestand",
"zweck": "Nachverkauf im Bestandskundenstamm: Kunden der Jahre 2022 bis 2025 werden telefonisch auf das neue Sortiment an Schutzgittern und Industrietoren angesprochen. Ziel: 300 Bestandskunden kontaktiert, Angebotsversand innerhalb von 48 Stunden.",
"verantwortlicher": "Aylin Kocabiyik",
"startdatum": date(2026, 9, 1),
"kontaktadressen": [
{
"anrede": "Frau", "name": "Petra Wilhelmi",
"firma": "Wilhelmi Logistikzentrum",
"position": "Technische Leiterin",
"telefon": "+49 421 338 7762",
"email": "p.wilhelmi@wilhelmi-logistik.de",
"strasse": "Universitätsallee 25", "plz": "28359",
"ort": "Bremen", "land": "Deutschland",
"notiz": "Sanierung der Tore für nächstes Jahr geplant; Budget muss noch beantragt werden.",
},
{
"anrede": "Herr", "name": "Heino Sievers",
"firma": "Sievers Agrartechnik",
"position": "Kundendienstleiter",
"telefon": "+49 441 763 519",
"email": "h.sievers@sievers-agrartechnik.de",
"strasse": "Boschdamm 18", "plz": "26122",
"ort": "Oldenburg", "land": "Deutschland",
"notiz": "Sucht Ersatzteile für ältere Hallentore; auch Interesse an Wartungsverträgen.",
},
{
"anrede": "Herr", "name": "Dietrich Voskamp",
"firma": "Voskamp Landbau",
"position": "Betriebsleiter",
"telefon": "+49 541 229 834",
"email": "d.voskamp@voskamp-landbau.de",
"strasse": "Natruper-Tor-Wall 70", "plz": "49074",
"ort": "Osnabrück", "land": "Deutschland",
"notiz": "Erweitert den Maschinenpark; über Neuanschaffungen entscheidet der Sohn mit.",
},
],
},
],
},
{
"name": "Vitalis Apothekenverbund eG",
"strasse": "Kaisersstr. 77",
"plz": "76133",
"ort": "Karlsruhe",
"land": "Deutschland",
"kontakte": [
{
"anrede": "Frau", "name": "Dr. Ingrid Halbach",
"position": "Mitglied der Geschäftsleitung",
"telefon": "+49 721 508 3390",
"email": "halbach@vitalis-apotheken.de",
"notiz": "Apothekerin; legt besonderen Wert auf seriös formulierte Gesprächsleitfäden.",
},
{
"anrede": "Herr", "name": "Jörn Kiebitz",
"position": "Leiter Filialmarketing",
"telefon": "+49 721 508 3377",
"email": "kiebitz@vitalis-apotheken.de",
"notiz": "Liefert die Filial- und Zielgruppenlisten für unsere Kampagnen; Ansprechpartner für Abstimmungstermine.",
},
],
"projekte": [
{
"projektname": "Terminierung Arzneimittelberatung – Vitalis Filialen Süd",
"zweck": "Telefonische Terminierung kostenpflichtiger Arzneimittelberatungen für Mitgliedsapotheken in Baden-Württemberg und Bayern. Ziel: 120 bestätigte Beratungstermine pro Monat bei maximal drei Absagen je Termin.",
"verantwortlicher": "Tobias Lenzen",
"startdatum": date(2026, 9, 7),
"kontaktadressen": [
{
"anrede": "Frau", "name": "Dr. Marlene Vogt",
"firma": "Vogt-Apotheke Stuttgart-West",
"position": "Inhaberin",
"telefon": "+49 711 563 448",
"email": "m.vogt@vogt-apotheke-stuttgart.de",
"strasse": "Kernerstr. 31", "plz": "70182",
"ort": "Stuttgart", "land": "Deutschland",
"notiz": "Führt die Beratungen selbst durch; Terminvorschläge möglichst vormittags.",
},
{
"anrede": "Herr", "name": "Ingo Pasternak",
"firma": "Pasternak Apotheke",
"position": "Filialleiter",
"telefon": "+49 89 448 2120",
"email": "i.pasternak@pasternak-apotheke.de",
"strasse": "Leopoldstr. 90", "plz": "80802",
"ort": "München", "land": "Deutschland",
"notiz": "Stellvertretung zu Servicezwecken; Zeitfenster dienstags und donnerstags.",
},
{
"anrede": "Frau", "name": "Regine Axt",
"firma": "Axt Rathaus-Apotheke",
"position": "Approbierte Apothekerin",
"telefon": "+49 721 976 512",
"email": "r.axt@axt-apotheke.de",
"strasse": "Am Stadtgarten 2", "plz": "76133",
"ort": "Karlsruhe", "land": "Deutschland",
"notiz": "Neu in der Mitgliedschaft; wünscht ein Einführungsgespräch.",
},
],
},
{
"projektname": "Rückgewinnung inaktiver Bonuskarten-Kunden",
"zweck": "Reaktivierung von rund 5.000 nicht mehr aktiven Bonuskarten-Kunden per Telefon mit anschließender Follow-up-Mail. Ziel: 8 Prozent Rückkehrquote im ersten Quartal, Nachverkauf über die Filialen.",
"verantwortlicher": "Camilla Brugger",
"startdatum": date(2026, 8, 24),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Falk Rüdiger",
"firma": "Sonnen-Apotheke Freiburg",
"position": "Inhaber",
"telefon": "+49 761 388 224",
"email": "f.ruediger@sonnen-apotheke-freiburg.de",
"strasse": "Salzstr. 60", "plz": "79098",
"ort": "Freiburg", "land": "Deutschland",
"notiz": "Wertet die Rückkehrquote monatlich aus; erwartet Zahlen zum Quartalsende.",
},
{
"anrede": "Frau", "name": "Heidrun Clauß",
"firma": "Clauß Apotheke am Markt",
"position": "Inhaberin",
"telefon": "+49 351 469 887",
"email": "h.clauss@clauss-apotheke.de",
"strasse": "Wilsdruffer Str. 24", "plz": "01067",
"ort": "Dresden", "land": "Deutschland",
"notiz": "Ist mit der bisherigen Rücklaufquote unzufrieden und drängt auf eine neue Ansprache.",
},
{
"anrede": "Herr", "name": "Marek Sawatzki",
"firma": "Sawatzki Apotheke Leipzig-Süd",
"position": "Fachapotheker",
"telefon": "+49 341 225 904",
"email": "m.sawatzki@sawatzki-apotheke.de",
"strasse": "Karl-Liebknecht-Str. 105", "plz": "04275",
"ort": "Leipzig", "land": "Deutschland",
"notiz": "Möchte besonders die junge Zielgruppe wieder ansprechen.",
},
],
},
{
"projektname": "Up-Selling Homöopathie-Eigenmarke bei Mitgliedsapotheken",
"zweck": "Telefonische Empfehlung der Eigenmarke 'Vitalis Natur' an alle 180 Mitgliedsapotheken. Ziel: Listung in mindestens 60 Apotheken bis zum Ende des ersten Quartals, durchschnittliches Bestellvolumen von 1.200 Euro je Listung.",
"verantwortlicher": "Nils Gröber",
"startdatum": date(2026, 9, 14),
"kontaktadressen": [
{
"anrede": "Frau", "name": "Brunhilde Kessler",
"firma": "Kessler Engelpforte-Apotheke",
"position": "Inhaberin",
"telefon": "+49 611 344 210",
"email": "b.kessler@kessler-apotheke.de",
"strasse": "Kirchgasse 32", "plz": "65183",
"ort": "Wiesbaden", "land": "Deutschland",
"notiz": "Listet bislang nur zwei Eigenmarken-Produkte; offen für eine Sortimentserweiterung.",
},
{
"anrede": "Herr", "name": "Ali Demirci",
"firma": "Demirci Apotheke Köln",
"position": "Apotheker",
"telefon": "+49 221 559 731",
"email": "a.demirci@demirci-apotheke.de",
"strasse": "Neumarkt 3", "plz": "50667",
"ort": "Köln", "land": "Deutschland",
"notiz": "Fragt nach Verkaufsunterlagen für das Beratungsgespräch.",
},
{
"anrede": "Frau", "name": "Yasmin Trautmann",
"firma": "Trautmann Apotheke",
"position": "Fachapothekerin",
"telefon": "+49 69 664 128",
"email": "y.trautmann@trautmann-apotheke.de",
"strasse": "Berger Str. 175", "plz": "60385",
"ort": "Frankfurt", "land": "Deutschland",
"notiz": "Möchte erst die Ergebnisse einer Testlistung abwarten.",
},
],
},
],
},
{
"name": "Alpina Holzwerke Sepp Achmüller GmbH",
"strasse": "Sepp-Achmüller-Str. 4",
"plz": "6330",
"ort": "Kufstein",
"land": "Österreich",
"kontakte": [
{
"anrede": "Herr", "name": "Sepp Achmüller jun.",
"position": "Geschäftsführer",
"telefon": "+43 5372 6234 18",
"email": "achmiller@alpina-holzwerke.at",
"notiz": "Prüft jede Kampagne vor dem Start persönlich ab; Budgetfreigabe erst nach Vorlage einer Musterabrechnung.",
},
{
"anrede": "Frau", "name": "Elisabeth Steger",
"position": "Vertriebsleiterin",
"telefon": "+43 5372 6234 22",
"email": "steger@alpina-holzwerke.at",
"notiz": "Stimmt Zielgruppen und Anrufregionen mit uns ab; bevorzugt die Abstimmung per Mail.",
},
],
"projekte": [
{
"projektname": "Kampagne Thermofenster Linienserie Alpin",
"zweck": "Telefonische Kampagne bei Baufirmen, Architekten und Holzbaubetrieben für die neue Thermofenster-Serie 'Alpin'. Ziel: 300 geführte Gespräche und 45 Mess- beziehungsweise Angebotstermine.",
"verantwortlicher": "Sandra Wittke",
"startdatum": date(2026, 8, 10),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Jakob Rainer",
"firma": "Rainer Holzbau",
"position": "Geschäftsführer",
"telefon": "+43 512 344 087",
"email": "j.rainer@rainer-holzbau.at",
"strasse": "Museumstr. 13", "plz": "6020",
"ort": "Innsbruck", "land": "Österreich",
"notiz": "Setzt auf Passivhaus-Komponenten; U-Werte müssen im Angebot ausgewiesen sein.",
},
{
"anrede": "Frau", "name": "Notburga Wörgötter",
"firma": "Wörgötter Zimmerei",
"position": "Kalkulation und Einkauf",
"telefon": "+43 662 834 556",
"email": "n.woergoetter@woergoetter-zimmerei.at",
"strasse": "Schallmooser Hauptstr. 44", "plz": "5020",
"ort": "Salzburg", "land": "Österreich",
"notiz": "Benötigt Muster für die Messepräsentation im November.",
},
{
"anrede": "Herr", "name": "Quirin Hofer",
"firma": "Hofer Bau & Planung",
"position": "Architekt",
"telefon": "+49 871 339 421",
"email": "q.hofer@hofer-bau-planung.de",
"strasse": "Ostengasse 15", "plz": "84028",
"ort": "Landshut", "land": "Deutschland",
"notiz": "Plant ein mehrgeschossiges Holzbauprojekt; Fensterelemente sind noch nicht vergeben.",
},
],
},
{
"projektname": "Wiedervorlage-Bestellungen Winterholz-Vorkauf",
"zweck": "Frühzeitige telefonische Vorkaufskampagne für Winterholz: Bestandskunden aus dem Bauhandel anrufen, Bedarf aufnehmen und Wiedervorlagen für die Bestellzeit setzen. Ziel: 65 Prozent Vorab-Buchung des Winterkontingents.",
"verantwortlicher": "Jonas Ade",
"startdatum": date(2026, 9, 21),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Werner Prantner",
"firma": "Prantner Baustoffe",
"position": "Verkauf",
"telefon": "+49 8631 554 22",
"email": "w.prantner@prantner-baustoffe.de",
"strasse": "Simbacher Str. 14", "plz": "84137",
"ort": "Vilsbiburg", "land": "Deutschland",
"notiz": "Bestellt traditionell erst im November; testet dieses Jahr erstmals den Vorkauf.",
},
{
"anrede": "Frau", "name": "Cäcilia Pfeiff",
"firma": "Pfeiff Holzmarkt",
"position": "Inhaberin",
"telefon": "+43 732 915 447",
"email": "c.pfeiff@pfeiff-holzmarkt.at",
"strasse": "Rudolfstr. 12", "plz": "4020",
"ort": "Linz", "land": "Österreich",
"notiz": "Braucht eine verbindliche Lieferzusage bis Anfang Dezember.",
},
{
"anrede": "Herr", "name": "Ambros Hinterseer",
"firma": "Hinterseer Sägewerke",
"position": "Disposition",
"telefon": "+43 5356 622 18",
"email": "a.hinterseer@hinterseer-saegewerke.at",
"strasse": "Jochberger Str. 9", "plz": "6360",
"ort": "Kitzbühel", "land": "Österreich",
"notiz": "Koordiniert den Transport selbst; Übergabetermine müssen exakt stehen.",
},
],
},
{
"projektname": "Lead-Qualifizierung Sägewerksbetriebe Bayern/Tirol",
"zweck": "Anrufcampagne zur Lead-Qualifizierung bei mittelständischen Sägewerken in Bayern und Tirol: Recherche der Ansprechpartner und Bewertung nach Einkaufsvolumen. Ziel: 200 qualifizierte Leads bis Mitte des zweiten Quartals.",
"verantwortlicher": "Pia Merkert",
"startdatum": date(2026, 9, 28),
"kontaktadressen": [
{
"anrede": "Herr", "name": "Hubert Mühlbacher",
"firma": "Mühlbacher Holz",
"position": "Einkaufsleiter",
"telefon": "+49 851 987 350",
"email": "h.muehlbacher@muehlbacher-holz.de",
"strasse": "Innstr. 40", "plz": "94032",
"ort": "Passau", "land": "Deutschland",
"notiz": "Fragt nach Zertifikaten und Lieferkonditionen bereits im Erstgespräch.",
},
{
"anrede": "Frau", "name": "Resi Peringer",
"firma": "Peringer Säge",
"position": "Disposition",
"telefon": "+43 5285 644 21",
"email": "r.peringer@peringer-saege.at",
"strasse": "Dorfstr. 22", "plz": "6290",
"ort": "Mayrhofen", "land": "Österreich",
"notiz": "Familienbetrieb in dritter Generation; die Entscheidung liegt beim Senior.",
},
{
"anrede": "Herr", "name": "Konrad Eder",
"firma": "Eder Holzwerk",
"position": "Betriebsleiter",
"telefon": "+49 8031 554 67",
"email": "k.eder@eder-holzwerk.de",
"strasse": "Chiemseestr. 28", "plz": "83022",
"ort": "Rosenheim", "land": "Deutschland",
"notiz": "Erweitert die Produktionskapazität; sucht Lieferanten mit hoher Flexibilität.",
},
],
},
],
},
]
orte = [
("Industriestr. 12", "45131", "Essen", "Deutschland"),
("Königsallee 5", "40212", "Düsseldorf", "Deutschland"),
("Hauptstr. 88", "50667", "Köln", "Deutschland"),
("Marienplatz 3", "80331", "München", "Deutschland"),
# Betreff-Vorlagen für die automatisch erzeugten Gesprächsnotizen. Passen
# zum Ablauf einer Telefon-Akquise (Erstkontakt -> Termin -> Angebot).
_BETREFFVORLAGEN = [
"Erstkontakt und Bedarfsaufnahme",
"Produktvorstellung am Telefon",
"Terminvorschlag Außendienst",
"Angebot nachreichen",
"Nachfassung zum Angebot",
"Wiedervorlage nach Sommerpause",
"Rückruf vereinbart",
]
rng = random.Random(123)
vorlagen_idx = 0
for projekt in projekte:
anzahl = rng.randint(1, 3)
for _ in range(anzahl):
anrede, name, firma, position = vorlagen[vorlagen_idx % len(vorlagen)]
vorlagen_idx += 1
strasse, plz, ort, land = rng.choice(orte)
ka = Kontaktadresse()
ka.projekt_id = projekt.id
ka.anrede = anrede
ka.name = name
ka.firma = firma
ka.position = position
ka.telefon = "+49 {vorwahl} {nummer}".format(
vorwahl=rng.randint(100, 999),
nummer=rng.randint(100000, 999999)
)
ka.email = name.lower().replace(" ", ".") + "@example.de"
ka.strasse = strasse
ka.plz = plz
ka.ort = ort
ka.land = land
ka.notiz = ""
session.add(ka)
session.commit()
def _seed_muster_calllogs():
"""
Legt für jedes Muster-Projekt 2 bis 5 Gesprächsnotizen an, falls die
call_logs-Tabelle noch leer ist.
"""
import random
from crm.model import Projekt, CallLog
session = dbapp.session
if session.query(CallLog).count() > 0:
return
projekte = session.query(Projekt).order_by(Projekt.id).all()
betreffvorlagen = [
"Anforderungen klären",
"Feedback zum Design",
"Technische Anforderungen",
"Schulungstermin",
"Feature-Request",
"Budgetgespräch",
"Projekt-Review",
]
notizvorlagen = [
"Kunde möchte responsive Design und neue Farbgestaltung. Budget noch offen.",
"Design-Entwürfe wurden positiv aufgenommen. Kleine Änderungen an der Navigation nötig.",
"Payment-Gateway und Versand-Schnittstellen müssen integriert werden.",
"Schulung für 10 Mitarbeiter geplant. Raum noch zu buchen.",
"Kunde wünscht Push-Benachrichtigungen und Offline-Modus.",
# Notiz-Vorlagen für die automatisch erzeugten Gesprächsnotizen. Formuliert
# wie typische Ergebnisse eines Akquise-Telefonats; ein leerer Eintrag
# steht für ein Telefonat ohne Notiz.
_NOTIZVORLAGEN = [
"Gesprächspartner zeigt grundsätzliches Interesse und bittet um die Preisliste als PDF. Das Angebot geht per Mail raus.",
"Kein akuter Bedarf, aber Erweiterungsplanung für das kommende Jahr. Wiedervorlage im Frühjahr gesetzt.",
"Vor-Ort-Termin zur Besichtigung vereinbart; Unterlagen werden vorab per Mail gesendet.",
"Zentrale verweist auf die Beschaffungsabteilung, der konkrete Ansprechpartner wird noch erfragt.",
"Muster werden angefragt; Rückruf zugesagt, sobald die Kollegin aus dem Urlaub zurück ist.",
"Angebot wurde durchgesprochen, das Budget ist noch nicht freigegeben. Nächster Anruf nach der Freigabe.",
"",
]
ansprechpartner = ["Herr Schmidt", "Frau Müller", "Herr Weber", "Frau Bauer", "Herr König"]
def _seed_musterdaten():
"""
Legt die vollständigen Musterdaten an: drei Kunden mit kundenspezifischen
Kontakten, je drei Telefon-Akquise-Projekte pro Kunde, projektbezogene
Kontaktadressen (Akquise-Ziele) und daraus abgeleitete Gesprächsnotizen.
Aufruf nur nach _purge_crm_daten(), da die Funktion die Daten ohne
Existenzprüfung neu anlegt. Die Gesprächsnotizen verweisen auf die
Kontaktadresse, zu der sie gehören (kontaktadresse_id ist Pflichtfeld),
und übernehmen Name und Telefonnummer des Ziels.
"""
import random
from crm.model import Kunde, Kontakt, Projekt, Kontaktadresse, CallLog
session = dbapp.session
# Fester Seed für reproduzierbare Musterdaten (Datum, Uhrzeit, Dauer).
rng = random.Random(456)
for projekt in projekte:
anzahl = rng.randint(2, 5)
for _ in range(anzahl):
for kundendaten in _MUSTERDATEN:
kunde = Kunde(
name=kundendaten["name"],
strasse=kundendaten["strasse"],
plz=kundendaten["plz"],
ort=kundendaten["ort"],
land=kundendaten["land"],
)
session.add(kunde)
# flush liefert die Auto-Increment-ID für die abhängigen Sätze.
session.flush()
for kontaktdaten in kundendaten["kontakte"]:
kontakt = Kontakt(
kunden_id=kunde.id,
anrede=kontaktdaten["anrede"],
name=kontaktdaten["name"],
position=kontaktdaten["position"],
telefon=kontaktdaten["telefon"],
email=kontaktdaten["email"],
notiz=kontaktdaten["notiz"],
)
session.add(kontakt)
for projektdaten in kundendaten["projekte"]:
projekt = Projekt(
kunden_id=kunde.id,
projektname=projektdaten["projektname"],
zweck=projektdaten["zweck"],
verantwortlicher=projektdaten["verantwortlicher"],
startdatum=projektdaten["startdatum"],
)
session.add(projekt)
session.flush()
for adressdaten in projektdaten["kontaktadressen"]:
adresse = Kontaktadresse(
projekt_id=projekt.id,
anrede=adressdaten["anrede"],
name=adressdaten["name"],
firma=adressdaten["firma"],
position=adressdaten["position"],
telefon=adressdaten["telefon"],
email=adressdaten["email"],
strasse=adressdaten["strasse"],
plz=adressdaten["plz"],
ort=adressdaten["ort"],
land=adressdaten["land"],
notiz=adressdaten["notiz"],
)
session.add(adresse)
session.flush()
# Je Kontaktadresse ein bis zwei Gesprächsnotizen, die das
# Akquise-Telefonat mit diesem Ziel dokumentieren.
for _ in range(rng.randint(1, 2)):
log = CallLog()
log.projekt_id = projekt.id
monat = rng.randint(1, 9)
tag = rng.randint(1, 28)
log.datum = "2024-0{m}-{t}".format(m=monat, t=tag) if monat < 10 else "2024-{m}-{t}".format(m=monat, t=tag)
log.kontaktadresse_id = adresse.id
log.datum = date(2026, 9, rng.randint(1, 26))
log.uhrzeit_beginn = "{h:02d}:{min:02d}".format(
h=rng.randint(8, 17), min=rng.randint(0, 59)
)
log.dauer = str(rng.randint(15, 90))
log.betreff = rng.choice(betreffvorlagen)
log.name_ansprechpartner = rng.choice(ansprechpartner)
log.telefon = "+49 {v} {n}".format(
v=rng.randint(100, 999), n=rng.randint(100000, 999999)
log.dauer = str(rng.randint(5, 45))
log.betreff = rng.choice(_BETREFFVORLAGEN)
log.name_ansprechpartner = adresse.name
log.telefon = adresse.telefon
log.notiz = rng.choice(_NOTIZVORLAGEN)
log.wiedervorlage_datum = log.datum + timedelta(
days=rng.randint(7, 28)
)
log.notiz = rng.choice(notizvorlagen)
wiedervorlage_monat = monat + 1 if monat < 12 else 1
log.wiedervorlage_datum = "2024-0{m}-{t}".format(m=wiedervorlage_monat, t=tag) if wiedervorlage_monat < 10 else "2024-{m}-{t}".format(m=wiedervorlage_monat, t=tag)
session.add(log)
session.commit()