feat: Integrate NotificationManager for handling notifications in sync operations

This commit is contained in:
2026-02-08 14:42:33 +00:00
parent b4e41e7381
commit da9a962858
2 changed files with 112 additions and 85 deletions

View File

@@ -16,6 +16,7 @@ import logging
import redis import redis
from config import Config from config import Config
from services.espocrm import EspoCRMAPI from services.espocrm import EspoCRMAPI
from services.notification_utils import NotificationManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -35,6 +36,7 @@ class BeteiligteSync:
self.espocrm = espocrm_api self.espocrm = espocrm_api
self.context = context self.context = context
self.redis = redis_client or self._init_redis() self.redis = redis_client or self._init_redis()
self.notification_manager = NotificationManager(espocrm_api=self.espocrm, context=context)
def _init_redis(self) -> redis.Redis: def _init_redis(self) -> redis.Redis:
"""Initialize Redis client for distributed locking""" """Initialize Redis client for distributed locking"""
@@ -395,11 +397,11 @@ class BeteiligteSync:
extra_data: Optional[Dict[str, Any]] = None extra_data: Optional[Dict[str, Any]] = None
) -> None: ) -> None:
""" """
Sendet EspoCRM In-App Notification Sendet EspoCRM Notification via NotificationManager
Args: Args:
entity_id: CBeteiligte Entity ID entity_id: CBeteiligte Entity ID
notification_type: "conflict" oder "deleted" notification_type: "conflict", "deleted" oder "error"
extra_data: Zusätzliche Daten für Nachricht extra_data: Zusätzliche Daten für Nachricht
""" """
try: try:
@@ -407,45 +409,58 @@ class BeteiligteSync:
entity = await self.espocrm.get_entity('CBeteiligte', entity_id) entity = await self.espocrm.get_entity('CBeteiligte', entity_id)
name = entity.get('name', 'Unbekannt') name = entity.get('name', 'Unbekannt')
betnr = entity.get('betnr') betnr = entity.get('betnr')
assigned_user = entity.get('assignedUserId')
# Erstelle Nachricht basierend auf Typ # Map notification_type zu action_type
if notification_type == "conflict": if notification_type == "conflict":
message = ( action_type = 'sync_conflict'
f"⚠️ Sync-Konflikt bei Beteiligten '{name}' (betNr: {betnr}). " details = {
f"EspoCRM hat Vorrang - Änderungen wurden nach Advoware übertragen. " 'message': f"Sync-Konflikt bei Beteiligten '{name}' (betNr: {betnr})",
f"Bitte prüfen Sie die Details." 'description': (
) f"EspoCRM hat Vorrang - Änderungen wurden nach Advoware übertragen.\n\n"
f"Bitte prüfen Sie die Details und stellen Sie sicher, dass die Daten korrekt sind."
),
'entity_name': name,
'betnr': betnr,
'priority': 'Normal'
}
elif notification_type == "deleted": elif notification_type == "deleted":
deleted_at = entity.get('advowareDeletedAt', 'unbekannt') deleted_at = entity.get('advowareDeletedAt', 'unbekannt')
message = ( action_type = 'entity_deleted_in_source'
f"🗑️ Beteiligter '{name}' (betNr: {betnr}) wurde in Advoware gelöscht " details = {
f"(am {deleted_at}). Der Datensatz wurde in EspoCRM markiert, aber nicht gelöscht. " 'message': f"Beteiligter '{name}' wurde in Advoware gelöscht",
f"Bitte prüfen Sie, ob dies beabsichtigt war." 'description': (
) f"Der Beteiligte '{name}' (betNr: {betnr}) wurde am {deleted_at} "
f"in Advoware gelöscht.\n\n"
f"Der Datensatz wurde in EspoCRM markiert, aber nicht gelöscht. "
f"Bitte prüfen Sie, ob dies beabsichtigt war."
),
'entity_name': name,
'betnr': betnr,
'deleted_at': deleted_at,
'priority': 'High'
}
else: else:
message = f"Benachrichtigung für Beteiligten '{name}'" action_type = 'general_manual_action'
details = {
'message': f"Benachrichtigung für Beteiligten '{name}'",
'entity_name': name,
'betnr': betnr
}
# Erstelle Notification in EspoCRM # Merge extra_data if provided
notification_data = { if extra_data:
'type': 'message', details.update(extra_data)
'message': message,
'relatedType': 'CBeteiligte',
'relatedId': entity_id,
}
# Wenn assigned user vorhanden, sende an diesen # Sende via NotificationManager
if assigned_user: await self.notification_manager.notify_manual_action_required(
notification_data['userId'] = assigned_user entity_type='CBeteiligte',
entity_id=entity_id,
# Sende via API action_type=action_type,
result = await self.espocrm.api_call( details=details,
'Notification', create_task=True
method='POST',
data=notification_data
) )
self._log(f"Notification gesendet für {entity_id}: {notification_type}") self._log(f"Notification via NotificationManager gesendet: {notification_type} für {entity_id}")
except Exception as e: except Exception as e:
self._log(f"Fehler beim Senden der Notification: {e}", level='error') self._log(f"Fehler beim Senden der Notification: {e}", level='error')

