diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 2f550853..ae443be8 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -8157,7 +8157,12 @@ class DataProcessor: def run_plausibility_checks_batch(self, start_sheet_row=None, end_sheet_row=None, limit=None): - self.logger.info(f"Starte Modus 'Plausibilitäts-Checks'. Bereich: {start_sheet_row if start_sheet_row is not None else 'Datenstart'} bis {end_sheet_row if end_sheet_row else 'Sheet-Ende'}, Limit: {limit if limit is not None else 'Unbegrenzt'}") + self.logger.info(f"Starte Modus 'Plausibilitäts-Checks mit Konsolidierung'. Bereich: {start_sheet_row if start_sheet_row is not None else 'Datenstart'} bis {end_sheet_row if end_sheet_row else 'Sheet-Ende'}, Limit: {limit if limit is not None else 'Unbegrenzt'}") + + plausi_ts_key = "Plausibilität Prüfdatum" + if plausi_ts_key not in COLUMN_MAP: + self.logger.error(f"FEHLER: Schlüssel '{plausi_ts_key}' nicht in COLUMN_MAP. Abbruch.") + return if not self.sheet_handler.load_data(): self.logger.error("Konnte Sheet-Daten nicht laden für Plausi-Checks. Abbruch.") @@ -8166,28 +8171,36 @@ class DataProcessor: all_data = self.sheet_handler.get_all_data_with_headers() header_offset = self.sheet_handler._header_rows - # Benötigte Spalten für den Input des Plausi-Checks - required_input_keys = [ - "Finaler Umsatz (Wiki>CRM)", "Finaler Mitarbeiter (Wiki>CRM)", + # Spalten für Konsolidierungs-Input + kons_input_keys = [ "CRM Umsatz", "Wiki Umsatz", "CRM Anzahl Mitarbeiter", "Wiki Mitarbeiter" ] - # Spalten für den Output - output_keys = [ + # Spalten für Konsolidierungs-Output (und Plausi-Input) + kons_output_keys = [ + "Finaler Umsatz (Wiki>CRM)", "Finaler Mitarbeiter (Wiki>CRM)" + ] + # Spalten für Plausi-Output + plausi_output_keys = [ "Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", - "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki", "Plausibilität Begründung" + "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki", "Plausibilität Begründung", + plausi_ts_key # Inklusive des neuen Timestamps ] - # Prüfen, ob alle Keys da sind (vereinfacht) - if not all(key in COLUMN_MAP for key in required_input_keys + output_keys): - self.logger.error("Nicht alle benötigten Spalten für Plausi-Checks in COLUMN_MAP. Abbruch.") + all_needed_keys = kons_input_keys + kons_output_keys + plausi_output_keys + if not all(key in COLUMN_MAP for key in all_needed_keys): + missing_k = [k for k in all_needed_keys if k not in COLUMN_MAP] + self.logger.error(f"Nicht alle benötigten Spalten ({missing_k}) für Plausi-Checks mit Konsolidierung in COLUMN_MAP. Abbruch.") return updates_fuer_sheet = [] processed_rows_count = 0 + now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") effective_start_row = start_sheet_row if start_sheet_row is not None else header_offset + 1 effective_end_row = end_sheet_row if end_sheet_row is not None else len(all_data) + self.logger.info(f"Prüfe und konsolidiere Zeilen {effective_start_row} bis {effective_end_row} für Plausi-Checks.") + for row_num_sheet in range(effective_start_row, effective_end_row + 1): if limit is not None and processed_rows_count >= limit: self.logger.info(f"Limit von {limit} Zeilen für Plausi-Checks erreicht.") @@ -8197,55 +8210,101 @@ class DataProcessor: if row_list_idx >= len(all_data): break row_data = all_data[row_list_idx] - # --- BEGINN NEUER DEBUG-BLOCK --- - crm_name_log = self._get_cell_value_safe(row_data, "CRM Name") - final_u_val_log = self._get_cell_value_safe(row_data, "Finaler Umsatz (Wiki>CRM)") - final_m_val_log = self._get_cell_value_safe(row_data, "Finaler Mitarbeiter (Wiki>CRM)") - self.logger.debug(f"Zeile {row_num_sheet} ({crm_name_log[:30]}...): Werte für Plausi-Vorabcheck -> Finaler U: '{final_u_val_log}', Finaler MA: '{final_m_val_log}'") - # --- ENDE NEUER DEBUG-BLOCK --- - - plausi_input_data = {} - valid_input_for_check = True - for key in required_input_keys: - val = self._get_cell_value_safe(row_data, key) - plausi_input_data[key] = val - if key in ["Finaler Umsatz (Wiki>CRM)", "Finaler Mitarbeiter (Wiki>CRM)"] and \ - (not val or str(val).lower() == 'k.a.' or str(val).upper().startswith("FEHLER")): - valid_input_for_check = False - self.logger.debug(f"Zeile {row_num_sheet}: Setze valid_input_for_check=False, da '{key}' den Wert '{str(val)[:30]}...' hat.") - break - - if not valid_input_for_check: - self.logger.debug(f"Zeile {row_num_sheet}: Übersprungen für Plausi-Check NACH Detailprüfung (valid_input_for_check=False).") - continue - - processed_rows_count +=1 - + # --- 1. Konsolidierung (Logik aus _process_single_row, Block 3e) --- + final_umsatz_str_konsolidiert = "k.A." + final_ma_str_konsolidiert = "k.A." + crm_umsatz_val_str = self._get_cell_value_safe(row_data, "CRM Umsatz") + # Für Wiki-Werte: Wenn _process_single_row nicht lief, sind final_wiki_data nicht aktuell. + # Wir müssen die Wiki-Werte direkt aus row_data lesen für diesen Modus. + wiki_umsatz_val_str = self._get_cell_value_safe(row_data, "Wiki Umsatz") + crm_ma_val_str = self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter") + wiki_ma_val_str = self._get_cell_value_safe(row_data, "Wiki Mitarbeiter") try: - plausi_results = self._check_financial_plausibility(plausi_input_data) - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "FEHLER")]]}) - # ... (alle anderen Plausi-Spalten hinzufügen) ... - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "FEHLER")]]}) - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "FEHLER")]]}) - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "FEHLER")]]}) - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "FEHLER")]]}) - updates_fuer_sheet.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler Begr.")]]}) + num_crm_umsatz = get_numeric_filter_value(crm_umsatz_val_str, is_umsatz=True) + num_wiki_umsatz = get_numeric_filter_value(wiki_umsatz_val_str, is_umsatz=True) + num_crm_ma = get_numeric_filter_value(crm_ma_val_str, is_umsatz=False) + num_wiki_ma = get_numeric_filter_value(wiki_ma_val_str, is_umsatz=False) - except Exception as e_plausi_run: - self.logger.error(f"Fehler im Plausi-Check Lauf für Zeile {row_num_sheet}: {e_plausi_run}") - # ... (Fehlerwerte in Plausi-Spalten schreiben) ... + final_num_umsatz = num_wiki_umsatz if num_wiki_umsatz > 0 else num_crm_umsatz + final_num_ma = num_wiki_ma if num_wiki_ma > 0 else num_crm_ma + + # String-Konvertierung (0 wird zu "k.A." wenn es aus leer/k.A. Quellen kommt, + # oder zu "0" wenn es z.B. aus "173" (Euro) gerundet wird) + final_umsatz_str_konsolidiert = str(int(round(final_num_umsatz))) if final_num_umsatz > 0 or (final_num_umsatz == 0 and crm_umsatz_val_str == "0" and wiki_umsatz_val_str == "0" ) else 'k.A.' + final_ma_str_konsolidiert = str(int(round(final_num_ma))) if final_num_ma > 0 or (final_num_ma == 0 and crm_ma_val_str == "0" and wiki_ma_val_str == "0") else 'k.A.' - if len(updates_fuer_sheet) >= getattr(Config, 'UPDATE_BATCH_ROW_LIMIT', 50) * 6: # 6 Spalten - self.logger.info(f"Sende Plausi-Check Batch-Update ({len(updates_fuer_sheet)//6} Zeilen)...") + except Exception as e_conso_direct: + self.logger.error(f"Fehler bei direkter Konsolidierung in Plausi-Check für Zeile {row_num_sheet}: {e_conso_direct}") + final_umsatz_str_konsolidiert = "FEHLER_KONSO_DIREKT" + final_ma_str_konsolidiert = "FEHLER_KONSO_DIREKT" + + # Update-Dicts für die konsolidierten Werte vorbereiten + current_row_updates = [] + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Umsatz (Wiki>CRM)"] + 1)}{row_num_sheet}', 'values': [[final_umsatz_str_konsolidiert]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Finaler Mitarbeiter (Wiki>CRM)"] + 1)}{row_num_sheet}', 'values': [[final_ma_str_konsolidiert]]}) + + + # --- 2. Plausibilitäts-Checks (basierend auf den gerade konsolidierten Werten) --- + # Bedingung, um Plausi-Check überhaupt zu starten (Input-Werte müssen vorhanden sein) + valid_input_for_plausi_check = True + if final_umsatz_str_konsolidiert.upper().startswith("FEHLER") or final_umsatz_str_konsolidiert.lower() == 'k.a.' or \ + final_ma_str_konsolidiert.upper().startswith("FEHLER") or final_ma_str_konsolidiert.lower() == 'k.a.': + # Wenn einer der Hauptwerte schon "k.A." oder Fehler ist, sind viele Plausi-Checks nicht sinnvoll + # Wir setzen die Flags auf NICHT_PRUEFBAR oder spezifische Fehler und die Begründung + plausi_results = { + "plaus_umsatz_flag": "NICHT_PRUEFBAR" if final_umsatz_str_konsolidiert.lower() == 'k.a.' else "FEHLER_INPUT", + "plaus_ma_flag": "NICHT_PRUEFBAR" if final_ma_str_konsolidiert.lower() == 'k.a.' else "FEHLER_INPUT", + "plaus_ratio_flag": "NICHT_PRUEFBAR", + "abweichung_umsatz_flag": "N/A", + "abweichung_ma_flag": "N/A", + "plausi_begruendung_final": f"Input für Plausi-Check unvollständig/fehlerhaft (U: {final_umsatz_str_konsolidiert}, MA: {final_ma_str_konsolidiert})." + } + valid_input_for_plausi_check = False # Für das Zählen + + if valid_input_for_plausi_check: + processed_rows_count +=1 # Zähle nur, wenn Plausi-Check tatsächlich durchgeführt wird + try: + plausi_input_data = { + "Finaler Umsatz (Wiki>CRM)": final_umsatz_str_konsolidiert, + "Finaler Mitarbeiter (Wiki>CRM)": final_ma_str_konsolidiert, + "CRM Umsatz": crm_umsatz_val_str, # Originalwerte für Abweichungscheck + "Wiki Umsatz": wiki_umsatz_val_str, + "CRM Anzahl Mitarbeiter": crm_ma_val_str, + "Wiki Mitarbeiter": wiki_ma_val_str + } + plausi_results = self._check_financial_plausibility(plausi_input_data) + except Exception as e_plausi_run: + self.logger.error(f"Fehler im Plausi-Check Lauf für Zeile {row_num_sheet}: {e_plausi_run}") + plausi_results = { # Fehler-Defaults + "plaus_umsatz_flag": "FEHLER_CHECK_RUNTIME", "plaus_ma_flag": "FEHLER_CHECK_RUNTIME", + "plaus_ratio_flag": "FEHLER_CHECK_RUNTIME", "abweichung_umsatz_flag": "FEHLER_CHECK_RUNTIME", + "abweichung_ma_flag": "FEHLER_CHECK_RUNTIME", + "plausi_begruendung_final": f"Systemfehler Plausi-Check: {str(e_plausi_run)[:100]}" + } + + # Plausi-Ergebnisse zu Updates hinzufügen + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "ERR_FLAG")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "ERR_FLAG")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "ERR_FLAG")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "ERR_FLAG")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "ERR_FLAG")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler Begr.")]]}) + current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[plausi_ts_key] + 1)}{row_num_sheet}', 'values': [[now_timestamp_str]]}) + + updates_fuer_sheet.extend(current_row_updates) + + # Batch-Update Logik (Anzahl Spalten: 2 Konsolidierung + 6 Plausi + 1 TS = 9) + if len(updates_fuer_sheet) >= getattr(Config, 'UPDATE_BATCH_ROW_LIMIT', 50) * 9: + self.logger.info(f"Sende Plausi-Check & Konsolidierungs Batch-Update ({len(updates_fuer_sheet)//9} Zeilen)...") self.sheet_handler.batch_update_cells(updates_fuer_sheet) updates_fuer_sheet = [] if updates_fuer_sheet: - self.logger.info(f"Sende finalen Plausi-Check Batch-Update ({len(updates_fuer_sheet)//6} Zeilen)...") + self.logger.info(f"Sende finalen Plausi-Check & Konsolidierungs Batch-Update ({len(updates_fuer_sheet)//9} Zeilen)...") self.sheet_handler.batch_update_cells(updates_fuer_sheet) - self.logger.info(f"Plausibilitäts-Check-Lauf beendet. {processed_rows_count} Zeilen geprüft.") + self.logger.info(f"Plausibilitäts-Check-Lauf (mit Konsolidierung) beendet. {processed_rows_count} Zeilen geprüft/konsolidiert.") # ========================================================================== # === Utility Methods (ML Data Prep & Training) ============================