154 lines
5.4 KiB
Python
154 lines
5.4 KiB
Python
#!/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
|