v1.7.5: Zahlenextraktion & Plausi-Logik verfeinert, Plausi-TS

- Zahlenextraktionsfunktionen (`get_numeric_filter_value`, `_get_numeric_value_for_plausi`) überarbeitet für robustere Behandlung von:
    - Tausendertrennern (Punkt, Apostroph, Leerzeichen).
    - Dezimaltrennzeichen (Komma, Punkt).
    - Annahme, dass Umsatz-Rohwerte im Sheet bereits in Mio. € sind (außer bei expliziten Einheiten wie Mrd/Tsd).
    - String-Input "0" wird in `_get_numeric_value_for_plausi` jetzt konsistent als "unbekannt" (NaN) behandelt.
- Methode `_check_financial_plausibility` angepasst:
    - Verwendet die korrigierten numerischen Werte für absolute Plausi-Checks.
    - Nutzt `get_numeric_filter_value` für den Vergleich von CRM- vs. Wiki-Umsatz (Mio.-Werte).
    - Default-Flags auf "NICHT_PRUEFBAR" geändert, wenn zugrundeliegende Zahlen NaN sind.
    - Logik für "FEHLER_FORMAT" präzisiert, um explizite "0"-Strings nicht fälschlicherweise als Formatfehler zu werten.
- Konsolidierungslogik in `_process_single_row` stellt sicher, dass `final_umsatz/ma_str_konsolidiert` korrekt "k.A." wird, wenn numerische Quellen 0 (im Sinne von unbekannt) sind.
- Plausibilitäts-Checks in `_process_single_row` verwenden nun die direkt zuvor neu konsolidierten Finanzwerte.
- Sicherstellung, dass der "Plausibilität Prüfdatum"-Timestamp (Spalte BM) auch bei Fehlern innerhalb des Plausi-Check-Blocks in `_process_single_row` gesetzt wird.
- Methode `run_plausibility_checks_batch` aktualisiert, um die korrigierte Konsolidierungs- und Plausi-Logik zu verwenden und den Plausi-Timestamp zu schreiben.
- Diverse Debug-Log-Ausgaben zur besseren Nachverfolgung der Zahlenverarbeitung und Plausi-Logik hinzugefügt/optimiert.
This commit is contained in:
2025-05-25 10:09:47 +00:00
parent f244e3ba7f
commit 6f049689a3

View File

@@ -808,14 +808,14 @@ def extract_numeric_value(raw_value, is_umsatz=False):
# Extrahiert und normalisiert Zahlenwerte fuer Vergleichslogik.
# Nutzt globale Helfer: clean_text, re.
# Globale Funktion (ersetzen Sie Ihre bestehende Version)
# Globale Funktion (ersetzen Sie Ihre bestehende Version)
def get_numeric_filter_value(value_str, is_umsatz=False):
logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__ + ".get_numeric_filter_value")
if value_str is None or pd.isna(value_str) or str(value_str).strip() == '':
return 0.0 if is_umsatz else 0
raw_value_str_original = str(value_str).strip()
# "0"-Strings und "k.A." für Filterlogik als 0 behandeln
if raw_value_str_original.lower() in ['k.a.', 'n/a', '-', '0', '0.0', '0,0', '0.00', '0,000', '0.000']:
return 0.0 if is_umsatz else 0
@@ -828,108 +828,52 @@ def get_numeric_filter_value(value_str, is_umsatz=False):
processed_value = re.sub(r'[€$£¥]', '', processed_value).strip()
processed_value = re.split(r'\s*(-||bis)\s*', processed_value, 1)[0].strip()
# Tausendertrenner entfernen (Apostroph, Punkt wenn nicht Dezimal)
# Komma zu Punkt für Dezimal machen
num_extraction_str = processed_value.replace("'", "")
num_extraction_str = processed_value.replace("'", "").replace(" ", "")
if not num_extraction_str: return 0.0 if is_umsatz else 0
# Fall 1: Enthält Punkt UND Komma (z.B. 1.234,56 oder 1,234.56)
if '.' in num_extraction_str and ',' in num_extraction_str:
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): # US-Stil: 1,234.56 -> Kommas entfernen
has_dot = '.' in num_extraction_str
has_comma = ',' in num_extraction_str
if has_dot and has_comma:
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','): # US: 1,234.56
num_extraction_str = num_extraction_str.replace(',', '')
else: # EU-Stil: 1.234,56 -> Punkte entfernen, Komma zu Punkt
else: # EU: 1.234,56
num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.')
# Fall 2: Enthält nur Komma (z.B. 1234,56) -> Komma zu Punkt
elif ',' in num_extraction_str:
num_extraction_str = num_extraction_str.replace(',', '.')
# Fall 3: Enthält nur Punkt(e) (z.B. 4.380 oder 123.45 oder 1.234.567)
elif '.' in num_extraction_str:
# Wenn der String klar ein Tausenderformat hat (Endet nicht mit .XX und hat Punkte)
if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str) and not re.search(r'\.\d{1,2}$', num_extraction_str):
elif has_comma: # Nur Kommas
if num_extraction_str.count(',') == 1 and re.search(r',\d{1,2}$', num_extraction_str) and not re.search(r',\d{3}', num_extraction_str):
num_extraction_str = num_extraction_str.replace(',', '.') # Dezimalkomma
else: # Tausenderkommas
num_extraction_str = num_extraction_str.replace(',', '')
elif has_dot: # Nur Punkte
if num_extraction_str.count('.') == 1 and re.search(r'\.\d{1,2}$', num_extraction_str) and not re.search(r'\.\d{3}', num_extraction_str):
pass # Dezimalpunkt, bleibt
else: # Tausenderpunkte
num_extraction_str = num_extraction_str.replace('.', '')
# Sonst ist ein einzelner Punkt ein Dezimalpunkt (z.B. "123.45")
# Bei mehreren Punkten, die nicht Tausenderformat sind (z.B. 1.2.3), wird es später als Fehler behandelt.
elif num_extraction_str.count('.') > 1:
logger.debug(f"get_numeric_filter_value: Mehrere Punkte in '{num_extraction_str}' (nicht als Tausenderformat erkannt).")
return 0.0 if is_umsatz else 0
match = re.search(r'([\d.\-]+)', num_extraction_str)
if not match:
# Finale Validierung mit Regex, ob es eine gültige Zahl ist
if not re.fullmatch(r'-?\d+(\.\d+)?', num_extraction_str):
logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{num_extraction_str}' (Original: '{raw_value_str_original}')")
return 0.0 if is_umsatz else 0
num_str_for_float = match.group(1)
if not num_str_for_float or num_str_for_float == '.' or num_str_for_float == '-' or \
(num_str_for_float.count('.') > 1) or \
(num_str_for_float.count('-') > 1) or \
(num_str_for_float.count('-') == 1 and not num_str_for_float.startswith('-')):
return 0.0 if is_umsatz else 0
num_as_float = float(num_str_for_float) # Dies ist der Wert, wie er im String steht (sollte Mio. € sein für Umsatz, wenn keine Einheit)
num_as_float = float(num_extraction_str)
scaled_num = num_as_float
original_lower = raw_value_str_original.lower()
if is_umsatz: # Zielwert soll in Millionen sein
if is_umsatz:
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
scaled_num = num_as_float * 1000.0 # z.B. "2 Mrd" -> num_as_float=2 -> 2000 Mio
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
scaled_num = num_as_float / 1000.0 # z.B. "500 Tsd" -> num_as_float=500 -> 0.5 Mio
# Ansonsten wird angenommen, dass num_as_float bereits den Wert in Mio. € darstellt (z.B. "173" aus CRM)
else: # Mitarbeiter (absolute Zahl)
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
scaled_num = num_as_float * 1000000000.0
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower):
scaled_num = num_as_float * 1000000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
scaled_num = num_as_float * 1000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
scaled_num = num_as_float / 1000.0
else: # Mitarbeiter
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): scaled_num = num_as_float * 1000000000.0
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): scaled_num = num_as_float * 1000000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): scaled_num = num_as_float * 1000.0
return scaled_num if scaled_num > 0 else (0.0 if is_umsatz else 0)
except ValueError as e:
logger.debug(f"get_numeric_filter_value: ValueError '{e}' bei Konvertierung von '{num_str_for_float if 'num_str_for_float' in locals() else raw_value_str_original[:30]}...'")
return 0.0 if is_umsatz else 0
except Exception as e_general:
logger.error(f"Unerwarteter Fehler in get_numeric_filter_value für '{raw_value_str_original[:50]}...': {e_general}")
logger.debug(traceback.format_exc())
return 0.0 if is_umsatz else 0
# Extrahiere die Zahl (kann jetzt einen Dezimalpunkt enthalten)
match = re.search(r'([\d.\-]+)', num_extraction_str)
if not match:
return 0.0 if is_umsatz else 0
num_str_for_float = match.group(1)
# Finale Prüfung des extrahierten Zahlenstrings
if not num_str_for_float or num_str_for_float == '.' or num_str_for_float == '-' or \
(num_str_for_float.count('.') > 1) or \
(num_str_for_float.count('-') > 1) or \
(num_str_for_float.count('-') == 1 and not num_str_for_float.startswith('-')):
return 0.0 if is_umsatz else 0
num_as_float = float(num_str_for_float)
scaled_num = num_as_float
original_lower = raw_value_str_original.lower()
if is_umsatz: # Wert soll in Mio. € sein
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
scaled_num = num_as_float * 1000.0 # von Mrd-Einheit zu Mio-Einheit
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
scaled_num = num_as_float / 1000.0 # von Tsd-Einheit zu Mio-Einheit
# Ansonsten wird angenommen, dass num_as_float bereits den Wert in Mio. € darstellt
else: # Mitarbeiter (absolute Zahl)
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
scaled_num = num_as_float * 1000000000.0
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower):
scaled_num = num_as_float * 1000000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
scaled_num = num_as_float * 1000.0
return scaled_num if scaled_num > 0 else (0.0 if is_umsatz else 0)
except ValueError as e:
logger.debug(f"get_numeric_filter_value: ValueError '{e}' bei Konvertierung von '{num_str_for_float if 'num_str_for_float' in locals() else raw_value_str_original[:30]}...'")
logger.debug(f"ValueError '{e}' bei Konvertierung (get_numeric_filter_value) von '{num_extraction_str if 'num_extraction_str' in locals() else raw_value_str_original[:30]}...'")
return 0.0 if is_umsatz else 0
except Exception as e_general:
logger.error(f"Unerwarteter Fehler in get_numeric_filter_value für '{raw_value_str_original[:50]}...': {e_general}")
@@ -4836,37 +4780,53 @@ class DataProcessor:
final_umsatz_str_konsolidiert = "FEHLER KONSO"
final_ma_str_konsolidiert = "FEHLER KONSO"
# --- NEU: 3f. Plausibilitäts-Checks durchführen (BB-BG) ---
self.logger.debug(f" -> Führe Plausibilitäts-Checks für Zeile {row_num_in_sheet} durch...")
# --- NEU: 3f. Plausibilitäts-Checks durchführen (BG-BM) ---
self.logger.debug(f" Zeile {row_num_in_sheet}: Führe Plausibilitäts-Checks durch...")
plausi_check_erfolgreich_durchgefuehrt = False # Flag
try:
# Erstelle ein Dictionary mit den benötigten Werten für den Plausi-Check
# ... (Erstellung von plausi_input_data wie zuvor) ...
plausi_input_data = {
"Finaler Umsatz (Wiki>CRM)": final_umsatz_str_konsolidiert,
"Finaler Mitarbeiter (Wiki>CRM)": final_ma_str_konsolidiert,
"CRM Umsatz": self._get_cell_value_safe(row_data, "CRM Umsatz"),
"Wiki Umsatz": final_wiki_data.get('umsatz', 'k.A.'), # Nutze den Wiki-Wert, der für Konsolidierung verwendet wurde
"Wiki Umsatz": self._get_cell_value_safe(row_data, "Wiki Umsatz"),
"CRM Anzahl Mitarbeiter": self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter"),
"Wiki Mitarbeiter": final_wiki_data.get('mitarbeiter', 'k.A.') # Nutze den Wiki-Wert, der für Konsolidierung verwendet wurde
"Wiki Mitarbeiter": self._get_cell_value_safe(row_data, "Wiki Mitarbeiter")
}
plausi_results = self._check_financial_plausibility(plausi_input_data)
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "FEHLER")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "FEHLER")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "FEHLER")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "FEHLER")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "FEHLER")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler bei Begründungserstellung")]]})
self.logger.debug(f" -> Plausi-Ergebnisse: U:{plausi_results.get('plaus_umsatz_flag')} MA:{plausi_results.get('plaus_ma_flag')} Ratio:{plausi_results.get('plaus_ratio_flag')} AbwU:{plausi_results.get('abweichung_umsatz_flag')} AbwMA:{plausi_results.get('abweichung_ma_flag')}")
except Exception as e_plausi:
self.logger.error(f"FEHLER bei Plausibilitäts-Checks für Zeile {row_num_in_sheet}: {e_plausi}")
# Setze Fehler-Flags, falls noch nicht geschehen
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [['FEHLER_CHECK']]})
# ... (ähnlich für andere Plausi-Spalten) ...
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[f"Systemfehler Plausi-Check: {str(e_plausi)[:50]}"]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_umsatz_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Mitarbeiter"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ma_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Umsatz/MA Ratio"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plaus_ratio_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung Umsatz CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_umsatz_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Abweichung MA CRM/Wiki"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("abweichung_ma_flag", "ERR_FLAG")]]})
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[plausi_results.get("plausi_begruendung_final", "Fehler Begr.")]]})
plausi_check_erfolgreich_durchgefuehrt = True # Markieren, dass der Check zumindest versucht wurde
except Exception as e_plausi_in_single_row:
self.logger.error(f"FEHLER bei Plausibilitäts-Checks in _process_single_row für Zeile {row_num_in_sheet}: {e_plausi_in_single_row}")
# Fehler-Flags für Plausi-Spalten setzen
error_val_plausi = [['FEHLER_PLAUSI_CALL']]
for key_flag_plausi in ["Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki"]:
if COLUMN_MAP.get(key_flag_plausi) is not None: # Sicherstellen, dass der Key existiert
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag_plausi] + 1)}{row_num_in_sheet}', 'values': error_val_plausi})
if COLUMN_MAP.get("Plausibilität Begründung") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_in_sheet}', 'values': [[f"Systemfehler Plausi-Call: {str(e_plausi_in_single_row)[:50]}"]]})
# Plausi-Timestamp setzen, wenn der Plausi-Block (3f) erreicht wurde
# (entweder erfolgreich oder mit Fehler im try-Block)
if COLUMN_MAP.get("Plausibilität Prüfdatum") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Prüfdatum"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
else:
self.logger.error("FEHLER: Schlüssel 'Plausibilität Prüfdatum' nicht in COLUMN_MAP. Kann Timestamp nicht setzen.")
# Setze den Timestamp letzte Pruefung (BI - vorher BB), da die ChatGPT-Evaluationen (und jetzt Plausi) liefen
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Pruefung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
# Setze den "Timestamp letzte Prüfung" (BO - für ChatGPT-Evaluationen), da dieser Block lief
if COLUMN_MAP.get("Timestamp letzte Prüfung") is not None:
updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Timestamp letzte Prüfung"] + 1)}{row_num_in_sheet}', 'values': [[now_timestamp]]})
else:
self.logger.error("FEHLER: Schlüssel 'Timestamp letzte Prüfung' nicht in COLUMN_MAP. Kann Haupt-Timestamp nicht setzen.")
# else if run_chat_step (aber nicht processing_needed):
# self.logger.debug(f"Zeile {row_num_in_sheet}: Ueberspringe CHATGPT Evaluationen und Plausi-Checks (Timestamp BI gesetzt, Wiki nicht aktualisiert und kein Re-Eval).")
@@ -7958,7 +7918,7 @@ class DataProcessor:
# Innerhalb der DataProcessor Klasse
def _get_numeric_value_for_plausi(self, value_str, is_umsatz=False):
logger = logging.getLogger(__name__ + "._get_numeric_value_for_plausi") # Eigener Logger
logger = logging.getLogger(__name__ + "._get_numeric_value_for_plausi")
if value_str is None or pd.isna(value_str): return np.nan
raw_value_str_clean = str(value_str).strip()
@@ -7973,58 +7933,53 @@ class DataProcessor:
temp_val = re.sub(r'[€$£¥]', '', temp_val).strip()
temp_val = re.split(r'\s*(-||bis)\s*', temp_val, 1)[0].strip()
num_extraction_str = temp_val.replace("'", "")
if '.' in num_extraction_str and ',' in num_extraction_str:
num_extraction_str = temp_val.replace("'", "").replace(" ", "")
if not num_extraction_str: return np.nan
has_dot = '.' in num_extraction_str
has_comma = ',' in num_extraction_str
if has_dot and has_comma:
if num_extraction_str.rfind('.') > num_extraction_str.rfind(','):
num_extraction_str = num_extraction_str.replace(',', '')
else:
num_extraction_str = num_extraction_str.replace('.', '').replace(',', '.')
elif ',' in num_extraction_str:
num_extraction_str = num_extraction_str.replace(',', '.')
elif '.' in num_extraction_str:
if re.fullmatch(r'^\d{1,3}(\.\d{3})+$', num_extraction_str) and not re.search(r'\.\d{1,2}$', num_extraction_str):
elif has_comma:
if num_extraction_str.count(',') == 1 and re.search(r',\d{1,2}$', num_extraction_str) and not re.search(r',\d{3}', num_extraction_str):
num_extraction_str = num_extraction_str.replace(',', '.')
else:
num_extraction_str = num_extraction_str.replace(',', '')
elif has_dot:
if num_extraction_str.count('.') == 1 and re.search(r'\.\d{1,2}$', num_extraction_str) and not re.search(r'\.\d{3}', num_extraction_str):
pass
else:
num_extraction_str = num_extraction_str.replace('.', '')
elif num_extraction_str.count('.') > 1:
logger.debug(f"Mehrere Punkte in '{num_extraction_str}' (nicht als Tausenderformat erkannt).")
return np.nan
match = re.search(r'([\d.\-]+)', num_extraction_str)
if not match:
logger.debug(f"Kein numerischer Match in '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.")
if not re.fullmatch(r'-?\d+(\.\d+)?', num_extraction_str):
logger.debug(f"Kein gültiger numerischer String nach Trennzeichenbehandlung: '{num_extraction_str}' (Original: '{raw_value_str_clean}')")
return np.nan
num_str_for_float = match.group(1)
try:
if not num_str_for_float or num_str_for_float == '.' or num_str_for_float == '-' or \
(num_str_for_float.count('.') > 1) or \
(num_str_for_float.count('-') > 1) or \
(num_str_for_float.count('-') == 1 and not num_str_for_float.startswith('-')):
raise ValueError("Ungültiger Zahlenstring nach Regex")
num_val = float(num_str_for_float)
try:
num_val = float(num_extraction_str)
original_lower = raw_value_str_clean.lower()
final_num_absolute = num_val
if is_umsatz: # Ziel ist der absolute Euro-Betrag
if is_umsatz:
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower):
final_num_absolute = num_val * 1000000000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower):
final_num_absolute = num_val * 1000.0
else: # Annahme: num_val ist bereits in Mio (z.B. "173" aus CRM), konvertiere zu absolutem Euro
else:
final_num_absolute = num_val * 1000000.0
else: # Mitarbeiter (absolute Zahl, außer bei expliziten Einheiten)
else:
if re.search(r'\bmrd\s*\b|\bmilliarden\s*\b|\bbillion\s*\b', original_lower): final_num_absolute = num_val * 1000000000.0
elif re.search(r'\bmio\s*\b|\bmillionen\s*\b|\bmill[.]?\s*\b', original_lower): final_num_absolute = num_val * 1000000.0
elif re.search(r'\btsd\s*\b|\btausend\s*\b', original_lower): final_num_absolute = num_val * 1000.0
# Explizite "0"-Strings wurden oben bereits zu NaN.
# Wenn final_num_absolute hier 0.0 ist, dann kam es von einer Berechnung (z.B. "0 Tsd").
# Die Plausi-Checks können dann mit diesem numerischen Wert 0.0 arbeiten.
return final_num_absolute
except ValueError as e:
logger.debug(f"ValueError '{e}' bei Konvertierung von '{num_str_for_float}' (von '{raw_value_str_clean}') -> NaN.")
logger.debug(f"ValueError '{e}' bei Konvertierung von '{num_extraction_str}' (von '{raw_value_str_clean}') -> NaN.")
return np.nan
except Exception as e_general:
logger.error(f"Unerwarteter Fehler in _get_numeric_value_for_plausi für '{raw_value_str_clean[:50]}...': {e_general}")