View File

@@ -2,6 +2,7 @@ from services.advoware import AdvowareAPI
from services.espocrm import EspoCRMAPI from services.espocrm import EspoCRMAPI
from services.bankverbindungen_mapper import BankverbindungenMapper from services.bankverbindungen_mapper import BankverbindungenMapper
from services.beteiligte_sync_utils import BeteiligteSync from services.beteiligte_sync_utils import BeteiligteSync
from services.notification_utils import NotificationManager
import json import json
import redis import redis
from config import Config from config import Config
@@ -53,6 +54,7 @@ async def handler(event_data, context):
advoware = AdvowareAPI(context) advoware = AdvowareAPI(context)
sync_utils = BeteiligteSync(espocrm, redis_client, context) # Reuse utils sync_utils = BeteiligteSync(espocrm, redis_client, context) # Reuse utils
mapper = BankverbindungenMapper() mapper = BankverbindungenMapper()
notification_mgr = NotificationManager(espocrm_api=espocrm, context=context)
try: try:
# 1. ACQUIRE LOCK # 1. ACQUIRE LOCK
@@ -98,11 +100,11 @@ async def handler(event_data, context):
# FALL B: Existiert (hat advowareId) → UPDATE oder CHECK # FALL B: Existiert (hat advowareId) → UPDATE oder CHECK
elif advoware_id: elif advoware_id:
await handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, mapper, context, redis_client, lock_key) await handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, mapper, notification_mgr, context, redis_client, lock_key)
# FALL C: DELETE # FALL C: DELETE
elif action == 'delete': elif action == 'delete':
await handle_delete(entity_id, betnr, advoware_id, espocrm, advoware, context, redis_client, lock_key) await handle_delete(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, notification_mgr, context, redis_client, lock_key)
else: else:
context.logger.warn(f"⚠️ Unbekannte Kombination: action={action}, advowareId={advoware_id}") context.logger.warn(f"⚠️ Unbekannte Kombination: action={action}, advowareId={advoware_id}")
@@ -165,35 +167,44 @@ async def handle_create(entity_id, betnr, espo_entity, espocrm, advoware, mapper
redis_client.delete(lock_key) redis_client.delete(lock_key)
async def handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, mapper, context, redis_client, lock_key): async def handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, mapper, notification_mgr, context, redis_client, lock_key):
"""Update nicht möglich - Sendet Notification an User""" """Update nicht möglich - Sendet Notification an User via NotificationManager"""
try: try:
context.logger.warn(f"⚠️ UPDATE: Advoware API unterstützt kein PUT für Bankverbindungen") context.logger.warn(f"⚠️ UPDATE: Advoware API unterstützt kein PUT für Bankverbindungen")
# Erstelle Notification für User in EspoCRM
iban = espo_entity.get('iban', 'N/A') iban = espo_entity.get('iban', 'N/A')
bank = espo_entity.get('bank', 'N/A') bank = espo_entity.get('bank', 'N/A')
name = espo_entity.get('name', 'Unbenannt')
notification_message = ( # Sende via NotificationManager
f"Bankverbindung wurde in EspoCRM geändert, aber die Advoware API unterstützt keine Updates.\n\n" await notification_mgr.notify_manual_action_required(
f"**Bitte manuell in Advoware aktualisieren:**\n" entity_type='CBankverbindungen',
f"- Bank: {bank}\n" entity_id=entity_id,
f"- IBAN: {iban}\n" action_type='api_limitation',
f"- Beteiligter betNr: {betnr}\n" details={
f"- Advoware ID: {advoware_id}\n\n" 'message': f'UPDATE nicht möglich für Bankverbindung: {name}',
f"**Workaround:** Löschen und neu erstellen in EspoCRM, dann wird neue Bankverbindung in Advoware angelegt." 'description': (
f"Die Advoware API unterstützt keine Updates für Bankverbindungen.\n\n"
f"**Details:**\n"
f"- Bank: {bank}\n"
f"- IBAN: {iban}\n"
f"- Beteiligter betNr: {betnr}\n"
f"- Advoware ID: {advoware_id}\n\n"
f"**Workaround:**\n"
f"Löschen Sie die Bankverbindung in EspoCRM und erstellen Sie sie neu. "
f"Die neue Bankverbindung wird dann automatisch in Advoware angelegt."
),
'entity_name': name,
'iban': iban,
'bank': bank,
'betnr': betnr,
'advoware_id': advoware_id,
'priority': 'Normal'
},
create_task=True
) )
# Sende Notification via EspoCRM API context.logger.info(f"📧 Notification via NotificationManager gesendet: Update-Limitation")
await espocrm.api_call('/Notification', method='POST', json_data={
'type': 'message',
'message': notification_message,
'userId': espo_entity.get('createdById') or espo_entity.get('modifiedById'),
'relatedType': 'CBankverbindungen',
'relatedId': entity_id
})
context.logger.info(f"📧 Notification an User gesendet: Manuelle Aktualisierung erforderlich")
redis_client.delete(lock_key) redis_client.delete(lock_key)
except Exception as e: except Exception as e:
@@ -203,8 +214,8 @@ async def handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, adv
redis_client.delete(lock_key) redis_client.delete(lock_key)
async def handle_delete(entity_id, betnr, advoware_id, espocrm, advoware, context, redis_client, lock_key): async def handle_delete(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, notification_mgr, context, redis_client, lock_key):
"""Delete nicht möglich - Sendet Notification an User""" """Delete nicht möglich - Sendet Notification an User via NotificationManager"""
try: try:
context.logger.warn(f"⚠️ DELETE: Advoware API unterstützt kein DELETE für Bankverbindungen") context.logger.warn(f"⚠️ DELETE: Advoware API unterstützt kein DELETE für Bankverbindungen")
@@ -213,37 +224,38 @@ async def handle_delete(entity_id, betnr, advoware_id, espocrm, advoware, contex
redis_client.delete(lock_key) redis_client.delete(lock_key)
return return
# Hole Entity-Details für Notification (vor dem Delete) iban = espo_entity.get('iban', 'N/A')
try: bank = espo_entity.get('bank', 'N/A')
espo_entity = await espocrm.get_entity('CBankverbindungen', entity_id) name = espo_entity.get('name', 'Unbenannt')
iban = espo_entity.get('iban', 'N/A')
bank = espo_entity.get('bank', 'N/A')
user_id = espo_entity.get('createdById') or espo_entity.get('modifiedById')
except:
iban = 'N/A'
bank = 'N/A'
user_id = None
# Erstelle Notification für User in EspoCRM # Sende via NotificationManager
notification_message = ( await notification_mgr.notify_manual_action_required(
f"Bankverbindung wurde in EspoCRM gelöscht, aber die Advoware API unterstützt keine Löschungen.\n\n" entity_type='CBankverbindungen',
f"**Bitte manuell in Advoware löschen:**\n" entity_id=entity_id,
f"- Bank: {bank}\n" action_type='delete_not_supported',
f"- IBAN: {iban}\n" details={
f"- Beteiligter betNr: {betnr}\n" 'message': f'DELETE erforderlich für Bankverbindung: {name}',
f"- Advoware ID: {advoware_id}\n\n" 'description': (
f"Die Bankverbindung bleibt in Advoware bestehen bis zur manuellen Löschung." f"Die Advoware API unterstützt keine Löschungen für Bankverbindungen.\n\n"
f"**Bitte manuell in Advoware löschen:**\n"
f"- Bank: {bank}\n"
f"- IBAN: {iban}\n"
f"- Beteiligter betNr: {betnr}\n"
f"- Advoware ID: {advoware_id}\n\n"
f"Die Bankverbindung wurde in EspoCRM gelöscht, bleibt aber in Advoware "
f"bestehen bis zur manuellen Löschung."
),
'entity_name': name,
'iban': iban,
'bank': bank,
'betnr': betnr,
'advoware_id': advoware_id,
'priority': 'Normal'
},
create_task=True
) )
# Sende Notification context.logger.info(f"📧 Notification via NotificationManager gesendet: Delete erforderlich")
if user_id:
await espocrm.api_call('/Notification', method='POST', json_data={
'type': 'message',
'message': notification_message,
'userId': user_id
})
context.logger.info(f"📧 Notification an User gesendet: Manuelle Löschung erforderlich")
redis_client.delete(lock_key) redis_client.delete(lock_key)
except Exception as e: except Exception as e: