bugfix
This commit is contained in:
@@ -8194,220 +8194,270 @@ class DataProcessor:
|
||||
|
||||
|
||||
def _check_financial_plausibility(self, row_data_dict):
|
||||
results = { # Defaults
|
||||
results = {
|
||||
"plaus_umsatz_flag": "NICHT_PRUEFBAR", "plaus_ma_flag": "NICHT_PRUEFBAR",
|
||||
"plaus_ratio_flag": "NICHT_PRUEFBAR", "abweichung_umsatz_flag": "N/A",
|
||||
"abweichung_ma_flag": "N/A", "begruendungen": []
|
||||
"abweichung_ma_flag": "N/A", "begruendungen": [], # Wird jetzt zu 'plausi_begruendung_final' am Ende
|
||||
"plausi_begruendung_final": "Plausibilität OK" # Default, wird überschrieben wenn es Begründungen gibt
|
||||
}
|
||||
|
||||
# Hole den Parent Account Name aus dem übergebenen Dictionary
|
||||
parent_account_name_d_val = row_data_dict.get("Parent Account Name", "").strip() # Spalte D
|
||||
is_konzern_tochter = bool(parent_account_name_d_val and parent_account_name_d_val.lower() != 'k.a.')
|
||||
|
||||
if is_konzern_tochter:
|
||||
self.logger.debug(f" PlausiCheck: Tochter von Konzern '{parent_account_name_d_val}' (D). Abweichungs-Checks werden als INFO_KONZERN_LOGIK behandelt.")
|
||||
|
||||
# --- 1. Plausibilität Finaler Umsatz (BG) ---
|
||||
final_umsatz_str = row_data_dict.get("Finaler Umsatz (Wiki>CRM)", "k.A.")
|
||||
final_ma_str = row_data_dict.get("Finaler Mitarbeiter (Wiki>CRM)", "k.A.")
|
||||
self.logger.debug(f" PlausiCheck Input: final_umsatz_str='{final_umsatz_str}', final_ma_str='{final_ma_str}'")
|
||||
|
||||
# Werte für absolute Plausi-Checks (Umsatz in Euro, MA absolut)
|
||||
umsatz_num_absolut = self._get_numeric_value_for_plausi(final_umsatz_str, is_umsatz=True)
|
||||
ma_num_absolut = self._get_numeric_value_for_plausi(final_ma_str, is_umsatz=False)
|
||||
self.logger.debug(f" PlausiCheck Numerisch (Absolut): umsatz_abs={umsatz_num_absolut}, ma_abs={ma_num_absolut}")
|
||||
|
||||
# ... (Logik für plaus_umsatz_flag und Begründung für Umsatz bleibt wie gehabt) ...
|
||||
# Beispielhaft, wie Begründungen gesammelt werden:
|
||||
temp_begruendungen = [] # Lokale Liste für Begründungen in diesem Check
|
||||
|
||||
# --- Plausi-Checks für absolute Werte und Ratio (verwenden umsatz_num_absolut, ma_num_absolut) ---
|
||||
# (Dieser Teil bleibt wie im letzten Vorschlag, der die Debug-Blöcke enthielt,
|
||||
# aber er arbeitet jetzt mit umsatz_num_absolut und ma_num_absolut)
|
||||
|
||||
# 1. Plausibilität Umsatz (absolut)
|
||||
if pd.isna(umsatz_num_absolut):
|
||||
exclusion_list = ['k.a.', '', 'n/a', '-', '0', '0.0', '0,00', '0.000', '0.00']
|
||||
if final_umsatz_str.lower().strip() not in exclusion_list:
|
||||
exclusion_list_common = ['k.a.', '', 'n/a', '-', '0', '0.0', '0,00', '0.000', '0.00']
|
||||
if final_umsatz_str.lower().strip() not in exclusion_list_common and not final_umsatz_str.startswith("FEHLER"): # Auch "FEHLER..." ausschließen
|
||||
results["plaus_umsatz_flag"] = "FEHLER_FORMAT"
|
||||
results["begruendungen"].append(f"Finaler Umsatz ('{final_umsatz_str}') konnte nicht als Zahl interpretiert werden (und war kein 'k.A.' oder '0').")
|
||||
temp_begruendungen.append(f"Finaler Umsatz ('{final_umsatz_str}') konnte nicht als Zahl interpretiert werden.")
|
||||
# else: bleibt NICHT_PRUEFBAR, wenn es k.A./0/Fehler war
|
||||
else: # umsatz_num_absolut ist eine gültige Zahl
|
||||
results["plaus_umsatz_flag"] = "OK"
|
||||
if umsatz_num_absolut == 0: # Hier ist 0 ein echter numerischer Wert
|
||||
if umsatz_num_absolut == 0 and final_umsatz_str != "0": # Echte numerische 0, aber nicht aus Input "0" (der als unbekannt gilt)
|
||||
results["plaus_umsatz_flag"] = "WARNUNG_NULL_WERT"
|
||||
results["begruendungen"].append(f"Finaler Umsatz ist numerisch 0 (aus '{final_umsatz_str}').")
|
||||
temp_begruendungen.append(f"Finaler Umsatz ist numerisch 0 (aus '{final_umsatz_str}').")
|
||||
elif umsatz_num_absolut < getattr(Config, 'PLAUSI_UMSATZ_MIN_WARNUNG', 50000):
|
||||
results["plaus_umsatz_flag"] = "WARNUNG_NIEDRIG"
|
||||
# ... Begründung ...
|
||||
temp_begruendungen.append(f"Finaler Umsatz ({umsatz_num_absolut:,.0f} €) < Min-Schwelle ({getattr(Config, 'PLAUSI_UMSATZ_MIN_WARNUNG', 50000):,.0f} €).")
|
||||
elif umsatz_num_absolut > getattr(Config, 'PLAUSI_UMSATZ_MAX_WARNUNG', 200000000000):
|
||||
results["plaus_umsatz_flag"] = "WARNUNG_HOCH"
|
||||
# ... Begründung ...
|
||||
temp_begruendungen.append(f"Finaler Umsatz ({umsatz_num_absolut:,.0f} €) > Max-Schwelle ({getattr(Config, 'PLAUSI_UMSATZ_MAX_WARNUNG', 200000000000):,.0f} €).")
|
||||
|
||||
|
||||
# --- 2. Plausibilität Finale Mitarbeiter (BH) ---
|
||||
final_ma_str = row_data_dict.get("Finaler Mitarbeiter (Wiki>CRM)", "k.A.")
|
||||
ma_num_absolut = self._get_numeric_value_for_plausi(final_ma_str, is_umsatz=False)
|
||||
|
||||
# 2. Plausibilität Mitarbeiter (absolut) - analog zu Umsatz
|
||||
# ... (Logik für plaus_ma_flag und Begründung für Mitarbeiter bleibt wie gehabt) ...
|
||||
if pd.isna(ma_num_absolut):
|
||||
exclusion_list = ['k.a.', '', 'n/a', '-', '0', '0.0', '0,00', '0.000', '0.00']
|
||||
if final_ma_str.lower().strip() not in exclusion_list:
|
||||
if final_ma_str.lower().strip() not in exclusion_list_common and not final_ma_str.startswith("FEHLER"):
|
||||
results["plaus_ma_flag"] = "FEHLER_FORMAT"
|
||||
# ... Begründung ...
|
||||
temp_begruendungen.append(f"Finale MA ('{final_ma_str}') konnte nicht als Zahl interpretiert werden.")
|
||||
else:
|
||||
results["plaus_ma_flag"] = "OK"
|
||||
if ma_num_absolut == 0:
|
||||
if ma_num_absolut == 0 and final_ma_str != "0":
|
||||
results["plaus_ma_flag"] = "WARNUNG_NULL_WERT"
|
||||
# ... Begründung ...
|
||||
temp_begruendungen.append(f"Finale MA ist numerisch 0 (aus '{final_ma_str}').")
|
||||
elif ma_num_absolut < getattr(Config, 'PLAUSI_MA_MIN_WARNUNG_ABS', 1):
|
||||
results["plaus_ma_flag"] = "WARNUNG_NIEDRIG"
|
||||
# ... Begründung ...
|
||||
# ... (Rest der MA-Checks, inkl. spezifischer Check für wenig MA bei Umsatz)
|
||||
temp_begruendungen.append(f"Finale MA ({ma_num_absolut:.0f}) < Min-Schwelle ({getattr(Config, 'PLAUSI_MA_MIN_WARNUNG_ABS', 1):.0f}).")
|
||||
elif not pd.isna(umsatz_num_absolut) and umsatz_num_absolut >= getattr(Config, 'PLAUSI_UMSATZ_MIN_SCHWELLE_FUER_MA_CHECK', 1000000) and \
|
||||
ma_num_absolut < getattr(Config, 'PLAUSI_MA_MIN_WARNUNG_BEI_UMSATZ', 3):
|
||||
results["plaus_ma_flag"] = "WARNUNG_ZU_WENIG_MA_BEI_UMSATZ"
|
||||
temp_begruendungen.append(f"Finale MA ({ma_num_absolut:.0f}) auffällig niedrig für Umsatz ({umsatz_num_absolut:,.0f} €).")
|
||||
elif ma_num_absolut > getattr(Config, 'PLAUSI_MA_MAX_WARNUNG', 1000000):
|
||||
results["plaus_ma_flag"] = "WARNUNG_HOCH"
|
||||
temp_begruendungen.append(f"Finale MA ({ma_num_absolut:.0f}) > Max-Schwelle ({getattr(Config, 'PLAUSI_MA_MAX_WARNUNG', 1000000):,.0f}).")
|
||||
|
||||
# 3. Umsatz/MA Ratio (verwendet umsatz_num_absolut, ma_num_absolut)
|
||||
if not pd.isna(umsatz_num_absolut) and not pd.isna(ma_num_absolut):
|
||||
# --- 3. Plausibilität Umsatz/MA Ratio (BI) ---
|
||||
if not pd.isna(umsatz_num_absolut) and not pd.isna(ma_num_absolut) and umsatz_num_absolut > 0 : # Nur wenn Umsatz > 0
|
||||
if ma_num_absolut > 0:
|
||||
ratio = umsatz_num_absolut / ma_num_absolut
|
||||
results["plaus_ratio_flag"] = "OK"
|
||||
# ... (Ratio-Warnungen basierend auf Config-Werten für PLAUSI_RATIO_UMSATZ_PRO_MA_MIN/MAX)
|
||||
# ... (Behandlung ma_num_absolut == 0)
|
||||
if ratio < getattr(Config, 'PLAUSI_RATIO_UMSATZ_PRO_MA_MIN', 25000):
|
||||
results["plaus_ratio_flag"] = "WARNUNG_RATIO_NIEDRIG"
|
||||
temp_begruendungen.append(f"Umsatz/MA Ratio ({ratio:,.0f} €/MA) < Min-Schwelle ({getattr(Config, 'PLAUSI_RATIO_UMSATZ_PRO_MA_MIN', 25000):,.0f} €/MA).")
|
||||
elif ratio > getattr(Config, 'PLAUSI_RATIO_UMSATZ_PRO_MA_MAX', 1500000):
|
||||
results["plaus_ratio_flag"] = "WARNUNG_RATIO_HOCH"
|
||||
temp_begruendungen.append(f"Umsatz/MA Ratio ({ratio:,.0f} €/MA) > Max-Schwelle ({getattr(Config, 'PLAUSI_RATIO_UMSATZ_PRO_MA_MAX', 1500000):,.0f} €/MA).")
|
||||
elif ma_num_absolut == 0: # Umsatz vorhanden, aber MA ist 0
|
||||
results["plaus_ratio_flag"] = "FEHLER_MA_NULL_BEI_UMSATZ"
|
||||
temp_begruendungen.append("Umsatz vorhanden, aber MA ist 0. Ratio nicht berechenbar.")
|
||||
# else: MA < 0 (sollte nicht passieren) oder NaN (bereits oben abgedeckt)
|
||||
elif pd.isna(umsatz_num_absolut) or umsatz_num_absolut <= 0 : # Wenn Umsatz NaN oder <= 0 ist, ist Ratio nicht sinnvoll prüfbar
|
||||
results["plaus_ratio_flag"] = "NICHT_PRUEFBAR_UMSATZ_FEHLT"
|
||||
# Der Fall "ma_num_absolut ist NaN" ist implizit durch die erste Bedingung abgedeckt.
|
||||
|
||||
|
||||
# --- 4. Abgleich CRM vs. Wiki ---
|
||||
# HIER VERWENDEN WIR get_numeric_filter_value, um die Mio-Werte für den Vergleich zu bekommen
|
||||
# --- 4. Abgleich CRM vs. Wiki (BJ, BK) ---
|
||||
crm_umsatz_str = row_data_dict.get("CRM Umsatz", "k.A.")
|
||||
wiki_umsatz_str = row_data_dict.get("Wiki Umsatz", "k.A.")
|
||||
wiki_umsatz_str = row_data_dict.get("Wiki Umsatz", "k.A.") # Dies ist der Wert aus Spalte W (kann von Parent sein)
|
||||
crm_ma_str = row_data_dict.get("CRM Anzahl Mitarbeiter", "k.A.")
|
||||
wiki_ma_str = row_data_dict.get("Wiki Mitarbeiter", "k.A.")
|
||||
wiki_ma_str = row_data_dict.get("Wiki Mitarbeiter", "k.A.") # Dies ist der Wert aus Spalte X (kann von Parent sein)
|
||||
|
||||
# Diese geben jetzt Umsatz in Mio zurück, oder 0 bei Unbekannt/Fehler für den Filter
|
||||
crm_u_mio = get_numeric_filter_value(crm_umsatz_str, True)
|
||||
wiki_u_mio = get_numeric_filter_value(wiki_umsatz_str, True)
|
||||
# Diese geben absolute MA-Zahl zurück, oder 0 bei Unbekannt/Fehler für den Filter
|
||||
crm_m_abs = get_numeric_filter_value(crm_ma_str, False)
|
||||
wiki_m_abs = get_numeric_filter_value(wiki_ma_str, False)
|
||||
# _get_numeric_value_for_plausi liefert absolute Werte oder NaN
|
||||
crm_u_abs = self._get_numeric_value_for_plausi(crm_umsatz_str, is_umsatz=True)
|
||||
wiki_u_abs = self._get_numeric_value_for_plausi(wiki_umsatz_str, is_umsatz=True)
|
||||
crm_m_abs_comp = self._get_numeric_value_for_plausi(crm_ma_str, is_umsatz=False) # anderer Variablenname, um Konflikt mit ma_num_absolut oben zu vermeiden
|
||||
wiki_m_abs_comp = self._get_numeric_value_for_plausi(wiki_ma_str, is_umsatz=False)
|
||||
|
||||
abweichung_prozent_config = getattr(Config, 'PLAUSI_ABWEICHUNG_CRM_WIKI_PROZENT', 30) / 100.0
|
||||
|
||||
# Umsatz Abweichung (arbeitet mit Mio-Werten)
|
||||
if crm_u_mio > 0 and wiki_u_mio > 0: # Beide müssen positive Werte haben für sinnvollen Vergleich
|
||||
diff = abs(crm_u_mio - wiki_u_mio)
|
||||
max_val = max(crm_u_mio, wiki_u_mio)
|
||||
if (diff / max_val) > abweichung_prozent_config:
|
||||
results["abweichung_umsatz_flag"] = "WARNUNG_SIGNIFIKANT"
|
||||
results["begruendungen"].append(f"Umsatz CRM ({crm_u_mio:,.1f} Mio) vs. Wiki ({wiki_u_mio:,.1f} Mio) weicht >{abweichung_prozent_config*100:.0f}% ab.")
|
||||
# Umsatz Abweichung (BJ)
|
||||
if pd.notna(crm_u_abs) and pd.notna(wiki_u_abs) and crm_u_abs > 0 and wiki_u_abs > 0 :
|
||||
if is_konzern_tochter:
|
||||
results["abweichung_umsatz_flag"] = "INFO_KONZERN_LOGIK"
|
||||
temp_begruendungen.append(f"INFO: Umsatz CRM vs. Wiki Abgleich bei Konzernzugehörigkeit nicht direkt bewertet (CRM: {crm_u_abs:,.0f}€, Wiki: {wiki_u_abs:,.0f}€).")
|
||||
else:
|
||||
results["abweichung_umsatz_flag"] = "OK"
|
||||
elif crm_u_mio > 0 and wiki_u_mio <= 0 : results["abweichung_umsatz_flag"] = "WIKI_FEHLT_ODER_NULL"
|
||||
elif crm_u_mio <= 0 and wiki_u_mio > 0 : results["abweichung_umsatz_flag"] = "CRM_FEHLT_ODER_NULL"
|
||||
else: results["abweichung_umsatz_flag"] = "BEIDE_FEHLEN_ODER_NULL" # Oder N/A
|
||||
diff = abs(crm_u_abs - wiki_u_abs)
|
||||
# Bezugswert für Prozentrechnung: der größere der beiden Werte oder Durchschnitt?
|
||||
# Hier: Bezug auf den größeren Wert, um konservativer zu sein
|
||||
bezugswert_umsatz = max(crm_u_abs, wiki_u_abs)
|
||||
if bezugswert_umsatz > 0 and (diff / bezugswert_umsatz) > abweichung_prozent_config:
|
||||
results["abweichung_umsatz_flag"] = "WARNUNG_SIGNIFIKANT"
|
||||
temp_begruendungen.append(f"Umsatz CRM ({crm_u_abs:,.0f} €) vs. Wiki ({wiki_u_abs:,.0f} €) weicht >{abweichung_prozent_config*100:.0f}% ab.")
|
||||
else:
|
||||
results["abweichung_umsatz_flag"] = "OK"
|
||||
elif pd.notna(crm_u_abs) and crm_u_abs > 0 and pd.isna(wiki_u_abs): results["abweichung_umsatz_flag"] = "WIKI_FEHLT_ODER_NULL"
|
||||
elif pd.isna(crm_u_abs) and pd.notna(wiki_u_abs) and wiki_u_abs > 0: results["abweichung_umsatz_flag"] = "CRM_FEHLT_ODER_NULL"
|
||||
else: results["abweichung_umsatz_flag"] = "BEIDE_FEHLEN_ODER_NULL"
|
||||
|
||||
# Mitarbeiter Abweichung (arbeitet mit absoluten Zahlen)
|
||||
if crm_m_abs > 0 and wiki_m_abs > 0:
|
||||
diff = abs(crm_m_abs - wiki_m_abs)
|
||||
max_val = max(crm_m_abs, wiki_m_abs)
|
||||
if (diff / max_val) > abweichung_prozent_config:
|
||||
results["abweichung_ma_flag"] = "WARNUNG_SIGNIFIKANT"
|
||||
results["begruendungen"].append(f"MA CRM ({crm_m_abs:.0f}) vs. Wiki ({wiki_m_abs:.0f}) weicht >{abweichung_prozent_config*100:.0f}% ab.")
|
||||
# Mitarbeiter Abweichung (BK)
|
||||
if pd.notna(crm_m_abs_comp) and pd.notna(wiki_m_abs_comp) and crm_m_abs_comp > 0 and wiki_m_abs_comp > 0:
|
||||
if is_konzern_tochter:
|
||||
results["abweichung_ma_flag"] = "INFO_KONZERN_LOGIK"
|
||||
temp_begruendungen.append(f"INFO: MA CRM vs. Wiki Abgleich bei Konzernzugehörigkeit nicht direkt bewertet (CRM: {crm_m_abs_comp:.0f}, Wiki: {wiki_m_abs_comp:.0f}).")
|
||||
else:
|
||||
results["abweichung_ma_flag"] = "OK"
|
||||
elif crm_m_abs > 0 and wiki_m_abs <= 0 : results["abweichung_ma_flag"] = "WIKI_FEHLT_ODER_NULL"
|
||||
elif crm_m_abs <= 0 and wiki_m_abs > 0 : results["abweichung_ma_flag"] = "CRM_FEHLT_ODER_NULL"
|
||||
diff = abs(crm_m_abs_comp - wiki_m_abs_comp)
|
||||
bezugswert_ma = max(crm_m_abs_comp, wiki_m_abs_comp)
|
||||
if bezugswert_ma > 0 and (diff / bezugswert_ma) > abweichung_prozent_config:
|
||||
results["abweichung_ma_flag"] = "WARNUNG_SIGNIFIKANT"
|
||||
temp_begruendungen.append(f"MA CRM ({crm_m_abs_comp:.0f}) vs. Wiki ({wiki_m_abs_comp:.0f}) weicht >{abweichung_prozent_config*100:.0f}% ab.")
|
||||
else:
|
||||
results["abweichung_ma_flag"] = "OK"
|
||||
elif pd.notna(crm_m_abs_comp) and crm_m_abs_comp > 0 and pd.isna(wiki_m_abs_comp): results["abweichung_ma_flag"] = "WIKI_FEHLT_ODER_NULL"
|
||||
elif pd.isna(crm_m_abs_comp) and pd.notna(wiki_m_abs_comp) and wiki_m_abs_comp > 0: results["abweichung_ma_flag"] = "CRM_FEHLT_ODER_NULL"
|
||||
else: results["abweichung_ma_flag"] = "BEIDE_FEHLEN_ODER_NULL"
|
||||
|
||||
if not results["begruendungen"]: results["plausi_begruendung_final"] = "Plausibilität OK"
|
||||
else: results["plausi_begruendung_final"] = "; ".join(results["begruendungen"])
|
||||
# Finale Begründung (BL)
|
||||
if temp_begruendungen:
|
||||
results["plausi_begruendung_final"] = "; ".join(temp_begruendungen)
|
||||
# else: Default "Plausibilität OK" bleibt
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
||||
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 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'}")
|
||||
"""
|
||||
Führt Konsolidierung und Plausibilitäts-Checks für einen Bereich von Zeilen durch.
|
||||
- Konsolidiert Umsatz/MA (BD/BE) basierend auf CRM/Wiki und Parent-Account (D).
|
||||
- Ruft _check_financial_plausibility für jede Zeile auf.
|
||||
- Schreibt konsolidierte Werte und Plausi-Ergebnisse (BG-BM) ins Sheet.
|
||||
- Setzt den Plausibilität Prüfdatum (BM).
|
||||
|
||||
plausi_ts_key = "Plausibilität Prüfdatum"
|
||||
# Stellen Sie sicher, dass alle benötigten Spalten in COLUMN_MAP existieren
|
||||
required_keys_for_mode = [
|
||||
"CRM Umsatz", "Wiki Umsatz", "CRM Anzahl Mitarbeiter", "Wiki Mitarbeiter", # Input für Konsolidierung
|
||||
"Finaler Umsatz (Wiki>CRM)", "Finaler Mitarbeiter (Wiki>CRM)", # Output Konsolidierung / Input Plausi
|
||||
"Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", # Output Plausi
|
||||
"Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki", "Plausibilität Begründung", # Output Plausi
|
||||
plausi_ts_key # Output Timestamp
|
||||
]
|
||||
if not all(key in COLUMN_MAP for key in required_keys_for_mode):
|
||||
missing_k = [k for k in required_keys_for_mode 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
|
||||
Args:
|
||||
start_sheet_row (int, optional): Die 1-basierte Startzeile. Default: Ab erster Datenzeile.
|
||||
end_sheet_row (int, optional): Die 1-basierte Endzeile. Default: Bis Ende des Sheets.
|
||||
limit (int, optional): Maximale Anzahl zu verarbeitender Zeilen. Default: Unbegrenzt.
|
||||
"""
|
||||
self.logger.info(f"Starte Modus 'plausi_check_data' (Konsolidierung & Plausi-Checks). Bereich: {start_sheet_row if start_sheet_row is not None else 'Start'}-{end_sheet_row if end_sheet_row else 'Ende'}, Limit: {limit if limit is not None else 'Unbegrenzt'}")
|
||||
|
||||
# --- Daten laden und Start/Ende bestimmen ---
|
||||
if not self.sheet_handler.load_data():
|
||||
self.logger.error("Konnte Sheet-Daten nicht laden für Plausi-Checks. Abbruch.")
|
||||
return
|
||||
|
||||
all_data = self.sheet_handler.get_all_data_with_headers()
|
||||
header_offset = self.sheet_handler._header_rows
|
||||
|
||||
updates_fuer_sheet = []
|
||||
processed_rows_count = 0
|
||||
now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
total_sheet_rows = len(all_data)
|
||||
|
||||
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)
|
||||
effective_end_row = end_sheet_row if end_sheet_row is not None else total_sheet_rows
|
||||
|
||||
self.logger.info(f"Konsolidiere und prüfe Plausibilität für Zeilen {effective_start_row} bis {effective_end_row}.")
|
||||
if effective_start_row > effective_end_row or effective_start_row > total_sheet_rows:
|
||||
self.logger.info("Start liegt nach Ende oder außerhalb des Sheets. Keine Zeilen zu verarbeiten.")
|
||||
return
|
||||
|
||||
self.logger.info(f"Verarbeite Zeilen {effective_start_row} bis {effective_end_row} für Konsolidierung und Plausi-Checks.")
|
||||
|
||||
# --- Notwendige Spalten prüfen ---
|
||||
required_keys_for_plausi_mode = [
|
||||
"CRM Umsatz", "Wiki Umsatz", "CRM Anzahl Mitarbeiter", "Wiki Mitarbeiter", "Parent Account Name",
|
||||
"Finaler Umsatz (Wiki>CRM)", "Finaler Mitarbeiter (Wiki>CRM)",
|
||||
"Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio",
|
||||
"Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki", "Plausibilität Begründung",
|
||||
"Plausibilität Prüfdatum"
|
||||
]
|
||||
if not all(key in COLUMN_MAP for key in required_keys_for_plausi_mode):
|
||||
missing_k = [k for k in required_keys_for_plausi_mode if k not in COLUMN_MAP]
|
||||
self.logger.error(f"Nicht alle benötigten Spalten ({missing_k}) für Modus 'plausi_check_data' in COLUMN_MAP. Abbruch.")
|
||||
return
|
||||
|
||||
# --- Verarbeitung ---
|
||||
all_sheet_updates = []
|
||||
processed_rows_count = 0
|
||||
now_timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
update_batch_limit_config = getattr(Config, 'UPDATE_BATCH_ROW_LIMIT', 50)
|
||||
# Jede Zeile generiert ca. 9 Updates (BD, BE, BG-BL, BM)
|
||||
# Daher ist die Anzahl der Operationen ca. 9 * Anzahl Zeilen
|
||||
# Wir lösen den Batch-Update aus, wenn die Anzahl der Operationen das Limit erreicht.
|
||||
# update_batch_operations_trigger = update_batch_limit_config * 9
|
||||
# Sicherer: nach jeder `update_batch_limit_config`-ten Zeile updaten
|
||||
|
||||
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"Verarbeitungslimit von {limit} Zeilen erreicht.")
|
||||
break
|
||||
|
||||
row_list_idx = row_num_sheet - 1
|
||||
if row_list_idx >= len(all_data): break
|
||||
row_data = all_data[row_list_idx]
|
||||
|
||||
# CRM Name für Logging
|
||||
# crm_name_log = self._get_cell_value_safe(row_data, "CRM Name")
|
||||
# self.logger.debug(f"Verarbeite Zeile {row_num_sheet} ({crm_name_log[:30]}...) für Plausi-Check & Konsolidierung.")
|
||||
if row_list_idx >= total_sheet_rows: break # Ende der Daten erreicht
|
||||
row_data = all_data[row_list_idx] # Rohdaten der aktuellen Zeile
|
||||
|
||||
# Überspringe leere Zeilen oder Zeilen ohne Firmennamen
|
||||
crm_name_check = self._get_cell_value_safe(row_data, "CRM Name").strip()
|
||||
if not crm_name_check:
|
||||
# self.logger.debug(f"Zeile {row_num_sheet}: Übersprungen (kein Firmenname).")
|
||||
continue
|
||||
|
||||
self.logger.debug(f"Zeile {row_num_sheet} ({crm_name_check[:30]}...): Starte Konsolidierung und Plausi-Check.")
|
||||
current_row_updates = []
|
||||
|
||||
# --- 1. Konsolidierung (Logik aus _process_single_row, Block 3e) ---
|
||||
# 1. Konsolidierung (BD, BE)
|
||||
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")
|
||||
wiki_umsatz_val_str = self._get_cell_value_safe(row_data, "Wiki Umsatz") # Liest direkt aus dem Sheet
|
||||
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") # Liest direkt aus dem Sheet
|
||||
|
||||
final_ma_str_konsolidiert = "k.A."
|
||||
parent_account_name_d_val = self._get_cell_value_safe(row_data, "Parent Account Name").strip()
|
||||
|
||||
try:
|
||||
# get_numeric_filter_value liefert Umsatz in Mio, MA absolut, oder 0 bei Fehler/unbekannt
|
||||
crm_umsatz_val_str = self._get_cell_value_safe(row_data, "CRM Umsatz")
|
||||
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")
|
||||
|
||||
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)
|
||||
|
||||
# Konsolidierung: Wiki > CRM. Priorisiere den Wert, der nicht 0 ist (was "unbekannt" von get_numeric_filter_value bedeutet).
|
||||
final_num_umsatz = num_wiki_umsatz if num_wiki_umsatz != 0.0 else num_crm_umsatz
|
||||
final_num_ma = num_wiki_ma if num_wiki_ma != 0 else num_crm_ma
|
||||
|
||||
# String-Konvertierung für Sheet:
|
||||
# Ein numerischer Wert von 0 (der aus "0 Tsd" oder echter 0 kommen kann) wird als String "0" geschrieben.
|
||||
# Wenn final_num_xxx 0 ist, weil *beide* Quellen 0 (oder leer/k.A.) waren, wird "k.A." geschrieben.
|
||||
if final_num_umsatz == 0.0 and (num_crm_umsatz == 0.0 and num_wiki_umsatz == 0.0): # Beide Quellen waren "unbekannt"
|
||||
final_umsatz_str_konsolidiert = 'k.A.'
|
||||
if parent_account_name_d_val and parent_account_name_d_val.lower() != 'k.a.':
|
||||
self.logger.debug(f" -> Parent D ('{parent_account_name_d_val}') ist gesetzt. Konsolidiere primär mit CRM-Daten der Tochter.")
|
||||
final_num_umsatz = num_crm_umsatz if num_crm_umsatz > 0 else num_wiki_umsatz
|
||||
final_num_ma = num_crm_ma if num_crm_ma > 0 else num_wiki_ma
|
||||
else:
|
||||
final_umsatz_str_konsolidiert = str(int(round(final_num_umsatz)))
|
||||
|
||||
if final_num_ma == 0 and (num_crm_ma == 0 and num_wiki_ma == 0): # Beide Quellen waren "unbekannt"
|
||||
final_ma_str_konsolidiert = 'k.A.'
|
||||
else:
|
||||
final_ma_str_konsolidiert = str(int(round(final_num_ma)))
|
||||
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
|
||||
|
||||
self.logger.debug(f" Zeile {row_num_sheet}: Konsolidiert -> U: '{final_umsatz_str_konsolidiert}', MA: '{final_ma_str_konsolidiert}'")
|
||||
|
||||
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_IN_PLAUSI"
|
||||
final_ma_str_konsolidiert = "FEHLER_KONSO_IN_PLAUSI"
|
||||
final_umsatz_str_konsolidiert = str(int(round(final_num_umsatz))) if final_num_umsatz > 0 else 'k.A.'
|
||||
final_ma_str_konsolidiert = str(int(round(final_num_ma))) if final_num_ma > 0 else 'k.A.'
|
||||
except Exception as e_conso_batch:
|
||||
self.logger.error(f"Fehler bei Konsolidierung in Plausi-Batch für Zeile {row_num_sheet}: {e_conso_batch}")
|
||||
final_umsatz_str_konsolidiert = "FEHLER_KONSO_PLAUSI"
|
||||
final_ma_str_konsolidiert = "FEHLER_KONSO_PLAUSI"
|
||||
|
||||
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) ---
|
||||
# Nur ausführen, wenn Konsolidierung keine FEHLER produziert hat
|
||||
# 2. Plausibilitäts-Checks (BG-BM)
|
||||
if not final_umsatz_str_konsolidiert.startswith("FEHLER") and not final_ma_str_konsolidiert.startswith("FEHLER"):
|
||||
processed_rows_count +=1 # Zähle nur Zeilen, für die Plausi tatsächlich versucht 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,
|
||||
"Wiki Umsatz": wiki_umsatz_val_str,
|
||||
"CRM Anzahl Mitarbeiter": crm_ma_val_str,
|
||||
"Wiki Mitarbeiter": wiki_ma_val_str
|
||||
"CRM Umsatz": self._get_cell_value_safe(row_data, "CRM Umsatz"),
|
||||
"Wiki Umsatz": self._get_cell_value_safe(row_data, "Wiki Umsatz"), # Direkter Wert aus Sheet
|
||||
"CRM Anzahl Mitarbeiter": self._get_cell_value_safe(row_data, "CRM Anzahl Mitarbeiter"),
|
||||
"Wiki Mitarbeiter": self._get_cell_value_safe(row_data, "Wiki Mitarbeiter"), # Direkter Wert aus Sheet
|
||||
"Parent Account Name": parent_account_name_d_val
|
||||
}
|
||||
plausi_results = self._check_financial_plausibility(plausi_input_data)
|
||||
|
||||
@@ -8418,35 +8468,41 @@ class DataProcessor:
|
||||
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.")]]})
|
||||
|
||||
except Exception as e_plausi_run:
|
||||
self.logger.error(f"Fehler im Plausi-Check Aufruf für Zeile {row_num_sheet}: {e_plausi_run}")
|
||||
except Exception as e_plausi_run_batch:
|
||||
self.logger.error(f"Fehler im Plausi-Check Aufruf (Batch-Modus) für Zeile {row_num_sheet}: {e_plausi_run_batch}")
|
||||
# Fehler-Flags für Plausi-Spalten setzen
|
||||
for key_flag in ["Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki"]:
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag] + 1)}{row_num_sheet}', 'values': [['FEHLER_PLAUSI_CALL']]})
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_sheet}', 'values': [[f"Systemfehler Plausi-Call: {str(e_plausi_run)[:100]}"]]})
|
||||
else: # Konsolidierung hat Fehler produziert
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag] + 1)}{row_num_sheet}', 'values': [['FEHLER_CALL']]})
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_sheet}', 'values': [[f"Systemfehler: {str(e_plausi_run_batch)[:100]}"]]})
|
||||
else:
|
||||
self.logger.warn(f"Zeile {row_num_sheet}: Überspringe Plausi-Checks wegen Fehler bei Konsolidierung.")
|
||||
# Setze Plausi-Flags auf einen Fehlerstatus oder lasse sie leer (aktuell wird nichts explizit gesetzt)
|
||||
for key_flag in ["Plausibilität Umsatz", "Plausibilität Mitarbeiter", "Plausibilität Umsatz/MA Ratio", "Abweichung Umsatz CRM/Wiki", "Abweichung MA CRM/Wiki"]:
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP[key_flag] + 1)}{row_num_sheet}', 'values': [['INPUT_FEHLER_KONSO']]})
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Begründung"] + 1)}{row_num_sheet}', 'values': [["Konsolidierung fehlgeschlagen"]]})
|
||||
|
||||
# Plausibilität Prüfdatum (BM) setzen
|
||||
current_row_updates.append({'range': f'{self.sheet_handler._get_col_letter(COLUMN_MAP["Plausibilität Prüfdatum"] + 1)}{row_num_sheet}', 'values': [[now_timestamp_str]]})
|
||||
|
||||
all_sheet_updates.extend(current_row_updates)
|
||||
processed_rows_count += 1
|
||||
|
||||
# Immer den Plausi-Timestamp setzen, da die Zeile betrachtet wurde
|
||||
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 auslösen, wenn Limit erreicht
|
||||
if processed_rows_count % update_batch_limit_config == 0 and all_sheet_updates:
|
||||
self.logger.info(f"Sende Plausi-Check & Konsolidierungs Batch-Update ({len(all_sheet_updates) / 9:.0f} Zeilen)...") # Ca. 9 Updates pro Zeile
|
||||
success = self.sheet_handler.batch_update_cells(all_sheet_updates)
|
||||
if success:
|
||||
self.logger.info(f" Sheet-Update für Plausi-Batch erfolgreich.")
|
||||
all_sheet_updates = [] # Liste leeren
|
||||
time.sleep(0.5) # Kurze Pause
|
||||
|
||||
# Batch-Update Logik (Anzahl Spalten jetzt: 2 Konsolidierung + 6 Plausi-Flags/Begründung + 1 Plausi-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 = []
|
||||
# time.sleep(0.5) # Kleine optionale Pause
|
||||
|
||||
if updates_fuer_sheet:
|
||||
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)
|
||||
# Letzten Batch senden, falls noch Updates vorhanden sind
|
||||
if all_sheet_updates:
|
||||
self.logger.info(f"Sende finalen Plausi-Check & Konsolidierungs Batch-Update ({len(all_sheet_updates) / 9:.0f} Zeilen)...")
|
||||
success = self.sheet_handler.batch_update_cells(all_sheet_updates)
|
||||
if success:
|
||||
self.logger.info(f" Finales Sheet-Update für Plausi-Batch erfolgreich.")
|
||||
|
||||
self.logger.info(f"Plausibilitäts-Check-Lauf (mit Konsolidierung) beendet. {processed_rows_count} Zeilen mit Plausi-Checks versehen, {skipped_count} Zeilen initial übersprungen.")
|
||||
self.logger.info(f"Modus 'plausi_check_data' abgeschlossen. {processed_rows_count} Zeilen mit Konsolidierung und Plausi-Checks versehen.")
|
||||
|
||||
# ==========================================================================
|
||||
# === Batch Processing Methods (Parent Account Suggestion) ==============
|
||||
@@ -11010,13 +11066,14 @@ def main():
|
||||
limit=final_limit_to_use # Verwendet das global ermittelte Limit
|
||||
)
|
||||
|
||||
elif selected_mode == "plausi_check_data": # <<< NEUER ELIF-BLOCK
|
||||
data_processor.run_plausibility_checks_batch( # Methode aus vorherigem Vorschlag
|
||||
start_sheet_row=args.start_sheet_row, # Nimmt CLI-Argumente für Bereich
|
||||
elif selected_mode == "plausi_check_data":
|
||||
data_processor.run_plausibility_checks_batch(
|
||||
start_sheet_row=args.start_sheet_row, # Nimmt CLI-Argumente für Bereich
|
||||
end_sheet_row=args.end_sheet_row,
|
||||
limit=final_limit_to_use # VERWENDE final_limit_to_use
|
||||
limit=final_limit_to_use # VERWENDE das ermittelte Limit
|
||||
)
|
||||
|
||||
|
||||
elif selected_mode == "suggest_parents": # <<< NEUER ELIF-BLOCK
|
||||
data_processor.process_parent_suggestion_batch(
|
||||
start_sheet_row=args.start_sheet_row,
|
||||
|
||||
Reference in New Issue
Block a user