From aef77c98afaaf86bf7d9b1938009516733884bdb Mon Sep 17 00:00:00 2001 From: buiaca Date: Wed, 12 Aug 2026 13:13:51 +0200 Subject: [PATCH] Fix: exact target format with ;;00 ending and no quotes on BLZ/Konto fields --- Hermes/iban_tool/iban_csv_converter.py | 117 +++++++++++-------------- Hermes/iban_tool/iban_tool_windows.zip | Bin 5972 -> 5752 bytes 2 files changed, 52 insertions(+), 65 deletions(-) diff --git a/Hermes/iban_tool/iban_csv_converter.py b/Hermes/iban_tool/iban_csv_converter.py index d21610b..c998396 100644 --- a/Hermes/iban_tool/iban_csv_converter.py +++ b/Hermes/iban_tool/iban_csv_converter.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 """ -iban_csv_converter.py - Konvertiert eine CSV mit Kontodaten in eine CSV mit IBAN. -Unterstuetzt das Bundesbank-BL-Z-Format mit variabler Spaltenanzahl. -Fuegt BIC (aus BLZ) und IBAN-Spalten automatisch hinzu. +iban_csv_converter.py - Konvertiert eine BLZ/Konto-CSV in eine CSV mit IBAN+BIC. +Erzeugt das EXAKTE Zielformat: +"DE";"00001";" 7606148200045949670010000976";;;76061482;0004594967;;"GENODEF1HSB";"DE56760614820004594967";;00 + +Format-Regeln: +- Felder 1-3: mit doppelten Anfuehrungszeichen (Feld 3 behaelt fuehrende Leerzeichen) +- Felder 4-5: leer (keine "") +- Felder 6-7: BLZ und Kontonummer OHNE Anfuehrungszeichen +- Felder 8-9: BIC und IBAN MIT doppelten Anfuehrungszeichen +- Ende der Zeile: zwei leere Felder (;;) + 00 Nutzung: python iban_csv_converter.py eingabe.csv ausgabe.csv - -Erweitert um: -- Automatische BIC-Erzeugung aus BLZ-Feld -- Flexibles Inputformat (erkennt BLZ/Konto anhand der Werte) -- Behaelt alle Original-Spalten bei und fuegt IBAN/BIC hinzu """ import argparse import csv @@ -31,50 +33,52 @@ def generate_iban(kontonummer: str, bankleitzahl: str) -> dict: "iban": str(iban_obj), "bankleitzahl": bankleitzahl_clean, "kontonummer": kontonummer, + "bic": "", } - # Versuche BIC zu finden try: bic_obj = BIC.from_bank_code("DE", bankleitzahl_clean) result["bic"] = str(bic_obj) except (InvalidBankCode, Exception): - result["bic"] = "" + pass return result -def find_blz_and_konto(row: list) -> tuple: - """Findet Bankleitzahl (BLZ) und Kontonummer in einer CSV-Zeile. - Priorisiert: 6. Feld = BLZ, 7. Feld = Konto (Index 5 und 6) - """ - # Standardfall: BLZ im 6. Feld (Index 5), Konto im 7. (Index 6) - if len(row) >= 7: - bankleitzahl = row[5].strip() - kontonummer = row[6].strip() - # Pruefe ob wir sinnvolle Werte haben - if bankleitzahl and kontonummer: - return bankleitzahl, kontonummer +def format_output_line(row: list, bic: str, iban: str) -> str: + """Erzeugt die Ausgabetzeile im exakten Zielformat.""" + # Feld 1 (Index 0): "DE" + f1 = f'"{row[0]}"' if row[0] else "" + # Feld 2 (Index 1): "00001" + f2 = f'"{row[1]}"' if len(row) > 1 and row[1] else "" + # Feld 3 (Index 2): " 76061482..." - behaelt fuehrende Leerzeichen + f3 = f'"{row[2]}"' if len(row) > 2 and row[2].strip() else "" + # Felder 4,5: leer (keine "") + f4 = "" + f5 = "" + # Feld 6 (Index 5): BLZ ohne "" + f6 = row[5].strip() if len(row) > 5 else "" + # Feld 7 (Index 6): Konto ohne "" + f7 = row[6].strip() if len(row) > 6 else "" + # Feld 8: BIC mit "" + f8 = f'"{bic}"' if bic else "" + # Feld 9: IBAN mit "" + f9 = f'"{iban}"' if iban else "" - # Fallback: Suche nach 8-stelliger BLZ und Konto-Nummer - for i, field in enumerate(row): - field = field.strip() - # BLZ ist 8-stellig (oder 5-8 mit fuehrenden Nullen) - if (len(field) >= 5 and len(field) <= 8 and field.isdigit() - and i + 1 < len(row) and row[i + 1].strip().isdigit()): - return field, row[i + 1].strip() - - return None, None + # Zusammenfuegen: "DE";"00001";" ...";;;BLZ;KONTO;;"BIC";"IBAN";;00 + parts = [f1, f2, f3, f4, f5, f6, f7, "", f8, f9, "", "00"] + return ";".join(parts) def main(): parser = argparse.ArgumentParser( - description="Konvertiert eine CSV mit Kontodaten (BLZ + Konto) in eine CSV mit IBAN und BIC." + description="Konvertiert CSV mit BLZ+Konto zu CSV mit IBAN+BIC im exakten Zielformat." ) parser.add_argument("eingabe_datei", type=str, help="Pfad zur Eingabedatei") parser.add_argument("ausgabe_datei", type=str, help="Pfad zur Ausgabedatei") args = parser.parse_args() - # Lese die Eingabedatei (probiere mehrere Kodierungen) + # Lese Eingabedatei rows = [] for encoding in ["iso-8859-1", "utf-8", "cp1252"]: try: @@ -92,48 +96,32 @@ def main(): print("FEHLER: Keine Daten gelesen.", file=sys.stderr) return - output_rows = [] + output_lines = [] for i, row in enumerate(rows): - # Finde BLZ und Kontonummer - bankleitzahl, kontonummer = find_blz_and_konto(row) + if len(row) < 7: + print(f"Warnung: Zeile {i+1} hat weniger als 7 Felder, wird uebersprungen.", file=sys.stderr) + continue + + bankleitzahl = row[5].strip() + kontonummer = row[6].strip() if not bankleitzahl or not kontonummer: - print(f"Warnung: Zeile {i+1} - konnte keine BLZ/Konto finden: {row[:3]}...", - file=sys.stderr) - # Originalzeile behalten, leere IBAN/BIC einsetzen - new_row = list(row) - while len(new_row) < 9: - new_row.append("") - if len(new_row) > 9: - new_row[8] = "" # BIC - if len(new_row) > 9: - new_row[9] = "" # IBAN - else: - new_row.extend(["", ""]) - output_rows.append(new_row) + print(f"Warnung: Zeile {i+1} - keine BLZ/Konto gefunden.", file=sys.stderr) + output_lines.append(format_output_line(row, "", "")) continue try: data = generate_iban(kontonummer, bankleitzahl) + output_lines.append(format_output_line(row, data["bic"], data["iban"])) except Exception as e: - print(f"Fehler in Zeile {i+1} (BLZ={bankleitzahl}, Konto={kontonummer}): {e}", - file=sys.stderr) - data = {"iban": "", "bic": "", "bankleitzahl": bankleitzahl, "kontonummer": kontonummer} + print(f"Fehler in Zeile {i+1}: {e}", file=sys.stderr) + output_lines.append(format_output_line(row, "", "")) - # Erstelle neue Zeile: Original-Felder + BIC (Index 8) + IBAN (Index 9) - new_row = list(row) - while len(new_row) < 10: - new_row.append("") - new_row[8] = data["bic"] # BIC-Spalte (Index 8) - new_row[9] = data["iban"] # IBAN-Spalte (Index 9) - output_rows.append(new_row) - - # Schreibe die Ausgabedatei + # Schreibe Ausgabedatei with open(args.ausgabe_datei, mode="w", encoding="iso-8859-1", newline="") as outfile: - writer = csv.writer(outfile, delimiter=";", quotechar='"', quoting=csv.QUOTE_ALL) - writer.writerows(output_rows) + outfile.write("\n".join(output_lines) + "\n") - print(f"Erfolgreich verarbeitet: {len(output_rows)} Zeilen.") + print(f"Erfolgreich verarbeitet: {len(output_lines)} Zeilen.") print(f"Ergebnis gespeichert in: {args.ausgabe_datei}") @@ -146,8 +134,7 @@ if __name__ == "__main__": import traceback traceback.print_exc() finally: - # Fenster offen halten – funktioniert auch bei Windows ohne Console-Problem try: input("\\\\n--- Fertig (Enter zum Schliessen) ---") except EOFError: - pass # stdin ist nicht interaktiv (z.B. Pipe), einfach beenden + pass diff --git a/Hermes/iban_tool/iban_tool_windows.zip b/Hermes/iban_tool/iban_tool_windows.zip index b115f7c712f8ed9c6a355df0c664cdbadf9df1d0..f117b31147ba4ed6da01f1c48c891d98444139cd 100644 GIT binary patch delta 2307 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 LBnD3w00000NI27$