Powershell/Hermes/iban_tool/iban_csv_converter.py

154 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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.
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
import sys
from schwifty import IBAN, BIC
from schwifty.exceptions import InvalidBankCode
def generate_iban(kontonummer: str, bankleitzahl: str) -> dict:
"""Erzeugt eine deutsche IBAN aus Kontonummer und Bankleitzahl."""
try:
bankleitzahl_clean = str(int(bankleitzahl)) # fuehrende Nullen entfernen
except ValueError:
bankleitzahl_clean = bankleitzahl
iban_obj = IBAN.generate("DE", bank_code=bankleitzahl_clean, account_code=kontonummer)
result = {
"iban": str(iban_obj),
"bankleitzahl": bankleitzahl_clean,
"kontonummer": kontonummer,
}
# Versuche BIC zu finden
try:
bic_obj = BIC.from_bank_code("DE", bankleitzahl_clean)
result["bic"] = str(bic_obj)
except (InvalidBankCode, Exception):
result["bic"] = ""
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
# 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
def main():
parser = argparse.ArgumentParser(
description="Konvertiert eine CSV mit Kontodaten (BLZ + Konto) in eine CSV mit IBAN und BIC."
)
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)
rows = []
for encoding in ["iso-8859-1", "utf-8", "cp1252"]:
try:
with open(args.eingabe_datei, mode="r", encoding=encoding, newline="") as f:
reader = csv.reader(f, delimiter=";", quotechar='"')
rows = list(reader)
break
except UnicodeDecodeError:
continue
except FileNotFoundError:
print(f"FEHLER: Datei nicht gefunden: {args.eingabe_datei}", file=sys.stderr)
return
if not rows:
print("FEHLER: Keine Daten gelesen.", file=sys.stderr)
return
output_rows = []
for i, row in enumerate(rows):
# Finde BLZ und Kontonummer
bankleitzahl, kontonummer = find_blz_and_konto(row)
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)
continue
try:
data = generate_iban(kontonummer, bankleitzahl)
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}
# 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
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)
print(f"Erfolgreich verarbeitet: {len(output_rows)} Zeilen.")
print(f"Ergebnis gespeichert in: {args.ausgabe_datei}")
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"\\nFEHLER: {e}", file=sys.stderr)
print("\\n--- Details:", file=sys.stderr)
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