feat: Add EspoCRM and Advoware integration for Beteiligte comparison

- Implemented `compare_beteiligte.py` script for comparing Beteiligte structures between EspoCRM and Advoware.
- Created `beteiligte_comparison_result.json` to store comparison results.
- Developed `EspoCRMAPI` service for handling API interactions with EspoCRM.
- Added comprehensive documentation for the EspoCRM API service.
- Included error handling and logging for API operations.
- Enhanced entity management with CRUD operations and search capabilities.
This commit is contained in:
2026-02-07 14:42:58 +00:00
parent 36552903e7
commit e6ab22d5f4
12 changed files with 2143 additions and 1426 deletions

View File

@@ -0,0 +1,286 @@
# Entity-Mapping: EspoCRM CBeteiligte ↔ Advoware Beteiligte
Basierend auf dem Vergleich von:
- **EspoCRM**: CBeteiligte Entity ID `68e4af00172be7924`
- **Advoware**: Beteiligter ID `104860`
## Gemeinsame Felder (direkte Übereinstimmung)
| EspoCRM Feld | Advoware Feld | Typ | Notes |
|--------------|---------------|-----|-------|
| `name` | `name` | string | Vollständiger Name |
| `rechtsform` | `rechtsform` | string | Rechtsform (z.B. "GmbH", "Frau") |
| `id` | `id` | mixed | **Achtung:** EspoCRM=string, Advoware=int |
## Namenfelder
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `firstName` | `vorname` | ✓ Direkt |
| `lastName` | `name` | ✓ Bei Personen |
| `middleName` | - | ❌ Kein direktes Mapping |
| `firmenname` | `name` | ✓ Bei Firmen |
| - | `geburtsname` | ← Nur in Advoware |
| - | `kurzname` | ← Nur in Advoware |
## Kontaktdaten
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `emailAddress` | `emailGesch` | ✓ Geschäftlich |
| `emailAddressData` (array) | `email` | ⚠️ Komplex: Array vs. String |
| `phoneNumber` | `telGesch` | ✓ Geschäftstelefon |
| `phoneNumberData` (array) | `telPrivat` | ⚠️ Komplex |
| - | `mobil` | ← Nur in Advoware |
| - | `faxGesch` / `faxPrivat` | ← Nur in Advoware |
| - | `autotelefon` | ← Nur in Advoware |
| - | `internet` | ← Nur in Advoware |
**Hinweis**: Advoware hat zusätzlich `kommunikation` Array mit strukturierten Kontaktdaten.
## Adressdaten
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `adressensIds` / `adressensNames` | `adressen` (array) | ⚠️ Beziehung |
| - | `strasse` | ← Hauptadresse in Advoware Root |
| - | `plz` | ← Hauptadresse in Advoware Root |
| - | `ort` | ← Hauptadresse in Advoware Root |
| - | `anschrift` | ← Formatierte Adresse |
**Hinweis**:
- EspoCRM: Adressen als Related Entities (IDs/Names)
- Advoware: Hauptadresse im Root-Objekt + `adressen` Array für zusätzliche
## Anrede & Titel
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `salutationName` | `anrede` | ✓ (z.B. "Frau", "Herr") |
| - | `bAnrede` | ← Briefanrede ("Sehr geehrte...") |
| - | `titel` | ← Titel (Dr., Prof., etc.) |
| - | `zusatz` | ← Namenszusatz |
## Geburtsdaten
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `dateOfBirth` | `geburtsdatum` | ✓ Direkt |
| - | `sterbedatum` | ← Nur in Advoware |
| - | `familienstand` | ← Nur in Advoware |
## Handelsregister (für Firmen)
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `handelsregisterNummer` | `handelsRegisterNummer` | ✓ Direkt |
| `handelsregisterArt` (z.B. "HRB") | - | ❌ Nur in EspoCRM |
| - | `registergericht` | ← Nur in Advoware |
## Bankverbindungen
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| `bankverbindungensIds` / Names | `bankkverbindungen` (array) | ⚠️ Related Entity vs. Array |
## Beteiligungen/Akten
| EspoCRM Feld | Advoware Feld | Mapping |
|--------------|---------------|---------|
| - | `beteiligungen` (array) | ← Nur in Advoware |
**Hinweis**: Advoware speichert die Akten-Beteiligungen direkt beim Beteiligten.
## EspoCRM-spezifische Felder
| Feld | Zweck |
|------|-------|
| `betnr` | Beteiligten-Nummer (= Advoware `betNr`) |
| `advowareLastSync` | Zeitstempel der letzten Synchronisation |
| `syncStatus` | Status: "clean", "dirty", "syncing" |
| `disgTyp` | DISC-Persönlichkeitstyp |
| `description` | Notizen/Beschreibung |
| `createdAt` / `createdById` / `createdByName` | Audit-Felder |
| `modifiedAt` / `modifiedById` / `modifiedByName` | Audit-Felder |
| `assignedUserId` / `assignedUserName` | Zuweisungen |
| `teamsIds` / `teamsNames` | Team-Zugehörigkeit |
| `deleted` | Soft-Delete Flag |
| `isFollowed` / `followersIds` | Social Features |
## Advoware-spezifische Felder
| Feld | Zweck |
|------|-------|
| `betNr` | Interne Beteiligten-Nummer |
| `rowId` | Datenbank Row-ID |
| `art` | Beteiligten-Art |
| `angelegtAm` / `angelegtVon` | Erstellt |
| `geaendertAm` / `geaendertVon` | Geändert |
| `kontaktpersonen` (array) | Kontaktpersonen bei Firmen |
| `ePost` / `bea` | Spezielle Kommunikationskanäle |
## Mapping-Strategie
### 1. Person (Natürliche Person)
```python
espocrm_to_advoware = {
'firstName': 'vorname',
'lastName': 'name',
'dateOfBirth': 'geburtsdatum',
'rechtsform': 'rechtsform', # z.B. "Herr", "Frau"
'salutationName': 'anrede',
'emailAddress': 'emailGesch',
'phoneNumber': 'telGesch',
}
```
### 2. Firma (Juristische Person)
```python
espocrm_to_advoware = {
'firmenname': 'name',
'rechtsform': 'rechtsform', # z.B. "GmbH", "AG"
'handelsregisterNummer': 'handelsRegisterNummer',
'emailAddress': 'emailGesch',
'phoneNumber': 'telGesch',
}
```
### 3. Adressen
**EspoCRM → Advoware**:
- Lade Related Entity `Adressen` via `adressensIds`
- Mappe Hauptadresse zu Root-Feldern `strasse`, `plz`, `ort`
- Zusätzliche Adressen in `adressen` Array
**Advoware → EspoCRM**:
- Hauptadresse aus Root-Feldern
- `adressen` Array → Related Entities in EspoCRM
### 4. Kontaktdaten (Komplex)
**EspoCRM `emailAddressData`**:
```json
[
{
"emailAddress": "primary@example.com",
"primary": true,
"optOut": false,
"invalid": false
}
]
```
**Advoware `kommunikation`**:
```json
[
{
"id": 88002,
"kommArt": 0, // 0=Telefon, 1=Email, etc.
"tlf": "0511/12345-60",
"online": false
}
]
```
**Mapping**: Erfordert Transformation basierend auf `kommArt`.
## Sync-Richtungen
### EspoCRM → Advoware (Webhook-getrieben)
1. Webhook empfängt `CBeteiligte` create/update/delete
2. Mappe Felder gemäß Tabelle oben
3. `POST /api/v1/advonet/Beteiligte` (create) oder
`PUT /api/v1/advonet/Beteiligte/{betNr}` (update)
4. Update `advowareLastSync` und `syncStatus` in EspoCRM
### Advoware → EspoCRM (Polling oder Webhook)
1. Überwache Änderungen in Advoware
2. Mappe Felder zurück
3. `PUT /api/v1/CBeteiligte/{id}` in EspoCRM
4. Setze `syncStatus = "clean"`
## Konflikte & Regeln
| Szenario | Regel |
|----------|-------|
| Beide Systeme geändert | Advoware als Master (führendes System) |
| Feld nur in EspoCRM | Ignorieren beim Export, behalten |
| Feld nur in Advoware | Null/Leer in EspoCRM setzen |
| `betnr` vs. `betNr` | Sync-Link: Muss identisch sein |
## ID-Mapping
**Problem**: EspoCRM und Advoware haben unterschiedliche ID-Systeme.
**Lösung**:
- EspoCRM `betnr` Feld = Advoware `betNr`
- Dies ist der Sync-Link zwischen beiden Systemen
- Bei Create in EspoCRM: `betnr` erst nach Advoware-Insert setzen
- Bei Create in Advoware: EspoCRM ID in Custom Field speichern?
## Nächste Schritte
1. **Mapper-Modul erstellen**: `bitbylaw/services/espocrm_mapper.py`
- `map_cbeteiligte_to_advoware(espo_data) -> advo_data`
- `map_advoware_to_cbeteiligte(advo_data) -> espo_data`
2. **Sync-Event-Step implementieren**: `bitbylaw/steps/vmh/beteiligte_sync_event_step.py`
- Subscribe to `vmh.beteiligte.create/update/delete`
- Fetch full entity from EspoCRM
- Transform via Mapper
- Write to Advoware
- Update sync metadata
3. **Testing**:
- Unit Tests für Mapper
- Integration Tests mit Sandbox-Daten
- Konflikt-Szenarien testen
4. **Error Handling**:
- Retry-Logic bei API-Fehlern
- Validation vor dem Sync
- Rollback bei Fehlern?
- Logging aller Sync-Operationen
5. **Performance**:
- Batch-Processing für mehrere Beteiligte
- Rate Limiting beachten
- Caching von Lookup-Daten
## Beispiel-Transformation
### EspoCRM CBeteiligte:
```json
{
"id": "68e4af00172be7924",
"firstName": "Angela",
"lastName": "Mustermanns",
"rechtsform": "Frau",
"emailAddress": "angela@example.com",
"phoneNumber": "0511/12345",
"betnr": 104860,
"handelsregisterNummer": null
}
```
### Advoware Beteiligter:
```json
{
"betNr": 104860,
"vorname": "Angela",
"name": "Mustermanns",
"rechtsform": "Frau",
"anrede": "Frau",
"emailGesch": "angela@example.com",
"telGesch": "0511/12345"
}
```
---
**Generiert am**: 2026-02-07
**Basierend auf**: Real-Daten-Vergleich mit `scripts/compare_beteiligte.py`