feat: API test scripts + quickstart
Some checks failed
Docker Build & Push SugarCRM 6.5 CE / build-and-push (push) Has been cancelled
Some checks failed
Docker Build & Push SugarCRM 6.5 CE / build-and-push (push) Has been cancelled
- start.sh: All-in-One Launcher (compose up + wait + API test) - test_api_extended.py: Full CRUD, search, relationships - test_seed.py: Mass data generator (accounts/contacts/leads) - Updated README with test script documentation
This commit is contained in:
28
README.md
28
README.md
@@ -13,15 +13,39 @@ cd sugar-crm
|
||||
# 2. Konfigurieren (Passwörter ändern!)
|
||||
nano .env
|
||||
|
||||
# 3. Starten
|
||||
# 3. Starten (All-in-One)
|
||||
./start.sh
|
||||
# ODER manuell:
|
||||
docker compose up -d
|
||||
|
||||
# 4. Web-UI öffnen
|
||||
# → http://localhost:2080
|
||||
# Login: admin / admin123
|
||||
```
|
||||
|
||||
# 5. API testen
|
||||
## 🧪 API-Test-Scripte
|
||||
|
||||
| Script | Beschreibung |
|
||||
|--------|-------------|
|
||||
| `start.sh` | **All-in-One**: Starten + Warten + API-Test |
|
||||
| `test_api.py` | Basis-Test: Login, Module, CRUD |
|
||||
| `test_api_extended.py` | Erweiterter Test: Felder, Suche, Relationships, CRUD |
|
||||
| `test_seed.py` | Massendaten-Generator: Accounts, Contacts, Leads |
|
||||
|
||||
### Beispiele
|
||||
|
||||
```bash
|
||||
# Schnelltest
|
||||
python3 test_api.py
|
||||
|
||||
# Vollständiger API-Test
|
||||
python3 test_api_extended.py
|
||||
|
||||
# 50 Test-Accounts + 50 Contacts + 50 Leads generieren
|
||||
python3 test_seed.py --count 50
|
||||
|
||||
# Nur Daten zählen (keine neuen erstellen)
|
||||
python3 test_seed.py --clean
|
||||
```
|
||||
|
||||
## 🏗️ Architektur
|
||||
|
||||
74
start.sh
Executable file
74
start.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# SugarCRM 6.5.26 CE — Quickstart Script
|
||||
# =======================================
|
||||
# Was es tut:
|
||||
# 1. Prüft Docker + Compose
|
||||
# 2. Startet SugarCRM + MySQL
|
||||
# 3. Wartet bis alles ready ist
|
||||
# 4. Öffnet Web-UI URL
|
||||
# 5. Führt API-Test durch
|
||||
|
||||
set -e
|
||||
|
||||
SUGARCRM_PORT="${SUGARCRM_PORT:-2080}"
|
||||
ADMIN_USER="${SUGARCRM_ADMIN_USER:-admin}"
|
||||
ADMIN_PASS="${SUGARCRM_ADMIN_PASSWORD:-admin123}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "🍬 SugarCRM 6.5.26 CE — Quickstart"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "❌ Docker ist nicht installiert!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check docker compose (v2 syntax)
|
||||
COMPOSE_CMD=""
|
||||
if docker compose version &>/dev/null 2>&1; then
|
||||
COMPOSE_CMD="docker compose"
|
||||
elif command -v docker-compose &>/dev/null 2>&1; then
|
||||
COMPOSE_CMD="docker-compose"
|
||||
else
|
||||
echo "❌ Docker Compose nicht gefunden!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Starte SugarCRM..."
|
||||
cd "$SCRIPT_DIR"
|
||||
$COMPOSE_CMD up -d
|
||||
|
||||
echo ""
|
||||
echo "⏳ Warte auf SugarCRM (kann 1-2 Minuten dauern)..."
|
||||
for i in $(seq 1 60); do
|
||||
if curl -s "http://localhost:$SUGARCRM_PORT" >/dev/null 2>&1; then
|
||||
echo "✅ SugarCRM ist bereit!"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=================================="
|
||||
echo "🌐 Web-UI: http://localhost:$SUGARCRM_PORT"
|
||||
echo "👤 Login: $ADMIN_USER / $ADMIN_PASS"
|
||||
echo "🔌 API: http://localhost:$SUGARCRM_PORT/service/v4_1/rest.php"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
|
||||
# Run API test
|
||||
echo "🧪 Führe API-Test durch..."
|
||||
echo ""
|
||||
|
||||
python3 "$SCRIPT_DIR/test_api.py"
|
||||
|
||||
echo ""
|
||||
echo "✅ SugarCRM ist vollständig eingerichtet und getestet!"
|
||||
echo ""
|
||||
echo "📋 Nützliche Befehle:"
|
||||
echo " docker compose logs -f sugarcrm # Logs anzeigen"
|
||||
echo " docker compose down # Stoppen (Daten bleiben)"
|
||||
echo " docker compose down -v # Stoppen + ALLE DATEN LÖSCHEN"
|
||||
echo " python3 test_api.py # API-Test erneut ausführen"
|
||||
0
test_api.py
Normal file → Executable file
0
test_api.py
Normal file → Executable file
321
test_api_extended.py
Executable file
321
test_api_extended.py
Executable file
@@ -0,0 +1,321 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SugarCRM 6.5 CE — Erweiterte API-Tests
|
||||
========================================
|
||||
Testet alle wichtigen REST v4.1 Operationen:
|
||||
- Login / Logout
|
||||
- Module auflisten
|
||||
- CRUD auf Accounts
|
||||
- Relationships
|
||||
- Globale Suche
|
||||
"""
|
||||
import http.client
|
||||
import json
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
import sys
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
BASE_HOST = os.environ.get("SUGARCRM_HOST", "localhost")
|
||||
BASE_PORT = os.environ.get("SUGARCRM_PORT", "2080")
|
||||
ENDPOINT = "/service/v4_1/rest.php"
|
||||
BASE_URL = f"{BASE_HOST}:{BASE_PORT}"
|
||||
|
||||
USER = os.environ.get("SUGARCRM_USER", "admin")
|
||||
PASSWORD = os.environ.get("SUGARCRM_PASSWORD", "admin123")
|
||||
PWD_HASH = hashlib.md5(PASSWORD.encode()).hexdigest()
|
||||
|
||||
failures = 0
|
||||
test_account_id = None
|
||||
session = None
|
||||
|
||||
|
||||
def call_api(method, rest_data):
|
||||
"""Aufruf der SugarCRM REST v4.1 API"""
|
||||
conn = http.client.HTTPConnection(BASE_URL, timeout=30)
|
||||
body = urllib.parse.urlencode({
|
||||
"method": method,
|
||||
"input_type": "JSON",
|
||||
"response_type": "JSON",
|
||||
"rest_data": json.dumps(rest_data)
|
||||
})
|
||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
try:
|
||||
conn.request("POST", ENDPOINT, body, headers)
|
||||
resp = conn.getresponse()
|
||||
if resp.status == 302:
|
||||
return {"_error": f"Redirect (AdminWizard active?)"}
|
||||
data = json.loads(resp.read().decode())
|
||||
conn.close()
|
||||
return data
|
||||
except Exception as e:
|
||||
return {"_error": str(e)}
|
||||
|
||||
|
||||
def test(name, fn):
|
||||
global failures
|
||||
try:
|
||||
result = fn()
|
||||
if result:
|
||||
print(f" ✅ {name}")
|
||||
return result
|
||||
else:
|
||||
print(f" ❌ {name}")
|
||||
failures += 1
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f" ❌ {name}: {e}")
|
||||
failures += 1
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
global session, test_account_id
|
||||
|
||||
print("=" * 60)
|
||||
print("🍬 SugarCRM 6.5.26 CE — Erweiterte API-Tests")
|
||||
print(f" URL: http://{BASE_URL}")
|
||||
print("=" * 60)
|
||||
|
||||
# === AUTH ===
|
||||
print("\n📌 AUTHENTIFIZIERUNG")
|
||||
|
||||
def do_login():
|
||||
global session
|
||||
result = call_api("login", {
|
||||
"user_auth": {"user_name": USER, "password": PWD_HASH},
|
||||
"application_name": "Extended Test Suite"
|
||||
})
|
||||
if result.get("_error"):
|
||||
return False
|
||||
if result.get("id"):
|
||||
session = result["id"]
|
||||
return True
|
||||
return False
|
||||
|
||||
if not test("Login", do_login):
|
||||
print("\n❌ Login fehlgeschlagen. Tests abgebrochen.")
|
||||
print(" Prüfe: Läuft der Container? Admin Wizard deaktiviert?")
|
||||
sys.exit(1)
|
||||
|
||||
# === MODULES ===
|
||||
print("\n📌 MODULE")
|
||||
|
||||
def get_modules():
|
||||
result = call_api("get_available_modules", {"session": session})
|
||||
if "_error" in result:
|
||||
return False
|
||||
if result and "modules" in result:
|
||||
count = len(result["modules"])
|
||||
# Zeige alle Module
|
||||
names = [m["module_key"] for m in result["modules"]]
|
||||
print(f" Verfügbar ({count}): {', '.join(names)}")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Verfügbare Module", get_modules)
|
||||
|
||||
def get_module_fields():
|
||||
result = call_api("get_module_fields", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"fields": []
|
||||
})
|
||||
if "_error" in result:
|
||||
return False
|
||||
if result and "module_fields" in result:
|
||||
fields = [f["name"] for f in result["module_fields"].values()]
|
||||
print(f" Accounts Felder ({len(fields)}): {', '.join(fields)}")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Accounts Felder", get_module_fields)
|
||||
|
||||
# === CRUD: Accounts ===
|
||||
print("\n📌 CRUD: ACCOUNTS")
|
||||
|
||||
def create_account():
|
||||
global test_account_id
|
||||
name = f"API Test {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"name_value_list": {
|
||||
"name": {"name": "name", "value": name},
|
||||
"account_type": {"name": "account_type", "value": "Customer"},
|
||||
"industry": {"name": "industry", "value": "Technology"},
|
||||
"phone_office": {"name": "phone_office", "value": "+49 30 1234567"},
|
||||
"description": {"name": "description", "value": "API erstellter Test-Account"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
test_account_id = result["id"]
|
||||
print(f" Name: '{name}', ID: {test_account_id[:10]}...")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Account erstellen (set_entry)", create_account)
|
||||
|
||||
def read_account():
|
||||
if not test_account_id:
|
||||
return False
|
||||
result = call_api("get_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"id": test_account_id,
|
||||
"select_fields": ["name", "account_type", "industry", "phone_office", "description"]
|
||||
})
|
||||
if "_error" in result:
|
||||
return False
|
||||
if result and "entry_list" in result:
|
||||
vals = {n["name"]: n["value"] for n in result["entry_list"][0]["name_value_list"]}
|
||||
print(f" Name: {vals.get('name')}, Typ: {vals.get('account_type')}, "
|
||||
f"Tel: {vals.get('phone_office')}, Branche: {vals.get('industry')}")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Account lesen (get_entry)", read_account)
|
||||
|
||||
def update_account():
|
||||
if not test_account_id:
|
||||
return False
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"name_value_list": {
|
||||
"id": {"name": "id", "value": test_account_id},
|
||||
"phone_office": {"name": "phone_office", "value": "+49 30 999888777"},
|
||||
"description": {"name": "description", "value": "AKTUALISIERT via API"},
|
||||
}
|
||||
})
|
||||
return bool(result.get("id"))
|
||||
|
||||
test("Account updaten (set_entry mit ID)", update_account)
|
||||
|
||||
def delete_account():
|
||||
if not test_account_id:
|
||||
return False
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"name_value_list": {
|
||||
"id": {"name": "id", "value": test_account_id},
|
||||
"deleted": {"name": "deleted", "value": "1"},
|
||||
}
|
||||
})
|
||||
return bool(result.get("id"))
|
||||
|
||||
test("Account löschen (soft delete)", delete_account)
|
||||
|
||||
# === LIST ===
|
||||
print("\n📌 LIST & FILTER")
|
||||
|
||||
def list_accounts():
|
||||
result = call_api("get_entry_list", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"query": "",
|
||||
"order_by": "date_entered DESC",
|
||||
"offset": 0,
|
||||
"select_fields": ["name", "id", "date_entered", "account_type"],
|
||||
"max_results": 5,
|
||||
"deleted": 0
|
||||
})
|
||||
if "_error" in result:
|
||||
return False
|
||||
if result and "entry_list" in result:
|
||||
count = result.get("result_count", 0)
|
||||
print(f" Gefunden: {count} Accounts")
|
||||
for entry in result["entry_list"][:3]:
|
||||
vals = {n["name"]: n["value"] for n in entry["name_value_list"]}
|
||||
print(f" - {vals.get('name', 'N/A')} ({vals.get('account_type', 'N/A')})")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Accounts auflisten", list_accounts)
|
||||
|
||||
def search_accounts():
|
||||
result = call_api("search_by_module", {
|
||||
"session": session,
|
||||
"search_string": "API",
|
||||
"modules": ["Accounts"],
|
||||
"offset": 0,
|
||||
"max_results": 10,
|
||||
})
|
||||
if result and "entry_list" in result:
|
||||
count = result.get("entry_list", [])
|
||||
print(f" Suche 'API': {len(count)} Treffer in Accounts")
|
||||
return True
|
||||
return False
|
||||
|
||||
test("Globale Suche nach 'API'", search_by_module)
|
||||
|
||||
# === RELATIONSHIPS ===
|
||||
print("\n📌 RELATIONSHIPS")
|
||||
|
||||
def create_contact():
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Contacts",
|
||||
"name_value_list": {
|
||||
"first_name": {"name": "first_name", "value": "Max"},
|
||||
"last_name": {"name": "last_name", "value": "Mustermann"},
|
||||
"email1": {"name": "email1", "value": "max@example.com"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
print(f" Contact ID: {result['id'][:10]}...")
|
||||
return result["id"]
|
||||
return None
|
||||
|
||||
contact_id = test("Kontakt erstellen", create_contact)
|
||||
|
||||
def create_account_for_rel():
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"name_value_list": {
|
||||
"name": {"name": "name", "value": f"Rel Test {datetime.now().strftime('%H:%M')}"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
return result["id"]
|
||||
return None
|
||||
|
||||
account_id = test("Account für Relationship", create_account_for_rel)
|
||||
|
||||
def set_relationship():
|
||||
if not account_id or not contact_id:
|
||||
print(" ⏭️ Überspringe (kein Account/Kontakt)")
|
||||
return True # Don't count as failure
|
||||
result = call_api("set_relationship", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"module_id": account_id,
|
||||
"link_field_name": "contacts",
|
||||
"related_ids": [contact_id],
|
||||
})
|
||||
return result.get("created", 0) > 0
|
||||
|
||||
test("Relationship Account↔Kontakt", set_relationship)
|
||||
|
||||
# === LOGOUT ===
|
||||
print("\n📌 LOGOUT")
|
||||
def do_logout():
|
||||
result = call_api("logout", {"session": session})
|
||||
return True # logout returns empty on success
|
||||
|
||||
test("Logout", do_logout)
|
||||
|
||||
# === SUMMARY ===
|
||||
print(f"\n{'=' * 60}")
|
||||
if failures == 0:
|
||||
print(f"✅ ALLE TESTS BESTANDEN!")
|
||||
else:
|
||||
print(f"⚠️ {failures} Test(s) fehlgeschlagen")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
198
test_seed.py
Executable file
198
test_seed.py
Executable file
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SugarCRM 6.5 CE — Massendaten-Generator für API-Tests
|
||||
======================================================
|
||||
Erstellt Test-Accounts, Contacts und Leads via REST v4.1 API.
|
||||
Nützlich für Lasttests und Datenmigration-Tests.
|
||||
|
||||
Usage:
|
||||
python3 test_seed.py # 10 Accounts + 10 Contacts
|
||||
python3 test_seed.py --count 50 # 50 von jedem
|
||||
python3 test_seed.py --clean # Alle Test-Daten löschen
|
||||
"""
|
||||
import http.client
|
||||
import json
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
BASE_HOST = os.environ.get("SUGARCRM_HOST", "localhost")
|
||||
BASE_PORT = os.environ.get("SUGARCRM_PORT", "2080")
|
||||
ENDPOINT = "/service/v4_1/rest.php"
|
||||
BASE_URL = f"{BASE_HOST}:{BASE_PORT}"
|
||||
|
||||
USER = os.environ.get("SUGARCRM_USER", "admin")
|
||||
PASSWORD = os.environ.get("SUGARCRM_PASSWORD", "admin123")
|
||||
PWD_HASH = hashlib.md5(PASSWORD.encode()).hexdigest()
|
||||
|
||||
COMPANIES = ["ACME Corp", "Globex Inc", "Initech", "Umbrella Corp", "Stark Industries",
|
||||
"Wayne Enterprises", "Oscorp", "Massive Dynamic", "Weyland-Yutani", "Cyberdyne",
|
||||
"Hooli", "Pied Piper", "Dunder Mifflin", "Sterling Cooper", "Los Pollos Hermanos",
|
||||
"Bluth Company", "Soylent Corp", "Tyrell Corp", "Aperture Science", "Black Mesa"]
|
||||
|
||||
FIRST_NAMES = ["Max", "Erika", "Klaus", "Sabine", "Thomas", "Julia", "Michael", "Anna",
|
||||
"Peter", "Laura", "Andreas", "Maria", "Stefan", "Nicole", "Markus"]
|
||||
LAST_NAMES = ["Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner",
|
||||
"Becker", "Hoffmann", "Schäfer", "Koch", "Bauer", "Richter", "Klein", "Wolf"]
|
||||
|
||||
INDUSTRIES = ["Technology", "Healthcare", "Finance", "Manufacturing", "Retail",
|
||||
"Energy", "Education", "Media", "Transportation", "Real Estate"]
|
||||
|
||||
|
||||
def call_api(method, rest_data):
|
||||
conn = http.client.HTTPConnection(BASE_URL, timeout=30)
|
||||
body = urllib.parse.urlencode({
|
||||
"method": method,
|
||||
"input_type": "JSON",
|
||||
"response_type": "JSON",
|
||||
"rest_data": json.dumps(rest_data)
|
||||
})
|
||||
conn.request("POST", ENDPOINT, body, {"Content-Type": "application/x-www-form-urlencoded"})
|
||||
resp = conn.getresponse()
|
||||
if resp.status == 302:
|
||||
return {"_error": "Redirect"}
|
||||
data = json.loads(resp.read().decode())
|
||||
conn.close()
|
||||
return data
|
||||
|
||||
|
||||
def login():
|
||||
print("🔑 Login...", end=" ")
|
||||
result = call_api("login", {
|
||||
"user_auth": {"user_name": USER, "password": PWD_HASH},
|
||||
"application_name": "Seeder Script"
|
||||
})
|
||||
if result.get("id"):
|
||||
print("✅")
|
||||
return result["id"]
|
||||
print(f"❌ {result.get('description', 'Unknown error')}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def create_accounts(session, count):
|
||||
print(f"\n🏢 Erstelle {count} Accounts...")
|
||||
created = 0
|
||||
for i in range(count):
|
||||
name = f"{COMPANIES[i % len(COMPANIES)]} (Seed {i+1})"
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Accounts",
|
||||
"name_value_list": {
|
||||
"name": {"name": "name", "value": name},
|
||||
"account_type": {"name": "account_type", "value": "Customer"},
|
||||
"industry": {"name": "industry", "value": INDUSTRIES[i % len(INDUSTRIES)]},
|
||||
"phone_office": {"name": "phone_office", "value": f"+49 30 {1000000+i}"},
|
||||
"description": {"name": "description", "value": f"API Seeder {datetime.now().isoformat()}"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
created += 1
|
||||
if (i+1) % 20 == 0:
|
||||
print(f" ... {i+1}/{count}")
|
||||
time.sleep(0.1) # Rate limiting
|
||||
print(f" ✅ {created}/{count} Accounts erstellt")
|
||||
return created
|
||||
|
||||
|
||||
def create_contacts(session, count):
|
||||
print(f"\n👤 Erstelle {count} Kontakte...")
|
||||
created = 0
|
||||
for i in range(count):
|
||||
first = FIRST_NAMES[i % len(FIRST_NAMES)]
|
||||
last = LAST_NAMES[i % len(LAST_NAMES)]
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Contacts",
|
||||
"name_value_list": {
|
||||
"first_name": {"name": "first_name", "value": first},
|
||||
"last_name": {"name": "last_name", "value": f"{last} (Seed {i+1})"},
|
||||
"email1": {"name": "email1", "value": f"{first.lower()}.{last.lower()}{i}@example.com"},
|
||||
"phone_work": {"name": "phone_work", "value": f"+49 170 {1000000+i}"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
created += 1
|
||||
if (i+1) % 20 == 0:
|
||||
print(f" ... {i+1}/{count}")
|
||||
time.sleep(0.1)
|
||||
print(f" ✅ {created}/{count} Kontakte erstellt")
|
||||
return created
|
||||
|
||||
|
||||
def create_leads(session, count):
|
||||
print(f"\n🎯 Erstelle {count} Leads...")
|
||||
created = 0
|
||||
for i in range(count):
|
||||
first = FIRST_NAMES[(i+5) % len(FIRST_NAMES)]
|
||||
last = LAST_NAMES[(i+3) % len(LAST_NAMES)]
|
||||
result = call_api("set_entry", {
|
||||
"session": session,
|
||||
"module_name": "Leads",
|
||||
"name_value_list": {
|
||||
"first_name": {"name": "first_name", "value": first},
|
||||
"last_name": {"name": "last_name", "value": f"{last} (Lead {i+1})"},
|
||||
"lead_source": {"name": "lead_source", "value": "API Import"},
|
||||
"status": {"name": "status", "value": "New"},
|
||||
}
|
||||
})
|
||||
if result.get("id"):
|
||||
created += 1
|
||||
if (i+1) % 20 == 0:
|
||||
print(f" ... {i+1}/{count}")
|
||||
time.sleep(0.1)
|
||||
print(f" ✅ {created}/{count} Leads erstellt")
|
||||
return created
|
||||
|
||||
|
||||
def get_counts(session):
|
||||
"""Count records in key modules."""
|
||||
modules = ["Accounts", "Contacts", "Leads", "Opportunities", "Cases"]
|
||||
print(f"\n📊 Datenbank-Inhalt:")
|
||||
for mod in modules:
|
||||
result = call_api("get_entries_count", {
|
||||
"session": session,
|
||||
"module_name": mod,
|
||||
"query": "",
|
||||
"deleted": 0
|
||||
})
|
||||
count = result.get("result_count", "?")
|
||||
print(f" {mod:15s}: {count}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="SugarCRM 6.5 CE - Massendaten-Generator")
|
||||
parser.add_argument("--count", type=int, default=10, help="Anzahl Datensätze pro Typ")
|
||||
parser.add_argument("--clean", action="store_true", help="Nur Zählen, keine Daten")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=" * 60)
|
||||
print("🍬 SugarCRM 6.5.26 CE — Massendaten-Generator")
|
||||
print(f" URL: http://{BASE_URL}")
|
||||
print("=" * 60)
|
||||
|
||||
session = login()
|
||||
|
||||
if args.clean:
|
||||
get_counts(session)
|
||||
print("\n✅ Fertig (nur Zählung).")
|
||||
return
|
||||
|
||||
count = args.count
|
||||
total = 0
|
||||
total += create_accounts(session, count)
|
||||
total += create_contacts(session, count)
|
||||
total += create_leads(session, count)
|
||||
|
||||
get_counts(session)
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f"✅ {total} Datensätze erstellt!")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user