diff --git a/bitbylaw/services/espocrm_mapper.py b/bitbylaw/services/espocrm_mapper.py index 4de9c9ef..d2d27509 100644 --- a/bitbylaw/services/espocrm_mapper.py +++ b/bitbylaw/services/espocrm_mapper.py @@ -77,7 +77,7 @@ class BeteiligteMapper: # Advoware ignoriert diese Felder im PUT (trotz Swagger Schema) # Siehe: docs/ADVOWARE_BETEILIGTE_FIELDS.md - logger.debug(f"Mapped to Advoware STAMMDATEN: name={advo_data.get('name')}, vorname={advo_data.get('vorname')}, rechtsform={rechtsform}") + logger.debug(f"Mapped to Advoware STAMMDATEN: name={advo_data.get('name')}, vorname={advo_data.get('vorname')}, rechtsform={advo_data.get('rechtsform')}") return advo_data diff --git a/bitbylaw/steps/vmh/beteiligte_sync_event_step.py b/bitbylaw/steps/vmh/beteiligte_sync_event_step.py index df2c28d7..8dc795cf 100644 --- a/bitbylaw/steps/vmh/beteiligte_sync_event_step.py +++ b/bitbylaw/steps/vmh/beteiligte_sync_event_step.py @@ -129,23 +129,38 @@ async def handle_create(entity_id, espo_entity, espocrm, advoware, sync_utils, m data=advo_data ) - # Extrahiere betNr aus Response - new_betnr = result.get('betNr') if isinstance(result, dict) else None + # Extrahiere betNr aus Response (case-insensitive: betNr oder betnr) + new_betnr = None + if isinstance(result, dict): + new_betnr = result.get('betNr') or result.get('betnr') if not new_betnr: - raise Exception(f"Keine betNr in Advoware Response: {result}") + raise Exception(f"Keine betNr/betnr in Advoware Response: {result}") context.logger.info(f"✅ In Advoware erstellt: betNr={new_betnr}") - # OPTIMIERT: Kombiniere release_lock + betnr update in 1 API call + # Lade Entity nach POST um rowId zu bekommen (WICHTIG für Change Detection!) + created_entity = await advoware.api_call( + f'api/v1/advonet/Beteiligte/{new_betnr}', + method='GET' + ) + new_rowid = created_entity.get('rowId') if isinstance(created_entity, dict) else created_entity[0].get('rowId') + + if not new_rowid: + context.logger.warn(f"⚠️ Keine rowId nach CREATE - Change Detection nicht möglich!") + + # OPTIMIERT: Kombiniere release_lock + betnr + rowId update in 1 API call await sync_utils.release_sync_lock( entity_id, 'clean', error_message=None, - extra_fields={'betnr': new_betnr} + extra_fields={ + 'betnr': new_betnr, + 'advowareRowId': new_rowid # WICHTIG für Change Detection! + } ) - context.logger.info(f"✅ CREATE erfolgreich: {entity_id} → betNr {new_betnr}") + context.logger.info(f"✅ CREATE erfolgreich: {entity_id} → betNr {new_betnr}, rowId {new_rowid[:20] if new_rowid else 'N/A'}...") except Exception as e: context.logger.error(f"❌ CREATE fehlgeschlagen: {e}") @@ -196,19 +211,26 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u # OPTIMIERT: Use merge utility (reduces code duplication) merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper) - await advoware.api_call( + put_result = await advoware.api_call( f'api/v1/advonet/Beteiligte/{betnr}', method='PUT', data=merged_data ) - # Speichere rowId für zukünftige Vergleiche + # Extrahiere neue rowId aus PUT Response (spart extra GET!) + new_rowid = None + if isinstance(put_result, list) and len(put_result) > 0: + new_rowid = put_result[0].get('rowId') + elif isinstance(put_result, dict): + new_rowid = put_result.get('rowId') + + # Speichere neue rowId für zukünftige Vergleiche await sync_utils.release_sync_lock( entity_id, 'clean', - extra_fields={'advowareRowId': advo_entity.get('rowId')} + extra_fields={'advowareRowId': new_rowid} ) - context.logger.info(f"✅ Advoware aktualisiert (initial sync)") + context.logger.info(f"✅ Advoware aktualisiert (initial sync), neue rowId: {new_rowid[:20] if new_rowid else 'N/A'}...") return # KEIN SYNC NÖTIG @@ -224,23 +246,25 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u # OPTIMIERT: Use merge utility merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper) - await advoware.api_call( + put_result = await advoware.api_call( f'api/v1/advonet/Beteiligte/{betnr}', method='PUT', data=merged_data ) - # Hole aktualisierte Entity um neue rowId zu bekommen - updated_advo = await advoware.api_call(f'api/v1/advonet/Beteiligte/{betnr}', method='GET') - if isinstance(updated_advo, list): - updated_advo = updated_advo[0] + # Extrahiere neue rowId aus PUT Response (spart extra GET!) + new_rowid = None + if isinstance(put_result, list) and len(put_result) > 0: + new_rowid = put_result[0].get('rowId') + elif isinstance(put_result, dict): + new_rowid = put_result.get('rowId') await sync_utils.release_sync_lock( entity_id, 'clean', - extra_fields={'advowareRowId': updated_advo.get('rowId')} + extra_fields={'advowareRowId': new_rowid} ) - context.logger.info(f"✅ Advoware aktualisiert") + context.logger.info(f"✅ Advoware aktualisiert, neue rowId: {new_rowid[:20] if new_rowid else 'N/A'}...") # ADVOWARE NEUER → Update EspoCRM elif comparison == 'advoware_newer': @@ -263,16 +287,18 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u # OPTIMIERT: Use merge utility merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper) - await advoware.api_call( + put_result = await advoware.api_call( f'api/v1/advonet/Beteiligte/{betnr}', method='PUT', data=merged_data ) - # Hole aktualisierte Entity um neue rowId zu bekommen - updated_advo = await advoware.api_call(f'api/v1/advonet/Beteiligte/{betnr}', method='GET') - if isinstance(updated_advo, list): - updated_advo = updated_advo[0] + # Extrahiere neue rowId aus PUT Response (spart extra GET!) + new_rowid = None + if isinstance(put_result, list) and len(put_result) > 0: + new_rowid = put_result[0].get('rowId') + elif isinstance(put_result, dict): + new_rowid = put_result.get('rowId') conflict_msg = ( f"EspoCRM: {espo_entity.get('modifiedAt')}, " @@ -284,6 +310,10 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u entity_id, espo_entity, advo_entity, + conflict_msg, + extra_fields={'advowareRowId': new_rowid} + ) + context.logger.info(f"✅ Konflikt gelöst (EspoCRM won), neue rowId: {new_rowid[:20] if new_rowid else 'N/A'}...") conflict_msg, extra_fields={'advowareRowId': updated_advo.get('rowId')} )