From da9a96285805cf9e253761745bdf0ccb0b21f17b Mon Sep 17 00:00:00 2001 From: bitbylaw Date: Sun, 8 Feb 2026 14:42:33 +0000 Subject: [PATCH] feat: Integrate NotificationManager for handling notifications in sync operations --- bitbylaw/services/beteiligte_sync_utils.py | 79 +++++++----- .../vmh/bankverbindungen_sync_event_step.py | 118 ++++++++++-------- 2 files changed, 112 insertions(+), 85 deletions(-) diff --git a/bitbylaw/services/beteiligte_sync_utils.py b/bitbylaw/services/beteiligte_sync_utils.py index b8b3578c..9afb3467 100644 --- a/bitbylaw/services/beteiligte_sync_utils.py +++ b/bitbylaw/services/beteiligte_sync_utils.py @@ -16,6 +16,7 @@ import logging import redis from config import Config from services.espocrm import EspoCRMAPI +from services.notification_utils import NotificationManager logger = logging.getLogger(__name__) @@ -35,6 +36,7 @@ class BeteiligteSync: self.espocrm = espocrm_api self.context = context self.redis = redis_client or self._init_redis() + self.notification_manager = NotificationManager(espocrm_api=self.espocrm, context=context) def _init_redis(self) -> redis.Redis: """Initialize Redis client for distributed locking""" @@ -395,11 +397,11 @@ class BeteiligteSync: extra_data: Optional[Dict[str, Any]] = None ) -> None: """ - Sendet EspoCRM In-App Notification + Sendet EspoCRM Notification via NotificationManager Args: entity_id: CBeteiligte Entity ID - notification_type: "conflict" oder "deleted" + notification_type: "conflict", "deleted" oder "error" extra_data: Zusätzliche Daten für Nachricht """ try: @@ -407,45 +409,58 @@ class BeteiligteSync: entity = await self.espocrm.get_entity('CBeteiligte', entity_id) name = entity.get('name', 'Unbekannt') betnr = entity.get('betnr') - assigned_user = entity.get('assignedUserId') - # Erstelle Nachricht basierend auf Typ + # Map notification_type zu action_type if notification_type == "conflict": - message = ( - f"⚠️ Sync-Konflikt bei Beteiligten '{name}' (betNr: {betnr}). " - f"EspoCRM hat Vorrang - Änderungen wurden nach Advoware übertragen. " - f"Bitte prüfen Sie die Details." - ) + action_type = 'sync_conflict' + details = { + 'message': f"Sync-Konflikt bei Beteiligten '{name}' (betNr: {betnr})", + '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": deleted_at = entity.get('advowareDeletedAt', 'unbekannt') - message = ( - f"🗑️ Beteiligter '{name}' (betNr: {betnr}) wurde in Advoware gelöscht " - f"(am {deleted_at}). Der Datensatz wurde in EspoCRM markiert, aber nicht gelöscht. " - f"Bitte prüfen Sie, ob dies beabsichtigt war." - ) + action_type = 'entity_deleted_in_source' + details = { + 'message': f"Beteiligter '{name}' wurde in Advoware gelöscht", + '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: - 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 - notification_data = { - 'type': 'message', - 'message': message, - 'relatedType': 'CBeteiligte', - 'relatedId': entity_id, - } + # Merge extra_data if provided + if extra_data: + details.update(extra_data) - # Wenn assigned user vorhanden, sende an diesen - if assigned_user: - notification_data['userId'] = assigned_user - - # Sende via API - result = await self.espocrm.api_call( - 'Notification', - method='POST', - data=notification_data + # Sende via NotificationManager + await self.notification_manager.notify_manual_action_required( + entity_type='CBeteiligte', + entity_id=entity_id, + action_type=action_type, + details=details, + create_task=True ) - 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: self._log(f"Fehler beim Senden der Notification: {e}", level='error') diff --git a/bitbylaw/steps/vmh/bankverbindungen_sync_event_step.py b/bitbylaw/steps/vmh/bankverbindungen_sync_event_step.py index 09422219..8abd1fac 100644 --- a/bitbylaw/steps/vmh/bankverbindungen_sync_event_step.py +++ b/bitbylaw/steps/vmh/bankverbindungen_sync_event_step.py @@ -2,6 +2,7 @@ from services.advoware import AdvowareAPI from services.espocrm import EspoCRMAPI from services.bankverbindungen_mapper import BankverbindungenMapper from services.beteiligte_sync_utils import BeteiligteSync +from services.notification_utils import NotificationManager import json import redis from config import Config @@ -53,6 +54,7 @@ async def handler(event_data, context): advoware = AdvowareAPI(context) sync_utils = BeteiligteSync(espocrm, redis_client, context) # Reuse utils mapper = BankverbindungenMapper() + notification_mgr = NotificationManager(espocrm_api=espocrm, context=context) try: # 1. ACQUIRE LOCK @@ -98,11 +100,11 @@ async def handler(event_data, context): # FALL B: Existiert (hat advowareId) → UPDATE oder CHECK 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 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: 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) -async def handle_update(entity_id, betnr, advoware_id, espo_entity, espocrm, advoware, mapper, context, redis_client, lock_key): - """Update nicht möglich - Sendet Notification an User""" +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 via NotificationManager""" try: 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') bank = espo_entity.get('bank', 'N/A') + name = espo_entity.get('name', 'Unbenannt') - notification_message = ( - f"Bankverbindung wurde in EspoCRM geändert, aber die Advoware API unterstützt keine Updates.\n\n" - f"**Bitte manuell in Advoware aktualisieren:**\n" - f"- Bank: {bank}\n" - f"- IBAN: {iban}\n" - f"- Beteiligter betNr: {betnr}\n" - f"- Advoware ID: {advoware_id}\n\n" - f"**Workaround:** Löschen und neu erstellen in EspoCRM, dann wird neue Bankverbindung in Advoware angelegt." + # Sende via NotificationManager + await notification_mgr.notify_manual_action_required( + entity_type='CBankverbindungen', + entity_id=entity_id, + action_type='api_limitation', + details={ + 'message': f'UPDATE nicht möglich für Bankverbindung: {name}', + '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 - 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") + context.logger.info(f"📧 Notification via NotificationManager gesendet: Update-Limitation") redis_client.delete(lock_key) 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) -async def handle_delete(entity_id, betnr, advoware_id, espocrm, advoware, context, redis_client, lock_key): - """Delete nicht möglich - Sendet Notification an User""" +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 via NotificationManager""" try: 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) return - # Hole Entity-Details für Notification (vor dem Delete) - try: - espo_entity = await espocrm.get_entity('CBankverbindungen', entity_id) - 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 + iban = espo_entity.get('iban', 'N/A') + bank = espo_entity.get('bank', 'N/A') + name = espo_entity.get('name', 'Unbenannt') - # Erstelle Notification für User in EspoCRM - notification_message = ( - f"Bankverbindung wurde in EspoCRM gelöscht, aber die Advoware API unterstützt keine Löschungen.\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 bleibt in Advoware bestehen bis zur manuellen Löschung." + # Sende via NotificationManager + await notification_mgr.notify_manual_action_required( + entity_type='CBankverbindungen', + entity_id=entity_id, + action_type='delete_not_supported', + details={ + 'message': f'DELETE erforderlich für Bankverbindung: {name}', + 'description': ( + 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 - 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") + context.logger.info(f"📧 Notification via NotificationManager gesendet: Delete erforderlich") redis_client.delete(lock_key) except Exception as e: