Files
buchhaltung/import_kerstin_zahlungen.py
2026-04-26 07:51:39 +02:00

247 lines
9.0 KiB
Python

#!/usr/bin/env python3
"""
Importiert alle Zahlungen aus "Schulden Kerstin.xlsx" für Kredit ID bdcb8b1e-a0e6-4d49-b6ee-c16742e38403
"""
import openpyxl
from datetime import datetime
import re
import requests
import sys
# Konfiguration
EXCEL_FILE = "Schulden Kerstin.xlsx"
SHEET_NAME = "Tabelle2"
KREDIT_ID = "bdcb8b1e-a0e6-4d49-b6ee-c16742e38403"
API_BASE = "http://localhost:3000/api"
def parse_betrag(value):
"""Parst einen Geldbetrag aus verschiedenen Formaten"""
if value is None:
return None
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, str):
# Entferne € und Leerzeichen, konvertiere Komma zu Punkt
text = value.replace('', '').replace(' ', '').strip()
text = text.replace(',', '.')
# Extrahiere Zahl mit optionalem Vorzeichen
match = re.search(r'[+-]?[\d.]+', text)
if match:
try:
return float(match.group())
except ValueError:
return None
return None
def parse_datum(value):
"""Parst ein Datum aus verschiedenen Formaten"""
if value is None:
return None
if isinstance(value, datetime):
return value
if isinstance(value, str):
# Verschiedene Datumsformate
formats = [
'%d.%m.%Y',
'%Y-%m-%d',
'%d/%m/%Y',
'%m/%d/%Y',
'%d.%m.%y',
]
for fmt in formats:
try:
return datetime.strptime(value.strip(), fmt)
except ValueError:
continue
return None
def is_datum_in_zelle(value):
"""Prüft ob ein Wert ein Datum enthält (Zahl oder Text mit Datum)"""
if isinstance(value, datetime):
return True
if isinstance(value, str):
# Prüfe auf Datumsformate
if re.search(r'\d{1,2}[./]\d{1,2}[./]\d{2,4}', value):
return True
# Excel-Datum als Zahl (Oberfläche: Zahl > 30000 sind meist Datums)
if isinstance(value, (int, float)):
if 30000 < value < 50000: # Excel-Datum-Bereich ca. 1980-2030
return True
return False
def excel_date_to_datetime(excel_date):
"""Konvertiert Excel-Datum (Zahl) zu datetime"""
# Excel-Datum: Tage seit 30.12.1899
return datetime(1899, 12, 30) + __import__('datetime').timedelta(days=excel_date)
def main():
print(f"Lade Excel-Datei: {EXCEL_FILE}")
# Excel laden
wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True)
# Sheet "Tabelle2" verwenden oder zweites Sheet
if SHEET_NAME in wb.sheetnames:
ws = wb[SHEET_NAME]
else:
ws = wb.worksheets[1] if len(wb.worksheets) > 1 else wb.worksheets[0]
print(f"Verwende Sheet: {ws.title}")
print(f"Sheet hat {ws.max_row} Zeilen und {ws.max_column} Spalten")
# Suche nach Datum- und Betragsspalten
zahlungen = []
print("\n--- Suche nach Zahlungen in allen Zeilen ---")
# Durchsuche alle Zeilen
for row_idx, row in enumerate(ws.iter_rows(min_row=1, max_row=ws.max_row, values_only=True), 1):
# Prüfe jede Zelle in der Zeile auf Datum
for col_idx, cell_value in enumerate(row, 1):
if cell_value is None:
continue
# Prüfe ob Zelle ein Datum enthält
datum = None
betrag = None
notiz = None
# Fall 1: Direktes datetime-Objekt
if isinstance(cell_value, datetime):
datum = cell_value
# Suche Betrag in derselben Zeile (andere Spalten)
for other_idx, other_val in enumerate(row, 1):
if other_idx != col_idx:
parsed = parse_betrag(other_val)
if parsed is not None:
betrag = parsed
break
# Fall 2: String mit Datum
elif isinstance(cell_value, str):
datum = parse_datum(cell_value)
if datum:
# Suche Betrag in derselben Zeile
for other_idx, other_val in enumerate(row, 1):
if other_idx != col_idx:
parsed = parse_betrag(other_val)
if parsed is not None:
betrag = parsed
break
# Fall 3: Excel-Datum als Zahl
elif isinstance(cell_value, (int, float)):
if 30000 < cell_value < 50000:
datum = excel_date_to_datetime(cell_value)
# Suche Betrag in derselben Zeile
for other_idx, other_val in enumerate(row, 1):
if other_idx != col_idx:
parsed = parse_betrag(other_val)
if parsed is not None:
betrag = parsed
break
# Wenn wir ein Datum haben, extrahiere Notiz aus erster nicht-leerer Spalte
if datum:
for other_idx, other_val in enumerate(row, 1):
if other_val and other_idx != col_idx:
if isinstance(other_val, str) and not parse_betrag(other_val):
notiz = other_val.strip()
break
# Füge Zahlung hinzu
zahlung = {
'zeile': row_idx,
'spalte': col_idx,
'datum': datum,
'betrag': betrag,
'notiz': notiz or f"Zeile {row_idx}"
}
zahlungen.append(zahlung)
print(f" Zeile {row_idx}, Spalte {col_idx}: {datum.strftime('%d.%m.%Y')}, {betrag}€, Notiz: {notiz}")
break # Nur eine Zahlung pro Zeile
print(f"\n--- Gefundene Zahlungen: {len(zahlungen)} ---")
for z in zahlungen:
print(f" {z['datum'].strftime('%d.%m.%Y')}: {z['betrag']}€ - {z['notiz']}")
# Speichere gefundene Zahlungen für Debugging
with open('gefundene_zahlungen.txt', 'w', encoding='utf-8') as f:
f.write(f"Gefundene Zahlungen: {len(zahlungen)}\n\n")
for z in zahlungen:
f.write(f"{z['datum'].strftime('%d.%m.%Y')}: {z['betrag']}€ - {z['notiz']}\n")
# Importiere Zahlungen via API
print(f"\n--- Importiere {len(zahlungen)} Zahlungen via API ---")
erfolgreich = 0
fehler = []
for zahlung in zahlungen:
# Bestimme Typ
typ = 'monatsrate' if zahlung['betrag'] and zahlung['betrag'] > 0 else 'auslage'
# Betrag immer positiv speichern (Typ gibt Richtung an)
betrag_abs = abs(zahlung['betrag']) if zahlung['betrag'] else 0
payload = {
'datum': zahlung['datum'].strftime('%Y-%m-%d'),
'betrag': betrag_abs,
'typ': typ,
'notiz': zahlung['notiz']
}
try:
url = f"{API_BASE}/kredite/{KREDIT_ID}/zahlungen"
print(f" POST {url}")
print(f" Payload: {payload}")
response = requests.post(url, json=payload, timeout=10)
if response.status_code == 201:
print(f" ✓ Erfolgreich importiert")
erfolgreich += 1
else:
error_msg = f"Fehler {response.status_code}: {response.text}"
print(f"{error_msg}")
fehler.append({'zahlung': zahlung, 'fehler': error_msg})
except Exception as e:
error_msg = f"Exception: {str(e)}"
print(f"{error_msg}")
fehler.append({'zahlung': zahlung, 'fehler': error_msg})
# Ergebnis
print(f"\n=== ERGEBNIS ===")
print(f"Gefundene Zahlungen in Excel: {len(zahlungen)}")
print(f"Erfolgreich importiert: {erfolgreich}")
print(f"Fehler: {len(fehler)}")
if fehler:
print(f"\nFehlerhafte Zahlungen:")
for f in fehler:
print(f" - {f['zahlung']['datum'].strftime('%d.%m.%Y')}: {f['fehler']}")
# Verifiziere via API
print(f"\n--- Verifizierung ---")
try:
verify_url = f"{API_BASE}/kredite/{KREDIT_ID}/zahlungen"
response = requests.get(verify_url, timeout=10)
if response.status_code == 200:
db_zahlungen = response.json()
print(f"Zahlungen in Datenbank: {len(db_zahlungen)}")
if len(db_zahlungen) == len(zahlungen):
print("✓ Anzahl stimmt überein!")
else:
print(f"✗ ANZAHL STIMMT NICHT ÜBEREIN: Excel={len(zahlungen)}, DB={len(db_zahlungen)}")
else:
print(f"Konnte DB nicht prüfen: {response.status_code}")
except Exception as e:
print(f"Konnte Verifizierung nicht durchführen: {e}")
return erfolgreich == len(zahlungen)
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)