sync_manager.py aktualisiert
This commit is contained in:
@@ -35,8 +35,6 @@ class SyncManager:
|
|||||||
self.sheet_handler = sheet_handler
|
self.sheet_handler = sheet_handler
|
||||||
self.d365_export_path = d365_export_path
|
self.d365_export_path = d365_export_path
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# <<< KORREKTUR: Der Name wird erst nach der Verbindung abgerufen.
|
|
||||||
self.target_sheet_name = None
|
self.target_sheet_name = None
|
||||||
|
|
||||||
# Definiert, welche D365-Spalten welchen GSheet-Spalten entsprechen.
|
# Definiert, welche D365-Spalten welchen GSheet-Spalten entsprechen.
|
||||||
@@ -60,7 +58,7 @@ class SyncManager:
|
|||||||
"CRM Umsatz", "CRM Anzahl Mitarbeiter"]
|
"CRM Umsatz", "CRM Anzahl Mitarbeiter"]
|
||||||
self.smart_merge_cols = ["CRM Website"]
|
self.smart_merge_cols = ["CRM Website"]
|
||||||
|
|
||||||
def _load_data(self):
|
def _load_data(self):
|
||||||
"""Lädt Daten aus der D365-Exportdatei und dem Google Sheet."""
|
"""Lädt Daten aus der D365-Exportdatei und dem Google Sheet."""
|
||||||
self.logger.info(f"Lade Daten aus D365-Export: '{self.d365_export_path}'...")
|
self.logger.info(f"Lade Daten aus D365-Export: '{self.d365_export_path}'...")
|
||||||
try:
|
try:
|
||||||
@@ -91,22 +89,17 @@ class SyncManager:
|
|||||||
self.logger.warning("Google Sheet scheint leer zu sein oder enthält keinen Header. Erstelle leeres DataFrame.")
|
self.logger.warning("Google Sheet scheint leer zu sein oder enthält keinen Header. Erstelle leeres DataFrame.")
|
||||||
self.gsheet_df = pd.DataFrame(columns=COLUMN_ORDER).fillna('')
|
self.gsheet_df = pd.DataFrame(columns=COLUMN_ORDER).fillna('')
|
||||||
else:
|
else:
|
||||||
# --- ROBUSTE DATENFRAME-ERSTELLUNG START ---
|
|
||||||
# 1. Nimm die korrekte Header-Zeile aus dem Sheet als Spaltennamen
|
|
||||||
actual_header = all_data_with_headers[self.sheet_handler._header_rows - 1]
|
actual_header = all_data_with_headers[self.sheet_handler._header_rows - 1]
|
||||||
data_rows = all_data_with_headers[self.sheet_handler._header_rows:]
|
data_rows = all_data_with_headers[self.sheet_handler._header_rows:]
|
||||||
|
|
||||||
temp_df = pd.DataFrame(data_rows, columns=actual_header).fillna('')
|
temp_df = pd.DataFrame(data_rows, columns=actual_header).fillna('')
|
||||||
|
|
||||||
# 2. Füge fehlende Spalten hinzu, die in unserer Config definiert sind
|
|
||||||
for col_name in COLUMN_ORDER:
|
for col_name in COLUMN_ORDER:
|
||||||
if col_name not in temp_df.columns:
|
if col_name not in temp_df.columns:
|
||||||
self.logger.warning(f"Spalte '{col_name}' fehlt im Google Sheet und wird hinzugefügt.")
|
self.logger.warning(f"Spalte '{col_name}' fehlt im Google Sheet und wird hinzugefügt.")
|
||||||
temp_df[col_name] = ''
|
temp_df[col_name] = ''
|
||||||
|
|
||||||
# 3. Stelle sicher, dass die Spaltenreihenfolge exakt unserer Config entspricht
|
|
||||||
self.gsheet_df = temp_df[COLUMN_ORDER]
|
self.gsheet_df = temp_df[COLUMN_ORDER]
|
||||||
# --- ROBUSTE DATENFRAME-ERSTELLUNG ENDE ---
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.critical(f"Fehler beim Laden oder Umwandeln der Google Sheet Daten: {e}", exc_info=True)
|
self.logger.critical(f"Fehler beim Laden oder Umwandeln der Google Sheet Daten: {e}", exc_info=True)
|
||||||
@@ -122,7 +115,6 @@ class SyncManager:
|
|||||||
if not self._load_data():
|
if not self._load_data():
|
||||||
return
|
return
|
||||||
|
|
||||||
# <<< KORREKTUR: Sheet-Namen nach erfolgreichem Laden abrufen
|
|
||||||
self.target_sheet_name = self.sheet_handler.get_main_sheet_name()
|
self.target_sheet_name = self.sheet_handler.get_main_sheet_name()
|
||||||
if not self.target_sheet_name:
|
if not self.target_sheet_name:
|
||||||
self.logger.critical("Konnte den Namen des Ziel-Sheets nicht ermitteln. Breche ab.")
|
self.logger.critical("Konnte den Namen des Ziel-Sheets nicht ermitteln. Breche ab.")
|
||||||
@@ -140,7 +132,6 @@ class SyncManager:
|
|||||||
updates_to_batch = []
|
updates_to_batch = []
|
||||||
rows_to_append = []
|
rows_to_append = []
|
||||||
|
|
||||||
# 1. Neue Accounts verarbeiten
|
|
||||||
if new_ids:
|
if new_ids:
|
||||||
new_accounts_df = self.d365_df[self.d365_df['CRM ID'].isin(new_ids)]
|
new_accounts_df = self.d365_df[self.d365_df['CRM ID'].isin(new_ids)]
|
||||||
for _, row in new_accounts_df.iterrows():
|
for _, row in new_accounts_df.iterrows():
|
||||||
@@ -151,19 +142,16 @@ class SyncManager:
|
|||||||
new_row_data[col_idx] = row[d365_col]
|
new_row_data[col_idx] = row[d365_col]
|
||||||
rows_to_append.append(new_row_data)
|
rows_to_append.append(new_row_data)
|
||||||
|
|
||||||
# 2. Gelöschte/Archivierte Accounts verarbeiten
|
|
||||||
if deleted_ids:
|
if deleted_ids:
|
||||||
for crm_id in deleted_ids:
|
for crm_id in deleted_ids:
|
||||||
row_indices = self.gsheet_df[self.gsheet_df['CRM ID'] == crm_id].index
|
row_indices = self.gsheet_df[self.gsheet_df['CRM ID'] == crm_id].index
|
||||||
if not row_indices.empty:
|
if not row_indices.empty:
|
||||||
row_idx = row_indices[0]
|
row_idx = row_indices[0]
|
||||||
updates_to_batch.append({
|
updates_to_batch.append({
|
||||||
# KORREKTUR: Dein Handler erwartet den Range ohne Sheet-Namen
|
|
||||||
"range": f"{COLUMN_MAP['Archiviert']['Titel']}{row_idx + 2}",
|
"range": f"{COLUMN_MAP['Archiviert']['Titel']}{row_idx + 2}",
|
||||||
"values": [["TRUE"]]
|
"values": [["TRUE"]]
|
||||||
})
|
})
|
||||||
|
|
||||||
# 3. Bestehende Accounts intelligent mergen
|
|
||||||
if existing_ids:
|
if existing_ids:
|
||||||
d365_indexed = self.d365_df.set_index('CRM ID')
|
d365_indexed = self.d365_df.set_index('CRM ID')
|
||||||
gsheet_indexed = self.gsheet_df.set_index('CRM ID')
|
gsheet_indexed = self.gsheet_df.set_index('CRM ID')
|
||||||
@@ -176,14 +164,12 @@ class SyncManager:
|
|||||||
conflict_messages = []
|
conflict_messages = []
|
||||||
needs_reeval = False
|
needs_reeval = False
|
||||||
|
|
||||||
# Strategie 1: D365 gewinnt immer
|
|
||||||
for gsheet_col in self.d365_wins_cols:
|
for gsheet_col in self.d365_wins_cols:
|
||||||
d365_col = next((k for k, v in self.d365_to_gsheet_map.items() if v == gsheet_col), None)
|
d365_col = next((k for k, v in self.d365_to_gsheet_map.items() if v == gsheet_col), None)
|
||||||
if d365_col and d365_col in d365_row and str(d365_row[d365_col]) != str(gsheet_row[gsheet_col]):
|
if d365_col and d365_col in d365_row and str(d365_row[d365_col]) != str(gsheet_row[gsheet_col]):
|
||||||
row_updates[gsheet_col] = str(d365_row[d365_col])
|
row_updates[gsheet_col] = str(d365_row[d365_col])
|
||||||
needs_reeval = True
|
needs_reeval = True
|
||||||
|
|
||||||
# Strategie 2: Smart-Merge
|
|
||||||
for gsheet_col in self.smart_merge_cols:
|
for gsheet_col in self.smart_merge_cols:
|
||||||
d365_col = next((k for k, v in self.d365_to_gsheet_map.items() if v == gsheet_col), None)
|
d365_col = next((k for k, v in self.d365_to_gsheet_map.items() if v == gsheet_col), None)
|
||||||
d365_val = str(d365_row.get(d365_col, ''))
|
d365_val = str(d365_row.get(d365_col, ''))
|
||||||
@@ -195,24 +181,20 @@ class SyncManager:
|
|||||||
elif d365_val and gsheet_val and d365_val != gsheet_val:
|
elif d365_val and gsheet_val and d365_val != gsheet_val:
|
||||||
conflict_messages.append(f"{gsheet_col}_CONFLICT: D365='{d365_val}' | GSHEET='{gsheet_val}'")
|
conflict_messages.append(f"{gsheet_col}_CONFLICT: D365='{d365_val}' | GSHEET='{gsheet_val}'")
|
||||||
|
|
||||||
# Updates und Flags
|
|
||||||
if conflict_messages:
|
if conflict_messages:
|
||||||
row_updates["SyncConflict"] = "; ".join(conflict_messages)
|
row_updates["SyncConflict"] = "; ".join(conflict_messages)
|
||||||
|
|
||||||
if needs_reeval:
|
if needs_reeval:
|
||||||
row_updates["ReEval Flag"] = "x"
|
row_updates["ReEval Flag"] = "x"
|
||||||
|
|
||||||
# Batch-Update-Objekte
|
|
||||||
if row_updates:
|
if row_updates:
|
||||||
row_idx = gsheet_indexed.index.get_loc(crm_id)
|
row_idx = gsheet_indexed.index.get_loc(crm_id)
|
||||||
for col_name, value in row_updates.items():
|
for col_name, value in row_updates.items():
|
||||||
updates_to_batch.append({
|
updates_to_batch.append({
|
||||||
# KORREKTUR: Dein Handler erwartet den Range ohne Sheet-Namen
|
|
||||||
"range": f"{COLUMN_MAP[col_name]['Titel']}{row_idx + 2}",
|
"range": f"{COLUMN_MAP[col_name]['Titel']}{row_idx + 2}",
|
||||||
"values": [[value]]
|
"values": [[value]]
|
||||||
})
|
})
|
||||||
|
|
||||||
# 4. Änderungen ins Google Sheet schreiben
|
|
||||||
if rows_to_append:
|
if rows_to_append:
|
||||||
self.logger.info(f"Füge {len(rows_to_append)} neue Zeilen zum Google Sheet hinzu...")
|
self.logger.info(f"Füge {len(rows_to_append)} neue Zeilen zum Google Sheet hinzu...")
|
||||||
self.sheet_handler.append_rows(sheet_name=self.target_sheet_name, values=rows_to_append)
|
self.sheet_handler.append_rows(sheet_name=self.target_sheet_name, values=rows_to_append)
|
||||||
|
|||||||
Reference in New Issue
Block a user