Initial commit - Stand 26.04.2026
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
#!/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)
|
||||
Reference in New Issue
Block a user