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:
296
bitbylaw/scripts/README.md
Normal file
296
bitbylaw/scripts/README.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Beteiligte Structure Comparison Tool
|
||||
|
||||
## Purpose
|
||||
|
||||
This helper script fetches entity data from both **EspoCRM** and **Advoware** to compare their data structures. This helps understand:
|
||||
|
||||
- What fields exist in each system
|
||||
- How field names differ
|
||||
- Potential field mappings for synchronization
|
||||
- Data type differences
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cd /opt/motia-app
|
||||
|
||||
# Basic usage: Compare by EspoCRM ID (will auto-search in Advoware)
|
||||
python bitbylaw/scripts/compare_beteiligte.py <espocrm_entity_id>
|
||||
|
||||
# Advanced: Specify both IDs
|
||||
python bitbylaw/scripts/compare_beteiligte.py <espocrm_entity_id> <advoware_id>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Example 1: Fetch from EspoCRM and search in Advoware by name
|
||||
python bitbylaw/scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc
|
||||
|
||||
# Example 2: Fetch from both systems by ID
|
||||
python bitbylaw/scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc 12345
|
||||
|
||||
# Example 3: Using the virtual environment
|
||||
source python_modules/bin/activate
|
||||
python bitbylaw/scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Make sure these are set in `.env` or environment:
|
||||
|
||||
```bash
|
||||
# EspoCRM
|
||||
ESPOCRM_API_BASE_URL=https://crm.bitbylaw.com/api/v1
|
||||
ESPOCRM_MARVIN_API_KEY=your_api_key_here
|
||||
|
||||
# Advoware
|
||||
ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
|
||||
ADVOWARE_API_KEY=your_base64_encoded_key
|
||||
ADVOWARE_USER=your_user
|
||||
ADVOWARE_PASSWORD=your_password
|
||||
ADVOWARE_KANZLEI=your_kanzlei
|
||||
ADVOWARE_DATABASE=your_database
|
||||
# ... (see config.py for all required vars)
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
```bash
|
||||
pip install aiohttp redis python-dotenv requests
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
The script produces:
|
||||
|
||||
### 1. Console Output
|
||||
|
||||
```
|
||||
================================================================================
|
||||
BETEILIGTE STRUCTURE COMPARISON TOOL
|
||||
================================================================================
|
||||
|
||||
EspoCRM Entity ID: 64a3f2b8c9e1234567890abc
|
||||
|
||||
Environment Check:
|
||||
----------------------------------------
|
||||
ESPOCRM_API_BASE_URL: https://crm.bitbylaw.com/api/v1
|
||||
ESPOCRM_API_KEY: ✓ Set
|
||||
ADVOWARE_API_BASE_URL: https://www2.advo-net.net:90/
|
||||
ADVOWARE_API_KEY: ✓ Set
|
||||
|
||||
================================================================================
|
||||
ESPOCRM - Fetching Beteiligter
|
||||
================================================================================
|
||||
|
||||
Trying entity type: Beteiligte
|
||||
|
||||
✓ Success! Found in Beteiligte
|
||||
|
||||
Entity Structure:
|
||||
--------------------------------------------------------------------------------
|
||||
{
|
||||
"id": "64a3f2b8c9e1234567890abc",
|
||||
"name": "Max Mustermann",
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"email": "max@example.com",
|
||||
"phone": "+49123456789",
|
||||
...
|
||||
}
|
||||
|
||||
================================================================================
|
||||
ADVOWARE - Fetching Beteiligter
|
||||
================================================================================
|
||||
|
||||
Searching by name: Max Mustermann
|
||||
Trying endpoint: /contacts
|
||||
|
||||
✓ Found 2 results
|
||||
|
||||
Search Results:
|
||||
--------------------------------------------------------------------------------
|
||||
[
|
||||
{
|
||||
"id": 12345,
|
||||
"full_name": "Max Mustermann",
|
||||
"email": "max@example.com",
|
||||
...
|
||||
}
|
||||
]
|
||||
|
||||
================================================================================
|
||||
STRUCTURE COMPARISON
|
||||
================================================================================
|
||||
|
||||
EspoCRM Fields (25):
|
||||
----------------------------------------
|
||||
id (str)
|
||||
name (str)
|
||||
firstName (str)
|
||||
lastName (str)
|
||||
email (str)
|
||||
phone (str)
|
||||
...
|
||||
|
||||
Advoware Fields (30):
|
||||
----------------------------------------
|
||||
id (int)
|
||||
full_name (str)
|
||||
email (str)
|
||||
phone_number (str)
|
||||
...
|
||||
|
||||
Common Fields (5):
|
||||
----------------------------------------
|
||||
✓ id
|
||||
✓ email
|
||||
✗ phone
|
||||
EspoCRM: +49123456789
|
||||
Advoware: 0123456789
|
||||
|
||||
EspoCRM Only (20):
|
||||
----------------------------------------
|
||||
firstName
|
||||
lastName
|
||||
...
|
||||
|
||||
Advoware Only (25):
|
||||
----------------------------------------
|
||||
full_name
|
||||
phone_number
|
||||
...
|
||||
|
||||
Potential Field Mappings:
|
||||
----------------------------------------
|
||||
firstName → first_name
|
||||
lastName → last_name
|
||||
email → email
|
||||
phone → phone_number
|
||||
...
|
||||
|
||||
================================================================================
|
||||
Comparison saved to: /opt/motia-app/bitbylaw/scripts/beteiligte_comparison_result.json
|
||||
================================================================================
|
||||
```
|
||||
|
||||
### 2. JSON Output File
|
||||
|
||||
Saved to `bitbylaw/scripts/beteiligte_comparison_result.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"espocrm_data": {
|
||||
"id": "64a3f2b8c9e1234567890abc",
|
||||
"name": "Max Mustermann",
|
||||
...
|
||||
},
|
||||
"advoware_data": {
|
||||
"id": 12345,
|
||||
"full_name": "Max Mustermann",
|
||||
...
|
||||
},
|
||||
"comparison": {
|
||||
"espo_fields": ["id", "name", "firstName", ...],
|
||||
"advo_fields": ["id", "full_name", "email", ...],
|
||||
"common": ["id", "email"],
|
||||
"espo_only": ["firstName", "lastName", ...],
|
||||
"advo_only": ["full_name", "phone_number", ...],
|
||||
"suggested_mappings": [
|
||||
["firstName", "first_name"],
|
||||
["lastName", "last_name"],
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. EspoCRM Fetch
|
||||
|
||||
The script tries multiple entity types to find the data:
|
||||
- `Beteiligte` (custom VMH entity)
|
||||
- `Contact` (standard)
|
||||
- `Account` (standard)
|
||||
- `Lead` (standard)
|
||||
|
||||
### 2. Advoware Fetch
|
||||
|
||||
**By ID (if provided):**
|
||||
- Tries: `/contacts/{id}`, `/parties/{id}`, `/clients/{id}`
|
||||
|
||||
**By Name (if EspoCRM data available):**
|
||||
- Searches: `/contacts?search=...`, `/parties?search=...`, `/clients?search=...`
|
||||
|
||||
### 3. Comparison
|
||||
|
||||
- Lists all fields from both systems
|
||||
- Identifies common fields (same name)
|
||||
- Shows values for common fields
|
||||
- Suggests mappings based on naming patterns
|
||||
- Exports full comparison to JSON
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "ESPOCRM_API_KEY not set"
|
||||
|
||||
```bash
|
||||
# Check if .env exists and has the key
|
||||
cat .env | grep ESPOCRM_MARVIN_API_KEY
|
||||
|
||||
# Or set it manually
|
||||
export ESPOCRM_MARVIN_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
### "Authentication failed - check API key"
|
||||
|
||||
1. Verify API key in EspoCRM admin panel
|
||||
2. Check API User permissions
|
||||
3. Ensure API User has access to entity type
|
||||
|
||||
### "Entity not found"
|
||||
|
||||
- Check if entity ID is correct
|
||||
- Verify entity type exists in EspoCRM
|
||||
- Check API User permissions for that entity
|
||||
|
||||
### "Advoware token error"
|
||||
|
||||
- Verify all Advoware credentials in `.env`
|
||||
- Check HMAC signature generation
|
||||
- Ensure API key is base64 encoded
|
||||
- Test token generation separately
|
||||
|
||||
## Next Steps
|
||||
|
||||
After running this script:
|
||||
|
||||
1. **Review JSON output** - Check `beteiligte_comparison_result.json`
|
||||
2. **Define mappings** - Create mapping table based on suggestions
|
||||
3. **Implement mapper** - Create transformation functions
|
||||
4. **Test sync** - Use mappings in sync event step
|
||||
|
||||
Example mapping implementation:
|
||||
|
||||
```python
|
||||
def map_espocrm_to_advoware(espo_entity: dict) -> dict:
|
||||
"""Transform EspoCRM Beteiligter to Advoware format"""
|
||||
return {
|
||||
'first_name': espo_entity.get('firstName'),
|
||||
'last_name': espo_entity.get('lastName'),
|
||||
'email': espo_entity.get('email'),
|
||||
'phone_number': espo_entity.get('phone'),
|
||||
# Add more mappings based on comparison...
|
||||
}
|
||||
```
|
||||
|
||||
## Related Files
|
||||
|
||||
- [services/espocrm.py](../services/espocrm.py) - EspoCRM API client
|
||||
- [services/advoware.py](../services/advoware.py) - Advoware API client
|
||||
- [services/ESPOCRM_SERVICE.md](../services/ESPOCRM_SERVICE.md) - EspoCRM docs
|
||||
- [config.py](../config.py) - Configuration
|
||||
399
bitbylaw/scripts/beteiligte_comparison_result.json
Normal file
399
bitbylaw/scripts/beteiligte_comparison_result.json
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"espocrm_data": {
|
||||
"id": "68e4af00172be7924",
|
||||
"name": "dasdas dasdasdas dasdasdas",
|
||||
"deleted": false,
|
||||
"salutationName": null,
|
||||
"rechtsform": "GmbH",
|
||||
"firmenname": "Filli llu GmbH",
|
||||
"firstName": "dasdasdas",
|
||||
"lastName": "dasdas",
|
||||
"dateOfBirth": null,
|
||||
"description": null,
|
||||
"emailAddress": "meier@meier.de",
|
||||
"phoneNumber": null,
|
||||
"createdAt": "2025-10-07 06:11:12",
|
||||
"modifiedAt": "2026-01-23 21:58:41",
|
||||
"betnr": 1234,
|
||||
"advowareLastSync": null,
|
||||
"syncStatus": "clean",
|
||||
"handelsregisterNummer": "12244546",
|
||||
"handelsregisterArt": "HRB",
|
||||
"disgTyp": "Unbekannt",
|
||||
"middleName": "dasdasdas",
|
||||
"emailAddressIsOptedOut": false,
|
||||
"emailAddressIsInvalid": false,
|
||||
"phoneNumberIsOptedOut": null,
|
||||
"phoneNumberIsInvalid": null,
|
||||
"streamUpdatedAt": null,
|
||||
"emailAddressData": [
|
||||
{
|
||||
"emailAddress": "meier@meier.de",
|
||||
"lower": "meier@meier.de",
|
||||
"primary": true,
|
||||
"optOut": false,
|
||||
"invalid": false
|
||||
},
|
||||
{
|
||||
"emailAddress": "a@r028tuj08wefj0w8efjw0d.de",
|
||||
"lower": "a@r028tuj08wefj0w8efjw0d.de",
|
||||
"primary": false,
|
||||
"optOut": false,
|
||||
"invalid": false
|
||||
}
|
||||
],
|
||||
"phoneNumberData": [],
|
||||
"createdById": "68d65929f18c2afef",
|
||||
"createdByName": "Admin",
|
||||
"modifiedById": "68d65929f18c2afef",
|
||||
"modifiedByName": "Admin",
|
||||
"assignedUserId": null,
|
||||
"assignedUserName": null,
|
||||
"teamsIds": [],
|
||||
"teamsNames": {},
|
||||
"adressensIds": [],
|
||||
"adressensNames": {},
|
||||
"calls1Ids": [],
|
||||
"calls1Names": {},
|
||||
"bankverbindungensIds": [],
|
||||
"bankverbindungensNames": {},
|
||||
"isFollowed": false,
|
||||
"followersIds": [],
|
||||
"followersNames": {}
|
||||
},
|
||||
"advoware_data": {
|
||||
"betNr": 104860,
|
||||
"kommunikation": [
|
||||
{
|
||||
"rowId": "FBABAAAANJFGABAAGJDOAEAPAAAAAPGFPDAFAAAA",
|
||||
"id": 88002,
|
||||
"betNr": 104860,
|
||||
"kommArt": 0,
|
||||
"tlf": "0511/12345-60",
|
||||
"bemerkung": null,
|
||||
"kommKz": 0,
|
||||
"online": false
|
||||
},
|
||||
{
|
||||
"rowId": "FBABAAAABBLIABAAGIDOAEAPAAAAAPHBEOAEAAAA",
|
||||
"id": 114914,
|
||||
"betNr": 104860,
|
||||
"kommArt": 0,
|
||||
"tlf": "kanzlei@ralup.de",
|
||||
"bemerkung": null,
|
||||
"kommKz": 0,
|
||||
"online": true
|
||||
}
|
||||
],
|
||||
"kontaktpersonen": [],
|
||||
"beteiligungen": [
|
||||
{
|
||||
"rowId": "LAADAAAAAHMDABAAGAAEIPBAAAAADGKEMPAFAAAA",
|
||||
"beteiligtenArt": "Sachverständiger",
|
||||
"akte": {
|
||||
"rowId": "",
|
||||
"nr": 2020001684,
|
||||
"az": "1684/20",
|
||||
"rubrum": "Siggel / Siggel",
|
||||
"referat": "SON",
|
||||
"wegen": "Bruderzwist II",
|
||||
"ablage": 1,
|
||||
"abgelegt": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"rowId": "LAADAAAAPGKFABAAGAAEIPBAAAAADGJOMBABAAAA",
|
||||
"beteiligtenArt": "Sachverständiger",
|
||||
"akte": {
|
||||
"rowId": "",
|
||||
"nr": 2020000203,
|
||||
"az": "203/20",
|
||||
"rubrum": "Siggel / Siggel",
|
||||
"referat": "SON",
|
||||
"wegen": "Bruderzwist",
|
||||
"ablage": 1,
|
||||
"abgelegt": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"rowId": "LAADAAAAPJAGACAAGAAEIPBAAAAADGLDFGADAAAA",
|
||||
"beteiligtenArt": "Mandant",
|
||||
"akte": {
|
||||
"rowId": "",
|
||||
"nr": 2019001145,
|
||||
"az": "1145/19",
|
||||
"rubrum": "Siggel / Siggel LALA",
|
||||
"referat": "VMH",
|
||||
"wegen": null,
|
||||
"ablage": 0,
|
||||
"abgelegt": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"adressen": [
|
||||
{
|
||||
"rowId": "KOADAAAAALNFAAAAFPAEIPBAAAAADGGPGAAJAAAA",
|
||||
"id": 0,
|
||||
"beteiligterId": 104860,
|
||||
"reihenfolgeIndex": 1,
|
||||
"strasse": "Musterstraße 12",
|
||||
"plz": "12345",
|
||||
"ort": "Musterort",
|
||||
"land": "D",
|
||||
"postfach": null,
|
||||
"postfachPLZ": null,
|
||||
"anschrift": "Frau\r\nAngela Mustermanns\r\nVorzimmer\r\nMusterstraße 12\r\n12345 Musterort",
|
||||
"standardAnschrift": false,
|
||||
"bemerkung": null,
|
||||
"gueltigVon": null,
|
||||
"gueltigBis": null
|
||||
}
|
||||
],
|
||||
"bankkverbindungen": [
|
||||
{
|
||||
"rowId": "EPABAAAAHBNFAAAAFPNBCGAAAAAAAPDIJDAJAAAA",
|
||||
"id": 54665,
|
||||
"bank": null,
|
||||
"ktoNr": null,
|
||||
"blz": null,
|
||||
"iban": null,
|
||||
"bic": null,
|
||||
"kontoinhaber": null,
|
||||
"mandatsreferenz": null,
|
||||
"mandatVom": null
|
||||
}
|
||||
],
|
||||
"rowId": "EMABAAAAFBNFAAAAFOAEIPBAAAAAAOMNKPAHAAAA",
|
||||
"id": 104860,
|
||||
"anschrift": "Frau\r\nAngela Mustermanns\r\nVorzimmer\r\nMusterstraße 12\r\n12345 Musterort",
|
||||
"strasse": "Musterstraße 12",
|
||||
"plz": "12345",
|
||||
"ort": "Musterort",
|
||||
"email": null,
|
||||
"emailGesch": "kanzlei@ralup.de",
|
||||
"mobil": null,
|
||||
"internet": null,
|
||||
"telGesch": "0511/12345-60",
|
||||
"telPrivat": null,
|
||||
"faxGesch": null,
|
||||
"faxPrivat": null,
|
||||
"autotelefon": null,
|
||||
"sonstige": null,
|
||||
"ePost": null,
|
||||
"bea": null,
|
||||
"art": null,
|
||||
"vorname": "Angela",
|
||||
"name": "Mustermanns",
|
||||
"kurzname": null,
|
||||
"geburtsname": null,
|
||||
"familienstand": null,
|
||||
"titel": null,
|
||||
"anrede": "Frau",
|
||||
"bAnrede": "Sehr geehrte Frau Mustermanns,",
|
||||
"geburtsdatum": null,
|
||||
"sterbedatum": null,
|
||||
"zusatz": "Vorzimmer",
|
||||
"rechtsform": "Frau",
|
||||
"geaendertAm": null,
|
||||
"geaendertVon": null,
|
||||
"angelegtAm": null,
|
||||
"angelegtVon": null,
|
||||
"handelsRegisterNummer": null,
|
||||
"registergericht": null
|
||||
},
|
||||
"comparison": {
|
||||
"espo_fields": [
|
||||
"emailAddressIsInvalid",
|
||||
"followersNames",
|
||||
"id",
|
||||
"handelsregisterNummer",
|
||||
"teamsNames",
|
||||
"assignedUserName",
|
||||
"modifiedAt",
|
||||
"modifiedByName",
|
||||
"betnr",
|
||||
"middleName",
|
||||
"disgTyp",
|
||||
"bankverbindungensNames",
|
||||
"phoneNumberIsOptedOut",
|
||||
"adressensIds",
|
||||
"emailAddressData",
|
||||
"deleted",
|
||||
"teamsIds",
|
||||
"phoneNumber",
|
||||
"isFollowed",
|
||||
"advowareLastSync",
|
||||
"createdById",
|
||||
"createdAt",
|
||||
"calls1Ids",
|
||||
"handelsregisterArt",
|
||||
"name",
|
||||
"phoneNumberIsInvalid",
|
||||
"rechtsform",
|
||||
"emailAddress",
|
||||
"emailAddressIsOptedOut",
|
||||
"firmenname",
|
||||
"description",
|
||||
"adressensNames",
|
||||
"createdByName",
|
||||
"lastName",
|
||||
"assignedUserId",
|
||||
"salutationName",
|
||||
"bankverbindungensIds",
|
||||
"phoneNumberData",
|
||||
"dateOfBirth",
|
||||
"modifiedById",
|
||||
"firstName",
|
||||
"followersIds",
|
||||
"streamUpdatedAt",
|
||||
"syncStatus",
|
||||
"calls1Names"
|
||||
],
|
||||
"advo_fields": [
|
||||
"kontaktpersonen",
|
||||
"rowId",
|
||||
"id",
|
||||
"angelegtVon",
|
||||
"zusatz",
|
||||
"bAnrede",
|
||||
"faxGesch",
|
||||
"bankkverbindungen",
|
||||
"geburtsname",
|
||||
"plz",
|
||||
"adressen",
|
||||
"kurzname",
|
||||
"telPrivat",
|
||||
"anrede",
|
||||
"sonstige",
|
||||
"email",
|
||||
"titel",
|
||||
"sterbedatum",
|
||||
"faxPrivat",
|
||||
"autotelefon",
|
||||
"name",
|
||||
"kommunikation",
|
||||
"rechtsform",
|
||||
"art",
|
||||
"geaendertAm",
|
||||
"anschrift",
|
||||
"beteiligungen",
|
||||
"bea",
|
||||
"handelsRegisterNummer",
|
||||
"registergericht",
|
||||
"internet",
|
||||
"ort",
|
||||
"geburtsdatum",
|
||||
"angelegtAm",
|
||||
"mobil",
|
||||
"emailGesch",
|
||||
"ePost",
|
||||
"strasse",
|
||||
"vorname",
|
||||
"familienstand",
|
||||
"betNr",
|
||||
"geaendertVon",
|
||||
"telGesch"
|
||||
],
|
||||
"common": [
|
||||
"name",
|
||||
"id",
|
||||
"rechtsform"
|
||||
],
|
||||
"espo_only": [
|
||||
"emailAddressIsInvalid",
|
||||
"followersNames",
|
||||
"handelsregisterNummer",
|
||||
"teamsNames",
|
||||
"assignedUserName",
|
||||
"modifiedAt",
|
||||
"modifiedByName",
|
||||
"betnr",
|
||||
"middleName",
|
||||
"disgTyp",
|
||||
"bankverbindungensNames",
|
||||
"phoneNumberIsOptedOut",
|
||||
"adressensIds",
|
||||
"emailAddressData",
|
||||
"deleted",
|
||||
"teamsIds",
|
||||
"phoneNumber",
|
||||
"isFollowed",
|
||||
"advowareLastSync",
|
||||
"createdById",
|
||||
"createdAt",
|
||||
"calls1Ids",
|
||||
"handelsregisterArt",
|
||||
"phoneNumberIsInvalid",
|
||||
"emailAddress",
|
||||
"emailAddressIsOptedOut",
|
||||
"firmenname",
|
||||
"description",
|
||||
"adressensNames",
|
||||
"createdByName",
|
||||
"lastName",
|
||||
"assignedUserId",
|
||||
"salutationName",
|
||||
"bankverbindungensIds",
|
||||
"phoneNumberData",
|
||||
"dateOfBirth",
|
||||
"modifiedById",
|
||||
"firstName",
|
||||
"followersIds",
|
||||
"streamUpdatedAt",
|
||||
"syncStatus",
|
||||
"calls1Names"
|
||||
],
|
||||
"advo_only": [
|
||||
"kontaktpersonen",
|
||||
"rowId",
|
||||
"angelegtVon",
|
||||
"zusatz",
|
||||
"bAnrede",
|
||||
"faxGesch",
|
||||
"bankkverbindungen",
|
||||
"geburtsname",
|
||||
"plz",
|
||||
"adressen",
|
||||
"kurzname",
|
||||
"telPrivat",
|
||||
"anrede",
|
||||
"sonstige",
|
||||
"email",
|
||||
"titel",
|
||||
"sterbedatum",
|
||||
"autotelefon",
|
||||
"faxPrivat",
|
||||
"kommunikation",
|
||||
"art",
|
||||
"geaendertAm",
|
||||
"anschrift",
|
||||
"beteiligungen",
|
||||
"bea",
|
||||
"handelsRegisterNummer",
|
||||
"registergericht",
|
||||
"internet",
|
||||
"ort",
|
||||
"geburtsdatum",
|
||||
"angelegtAm",
|
||||
"mobil",
|
||||
"emailGesch",
|
||||
"ePost",
|
||||
"strasse",
|
||||
"vorname",
|
||||
"familienstand",
|
||||
"betNr",
|
||||
"geaendertVon",
|
||||
"telGesch"
|
||||
],
|
||||
"suggested_mappings": [
|
||||
[
|
||||
"name",
|
||||
"name"
|
||||
],
|
||||
[
|
||||
"emailAddress",
|
||||
"email"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
323
bitbylaw/scripts/compare_beteiligte.py
Executable file
323
bitbylaw/scripts/compare_beteiligte.py
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Helper-Script zum Vergleichen der Beteiligten-Strukturen zwischen Advoware und EspoCRM.
|
||||
|
||||
Usage:
|
||||
python scripts/compare_beteiligte.py <entity_id_espocrm> [advoware_id]
|
||||
|
||||
Examples:
|
||||
# Vergleiche EspoCRM Beteiligten (automatische Suche in Advoware)
|
||||
python scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc
|
||||
|
||||
# Vergleiche mit spezifischer Advoware ID
|
||||
python scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc 12345
|
||||
"""
|
||||
|
||||
import sys
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add bitbylaw directory to path for imports
|
||||
bitbylaw_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(bitbylaw_dir))
|
||||
|
||||
from services.espocrm import EspoCRMAPI
|
||||
from services.advoware import AdvowareAPI
|
||||
from config import Config
|
||||
|
||||
|
||||
class SimpleContext:
|
||||
"""Simple context for logging"""
|
||||
class Logger:
|
||||
def info(self, msg):
|
||||
print(f"[INFO] {msg}")
|
||||
|
||||
def error(self, msg):
|
||||
print(f"[ERROR] {msg}")
|
||||
|
||||
def debug(self, msg):
|
||||
print(f"[DEBUG] {msg}")
|
||||
|
||||
def warning(self, msg):
|
||||
print(f"[WARNING] {msg}")
|
||||
|
||||
def __init__(self):
|
||||
self.logger = self.Logger()
|
||||
|
||||
|
||||
async def fetch_from_espocrm(entity_id: str):
|
||||
"""Fetch Beteiligter from EspoCRM"""
|
||||
print("\n" + "="*80)
|
||||
print("ESPOCRM - Fetching Beteiligter")
|
||||
print("="*80)
|
||||
|
||||
context = SimpleContext()
|
||||
espo = EspoCRMAPI(context=context)
|
||||
|
||||
try:
|
||||
# Try different entity types that might contain Beteiligte
|
||||
entity_types = ['CBeteiligte', 'Beteiligte', 'Contact', 'Account', 'Lead', 'CVmhErstgespraech', 'CVmhBeteiligte']
|
||||
|
||||
for entity_type in entity_types:
|
||||
try:
|
||||
print(f"\nTrying entity type: {entity_type}")
|
||||
result = await espo.get_entity(entity_type, entity_id)
|
||||
|
||||
print(f"\n✓ Success! Found in {entity_type}")
|
||||
print(f"\nEntity Structure:")
|
||||
print("-" * 80)
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Not found in {entity_type}: {e}")
|
||||
continue
|
||||
|
||||
print("\n✗ Entity not found in any known entity type")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error fetching from EspoCRM: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def fetch_from_advoware(advoware_id: str = None, search_name: str = None):
|
||||
"""Fetch Beteiligter from Advoware"""
|
||||
print("\n" + "="*80)
|
||||
print("ADVOWARE - Fetching Beteiligter")
|
||||
print("="*80)
|
||||
|
||||
context = SimpleContext()
|
||||
advo = AdvowareAPI(context=context)
|
||||
|
||||
try:
|
||||
# Try to fetch by ID if provided
|
||||
if advoware_id:
|
||||
print(f"\nFetching by ID: {advoware_id}")
|
||||
# Try correct Advoware endpoint
|
||||
endpoints = [
|
||||
f'/api/v1/advonet/Beteiligte/{advoware_id}',
|
||||
]
|
||||
|
||||
for endpoint in endpoints:
|
||||
try:
|
||||
print(f" Trying endpoint: {endpoint}")
|
||||
result = await advo.api_call(endpoint, method='GET')
|
||||
|
||||
if result:
|
||||
# Advoware gibt oft Listen zurück, nehme erstes Element
|
||||
if isinstance(result, list) and len(result) > 0:
|
||||
result = result[0]
|
||||
|
||||
print(f"\n✓ Success! Found at {endpoint}")
|
||||
print(f"\nEntity Structure:")
|
||||
print("-" * 80)
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Not found at {endpoint}: {e}")
|
||||
continue
|
||||
|
||||
# Try to search by name if EspoCRM data available
|
||||
if search_name:
|
||||
print(f"\nSearching by name: {search_name}")
|
||||
search_endpoints = [
|
||||
'/api/v1/advonet/Beteiligte',
|
||||
]
|
||||
|
||||
for endpoint in search_endpoints:
|
||||
try:
|
||||
print(f" Trying endpoint: {endpoint}")
|
||||
result = await advo.api_call(
|
||||
endpoint,
|
||||
method='GET',
|
||||
params={'search': search_name, 'limit': 5}
|
||||
)
|
||||
|
||||
if result and (isinstance(result, list) and len(result) > 0 or
|
||||
isinstance(result, dict) and result.get('data')):
|
||||
print(f"\n✓ Found {len(result) if isinstance(result, list) else len(result.get('data', []))} results")
|
||||
print(f"\nSearch Results:")
|
||||
print("-" * 80)
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Search failed at {endpoint}: {e}")
|
||||
continue
|
||||
|
||||
print("\n✗ Entity not found in Advoware")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error fetching from Advoware: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
async def compare_structures(espo_data: dict, advo_data: dict):
|
||||
"""Compare field structures between EspoCRM and Advoware"""
|
||||
print("\n" + "="*80)
|
||||
print("STRUCTURE COMPARISON")
|
||||
print("="*80)
|
||||
|
||||
if not espo_data or not advo_data:
|
||||
print("\n⚠ Cannot compare - missing data from one or both systems")
|
||||
return
|
||||
|
||||
# Extract fields
|
||||
espo_fields = set(espo_data.keys()) if isinstance(espo_data, dict) else set()
|
||||
|
||||
# Handle Advoware data structure (might be nested)
|
||||
if isinstance(advo_data, dict):
|
||||
if 'data' in advo_data:
|
||||
advo_data = advo_data['data']
|
||||
if isinstance(advo_data, list) and len(advo_data) > 0:
|
||||
advo_data = advo_data[0]
|
||||
|
||||
advo_fields = set(advo_data.keys()) if isinstance(advo_data, dict) else set()
|
||||
|
||||
print(f"\nEspoCRM Fields ({len(espo_fields)}):")
|
||||
print("-" * 40)
|
||||
for field in sorted(espo_fields):
|
||||
value = espo_data.get(field)
|
||||
value_type = type(value).__name__
|
||||
print(f" {field:<30} ({value_type})")
|
||||
|
||||
print(f"\nAdvoware Fields ({len(advo_fields)}):")
|
||||
print("-" * 40)
|
||||
for field in sorted(advo_fields):
|
||||
value = advo_data.get(field)
|
||||
value_type = type(value).__name__
|
||||
print(f" {field:<30} ({value_type})")
|
||||
|
||||
# Find common fields (potential mappings)
|
||||
common = espo_fields & advo_fields
|
||||
espo_only = espo_fields - advo_fields
|
||||
advo_only = advo_fields - espo_fields
|
||||
|
||||
print(f"\nCommon Fields ({len(common)}):")
|
||||
print("-" * 40)
|
||||
for field in sorted(common):
|
||||
espo_val = espo_data.get(field)
|
||||
advo_val = advo_data.get(field)
|
||||
match = "✓" if espo_val == advo_val else "✗"
|
||||
print(f" {match} {field}")
|
||||
if espo_val != advo_val:
|
||||
print(f" EspoCRM: {espo_val}")
|
||||
print(f" Advoware: {advo_val}")
|
||||
|
||||
print(f"\nEspoCRM Only ({len(espo_only)}):")
|
||||
print("-" * 40)
|
||||
for field in sorted(espo_only):
|
||||
print(f" {field}")
|
||||
|
||||
print(f"\nAdvoware Only ({len(advo_only)}):")
|
||||
print("-" * 40)
|
||||
for field in sorted(advo_only):
|
||||
print(f" {field}")
|
||||
|
||||
# Suggest potential mappings based on field names
|
||||
print(f"\nPotential Field Mappings:")
|
||||
print("-" * 40)
|
||||
|
||||
mapping_suggestions = []
|
||||
|
||||
# Common name patterns
|
||||
name_patterns = [
|
||||
('name', 'name'),
|
||||
('firstName', 'first_name'),
|
||||
('lastName', 'last_name'),
|
||||
('email', 'email'),
|
||||
('emailAddress', 'email'),
|
||||
('phone', 'phone'),
|
||||
('phoneNumber', 'phone_number'),
|
||||
('address', 'address'),
|
||||
('street', 'street'),
|
||||
('city', 'city'),
|
||||
('postalCode', 'postal_code'),
|
||||
('zipCode', 'postal_code'),
|
||||
('country', 'country'),
|
||||
]
|
||||
|
||||
for espo_field, advo_field in name_patterns:
|
||||
if espo_field in espo_fields and advo_field in advo_fields:
|
||||
mapping_suggestions.append((espo_field, advo_field))
|
||||
print(f" {espo_field:<30} → {advo_field}")
|
||||
|
||||
return {
|
||||
'espo_fields': list(espo_fields),
|
||||
'advo_fields': list(advo_fields),
|
||||
'common': list(common),
|
||||
'espo_only': list(espo_only),
|
||||
'advo_only': list(advo_only),
|
||||
'suggested_mappings': mapping_suggestions
|
||||
}
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function"""
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
espocrm_id = sys.argv[1]
|
||||
advoware_id = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("BETEILIGTE STRUCTURE COMPARISON TOOL")
|
||||
print("="*80)
|
||||
print(f"\nEspoCRM Entity ID: {espocrm_id}")
|
||||
if advoware_id:
|
||||
print(f"Advoware ID: {advoware_id}")
|
||||
|
||||
# Check environment variables
|
||||
print("\nEnvironment Check:")
|
||||
print("-" * 40)
|
||||
print(f"ESPOCRM_API_BASE_URL: {Config.ESPOCRM_API_BASE_URL}")
|
||||
print(f"ESPOCRM_API_KEY: {'✓ Set' if Config.ESPOCRM_API_KEY else '✗ Missing'}")
|
||||
print(f"ADVOWARE_API_BASE_URL: {Config.ADVOWARE_API_BASE_URL}")
|
||||
print(f"ADVOWARE_API_KEY: {'✓ Set' if Config.ADVOWARE_API_KEY else '✗ Missing'}")
|
||||
|
||||
# Fetch from EspoCRM
|
||||
espo_data = await fetch_from_espocrm(espocrm_id)
|
||||
|
||||
# Extract name for Advoware search
|
||||
search_name = None
|
||||
if espo_data:
|
||||
search_name = (
|
||||
espo_data.get('name') or
|
||||
f"{espo_data.get('firstName', '')} {espo_data.get('lastName', '')}".strip() or
|
||||
None
|
||||
)
|
||||
|
||||
# Fetch from Advoware
|
||||
advo_data = await fetch_from_advoware(advoware_id, search_name)
|
||||
|
||||
# Compare structures
|
||||
if espo_data or advo_data:
|
||||
comparison = await compare_structures(espo_data, advo_data)
|
||||
|
||||
# Save comparison to file
|
||||
output_file = Path(__file__).parent / 'beteiligte_comparison_result.json'
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
'espocrm_data': espo_data,
|
||||
'advoware_data': advo_data,
|
||||
'comparison': comparison
|
||||
}, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n\n{'='*80}")
|
||||
print(f"Comparison saved to: {output_file}")
|
||||
print(f"{'='*80}\n")
|
||||
else:
|
||||
print("\n⚠ No data available for comparison")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user