diff --git a/sync_manager.py b/sync_manager.py index 4676fd73..97cf24a8 100644 --- a/sync_manager.py +++ b/sync_manager.py @@ -464,4 +464,113 @@ class SyncManager: else: self.logger.info("--> Ergebnis: Werte sind IDENTISCH.") - self.logger.info("========== ENDE SYNC-DEBUG-MODUS ==========") \ No newline at end of file + self.logger.info("========== ENDE SYNC-DEBUG-MODUS ==========") + + def simulate_sync(self): + """ + Führt eine reine "Trockenlauf"-Analyse des Sync-Prozesses durch, ohne Daten zu schreiben. + Gibt einen detaillierten Bericht über alle potenziellen Änderungen aus. + """ + self.logger.info("========== START SYNC-SIMULATION ==========") + if not self._load_data(): + self.logger.error("Simulation abgebrochen, da das Laden der Daten fehlschlug.") + return + + # Die Analyse-Logik ist identisch zum echten Lauf + d365_ids = set(self.d365_df['CRM ID'].dropna()) + gsheet_ids = set(self.gsheet_df['CRM ID'].dropna()) + new_ids = d365_ids - gsheet_ids + existing_ids = d365_ids.intersection(gsheet_ids) + + simulation_results = defaultdict(list) + + # 1. Bestehende Accounts analysieren + if existing_ids: + d365_indexed = self.d365_df.set_index('CRM ID') + gsheet_to_update_df = self.gsheet_df[self.gsheet_df['CRM ID'].isin(existing_ids)] + + for _, gsheet_row in gsheet_to_update_df.iterrows(): + crm_id = gsheet_row['CRM ID'] + d365_row = d365_indexed.loc[crm_id] + + changes = [] + conflicts = [] + needs_reeval = False + + for gsheet_col in self.d365_wins_cols: + d365_val = str(d365_row[gsheet_col]).strip() + gsheet_val = str(gsheet_row[gsheet_col]).strip() + + trigger_update = False + if gsheet_col == 'CRM Land': + d365_code_lower, gsheet_val_lower = d365_val.lower(), gsheet_val.lower() + d365_translated = Config.COUNTRY_CODE_MAP.get(d365_code_lower, d365_code_lower).lower() + if gsheet_val_lower != d365_code_lower and gsheet_val_lower != d365_translated: + trigger_update = True + elif gsheet_col == 'CRM Anzahl Techniker': + semantically_empty = ['', '0', '-1'] + if d365_val in semantically_empty and gsheet_val in semantically_empty: pass + elif d365_val != gsheet_val: trigger_update = True + elif gsheet_col == 'CRM Branche': + if gsheet_row['Chat Vorschlag Branche'] == '' and d365_val != gsheet_val: + trigger_update = True + elif gsheet_col == 'CRM Umsatz': + if gsheet_row['Wiki Umsatz'] == '' and d365_val != gsheet_val: + trigger_update = True + elif gsheet_col == 'CRM Anzahl Mitarbeiter': + if gsheet_row['Wiki Mitarbeiter'] == '' and d365_val != gsheet_val: + trigger_update = True + elif gsheet_col == 'CRM Beschreibung': + if gsheet_row['Website Zusammenfassung'] == '' and d365_val != gsheet_val: + trigger_update = True + else: + if d365_val != gsheet_val: trigger_update = True + + if trigger_update: + changes.append(f"UPDATE: {gsheet_col} von '{gsheet_val}' zu '{d365_val}'") + needs_reeval = True + + for gsheet_col in self.smart_merge_cols: + d365_val = str(d365_row.get(gsheet_col, '')).strip() + gsheet_val = str(gsheet_row.get(gsheet_col, '')).strip() + if d365_val and gsheet_val and d365_val != gsheet_val: + conflicts.append(f"CONFLICT: {gsheet_col} (D365='{d365_val}' vs GSheet='{gsheet_val}')") + + if changes or conflicts: + account_name = d365_row.get('CRM Name', 'Unbekannt') + key = f"ACCOUNT: {crm_id} ({account_name})" + simulation_results[key].extend(changes) + simulation_results[key].extend(conflicts) + if needs_reeval: + simulation_results[key].append("AKTION: ReEval Flag würde gesetzt werden.") + + # 2. Den Bericht generieren und ausgeben + self.logger.info("\n\n" + "="*80) + self.logger.info(" S Y N C S I M U L A T I O N S B E R I C H T") + self.logger.info("="*80) + + self.logger.info(f"\n--- ZUSAMMENFASSUNG ---") + self.logger.info(f"Accounts im D365-Export: {len(d365_ids)}") + self.logger.info(f"Accounts im Google Sheet: {len(gsheet_ids)}") + self.logger.info(f"--> {len(new_ids)} NEUE Accounts würden hinzugefügt.") + self.logger.info(f"--> {len(simulation_results)} BESTEHENDE Accounts würden geändert.") + self.logger.info(f"--> {len(existing_ids) - len(simulation_results)} bestehende Accounts bleiben UNVERÄNDERT.") + self.logger.info("-" * 80) + + if new_ids: + self.logger.info(f"\n--- {len(new_ids)} NEUE ACCOUNTS ---") + new_accounts_df = self.d365_df[self.d365_df['CRM ID'].isin(new_ids)] + for _, row in new_accounts_df.head(20).iterrows(): # Zeige maximal die ersten 20 + self.logger.info(f" - NEU: {row['CRM ID']} ({row['CRM Name']})") + if len(new_ids) > 20: self.logger.info(" - ... und weitere.") + + if simulation_results: + self.logger.info(f"\n--- {len(simulation_results)} ZU AKTUALISIERENDE ACCOUNTS ---") + for account, details in simulation_results.items(): + self.logger.info(account) + for detail in details: + self.logger.info(f" - {detail}") + + self.logger.info("\n" + "="*80) + self.logger.info(" S I M U L A T I O N B E E N D E T") + self.logger.info("="*80) \ No newline at end of file