276 lines
8.8 KiB
Python
276 lines
8.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
VOLLSTÄNDIGER NEU-IMPORT: Kerstin Kredit
|
|
Importiert Kerstin-Schulden aus Excel mit korrekter Berechnung
|
|
"""
|
|
|
|
import psycopg2
|
|
import pandas as pd
|
|
import openpyxl
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
|
|
# Datenbank-Verbindung - Host aus Umgebungsvariable oder localhost
|
|
import os
|
|
db_host = os.environ.get('DB_HOST', 'localhost')
|
|
conn = psycopg2.connect(
|
|
host=db_host,
|
|
port=5432,
|
|
database="buchhaltung",
|
|
user="postgres",
|
|
password="postgres"
|
|
)
|
|
cursor = conn.cursor()
|
|
|
|
print("="*60)
|
|
print("BACKUP: Übersprungen (pg_dump nicht verfügbar)")
|
|
print("="*60)
|
|
print("Manuelles Backup existiert bereits: Schulden Kerstin.xlsx.backup_*")
|
|
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 1: Alten Kredit 'Kerstin' löschen falls vorhanden")
|
|
print("="*60)
|
|
|
|
# Finde und lösche Kredit "Kerstin"
|
|
cursor.execute("SELECT id, name FROM kredite WHERE name ILIKE '%kerstin%'")
|
|
kerstin_kredite = cursor.fetchall()
|
|
|
|
if kerstin_kredite:
|
|
for kredit_id, name in kerstin_kredite:
|
|
print(f"Lösche Kredit: {name} (ID: {kredit_id})")
|
|
cursor.execute("DELETE FROM kredit_zahlungen WHERE kredit_id = %s", (kredit_id,))
|
|
cursor.execute("DELETE FROM kredit_buchungen WHERE kredit_id = %s", (kredit_id,))
|
|
cursor.execute("DELETE FROM kredite WHERE id = %s", (kredit_id,))
|
|
print(f" -> Gelöscht")
|
|
else:
|
|
print("Kein Kredit 'Kerstin' gefunden")
|
|
|
|
conn.commit()
|
|
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 2: Excel Tabelle1 analysieren (Kredit-Details)")
|
|
print("="*60)
|
|
|
|
# Excel-Datei laden mit openpyxl für berechnete Werte
|
|
wb = openpyxl.load_workbook('Schulden Kerstin.xlsx', data_only=True)
|
|
ws1 = wb['Tabelle1']
|
|
|
|
# Erste Zeile: Restsumme=15000, Rate=350, Zinssatz=0.04
|
|
restsumme = ws1.cell(2, 1).value # A2
|
|
rate = ws1.cell(2, 2).value # B2
|
|
zinssatz = ws1.cell(2, 3).value # C2
|
|
start_datum = ws1.cell(2, 5).value # E2
|
|
|
|
print(f"Kredit-Details aus Excel:")
|
|
print(f" Restsumme/Startbetrag: {restsumme} €")
|
|
print(f" Monatsrate: {rate} €")
|
|
print(f" Zinssatz: {zinssatz} ({zinssatz*100 if zinssatz else 0}%)")
|
|
print(f" Startdatum: {start_datum}")
|
|
|
|
wb.close()
|
|
|
|
# Jetzt Tabelle2 für Zahlungen/Auslagen
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 3: Excel Tabelle2 analysieren (Zahlungen)")
|
|
print("="*60)
|
|
|
|
wb2 = openpyxl.load_workbook('Schulden Kerstin.xlsx', data_only=True)
|
|
ws2 = wb2['Tabelle2']
|
|
|
|
zahlungen = []
|
|
for row in ws2.iter_rows(min_row=2, values_only=True): # Ab Zeile 2 (nach Header)
|
|
datum = row[0] # Spalte A
|
|
beschreibung = row[1] # Spalte B
|
|
betrag = row[2] # Spalte C
|
|
|
|
# Überspringe leere Zeilen
|
|
if betrag is None or (datum is None and beschreibung is None):
|
|
continue
|
|
|
|
# Konvertiere Betrag
|
|
try:
|
|
betrag_val = float(betrag)
|
|
except:
|
|
continue
|
|
|
|
# Konvertiere Datum
|
|
datum_str = None
|
|
if datum and not pd.isna(datum):
|
|
if isinstance(datum, datetime):
|
|
datum_str = datum.strftime('%Y-%m-%d')
|
|
elif isinstance(datum, str) and len(datum) >= 10:
|
|
datum_str = datum[:10]
|
|
|
|
# Bestimme Typ basierend auf Betrag und Beschreibung
|
|
# Negative Beträge = Rückzahlungen (monatsrate)
|
|
# Positive Beträge = neue Ausgaben (auslage)
|
|
|
|
if betrag_val < 0:
|
|
# Negative Werte = Rückzahlungen
|
|
typ = 'monatsrate'
|
|
notiz = beschreibung if beschreibung else "Rate gezahlt"
|
|
else:
|
|
# Positive Werte = neue Ausgaben/Auslagen
|
|
typ = 'auslage'
|
|
notiz = beschreibung if beschreibung else "Auslage"
|
|
|
|
zahlungen.append({
|
|
'datum': datum_str,
|
|
'typ': typ,
|
|
'betrag': abs(betrag_val), # Absolutwert für DB
|
|
'roh_betrag': betrag_val,
|
|
'notiz': str(notiz) if notiz else ""
|
|
})
|
|
|
|
print(f"Gefunden: {len(zahlungen)} Zahlungen/Auslagen")
|
|
for z in zahlungen[:15]: # Erste 15 zeigen
|
|
print(f" {z['datum'] or '----------'} | {z['typ']:12} | {z['roh_betrag']:10.2f} € | {z['notiz'][:30]}")
|
|
if len(zahlungen) > 15:
|
|
print(f" ... und {len(zahlungen)-15} weitere")
|
|
|
|
summe_raten = sum(z['roh_betrag'] for z in zahlungen if z['typ'] == 'monatsrate')
|
|
summe_auslagen = sum(z['roh_betrag'] for z in zahlungen if z['typ'] == 'auslage')
|
|
|
|
print(f"\nZusammenfassung:")
|
|
print(f" Monatsraten (negativ): {summe_raten:.2f} €")
|
|
print(f" Auslagen (positiv): {summe_auslagen:.2f} €")
|
|
print(f" Netto gezahlt: {summe_raten + summe_auslagen:.2f} €")
|
|
|
|
wb2.close()
|
|
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 4: Kredit anlegen")
|
|
print("="*60)
|
|
|
|
# Kredit-Daten
|
|
kredit_name = "Kerstin Schulden"
|
|
kredit_betrag = float(restsumme) if restsumme else 15000.0
|
|
zinssatz_pct = float(zinssatz) * 100 if zinssatz else 4.0
|
|
start_dt = start_datum.strftime('%Y-%m-%d') if isinstance(start_datum, datetime) else '2014-08-01'
|
|
monatsrate = float(rate) if rate else 350.0
|
|
richtung = "ausgehend" # Schulden - ich zahle
|
|
|
|
# Berechne Restschuld
|
|
restschuld = kredit_betrag + summe_raten + summe_auslagen
|
|
|
|
print(f"Kredit: {kredit_name}")
|
|
print(f" Ursprungsschuld: {kredit_betrag:.2f} €")
|
|
print(f" Zinssatz: {zinssatz_pct}%")
|
|
print(f" Start: {start_dt}")
|
|
print(f" Richtung: {richtung}")
|
|
print(f" Berechnete Restschuld: {restschuld:.2f} €")
|
|
|
|
# Kredit einfügen
|
|
cursor.execute("""
|
|
INSERT INTO kredite (name, kreditgeber, person, ursprungsschuld, restschuld,
|
|
monatsrate, zinssatz, start_datum, notizen, richtung, status)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
RETURNING id
|
|
""", (
|
|
kredit_name,
|
|
"Kerstin",
|
|
"Kerstin",
|
|
kredit_betrag,
|
|
restschuld,
|
|
monatsrate,
|
|
zinssatz_pct,
|
|
start_dt,
|
|
"Importiert aus Excel 'Schulden Kerstin.xlsx'",
|
|
richtung,
|
|
"aktiv" if restschuld > 0 else "abgeschlossen"
|
|
))
|
|
|
|
kredit_id = cursor.fetchone()[0]
|
|
conn.commit()
|
|
print(f"\nKredit angelegt mit ID: {kredit_id}")
|
|
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 5: Zahlungen importieren")
|
|
print("="*60)
|
|
|
|
# Zahlungen einfügen
|
|
for z in zahlungen:
|
|
cursor.execute("""
|
|
INSERT INTO kredit_zahlungen (kredit_id, datum, betrag, typ, notizen)
|
|
VALUES (%s, %s, %s, %s, %s)
|
|
""", (
|
|
kredit_id,
|
|
z['datum'],
|
|
z['betrag'], # Absolutwert
|
|
z['typ'],
|
|
z['notiz'][:200] if z['notiz'] else "" # Begrenze auf 200 Zeichen
|
|
))
|
|
print(f" {z['datum'] or '----------'} | {z['typ']:12} | {z['roh_betrag']:10.2f} € | {z['notiz'][:40] if z['notiz'] else ''}")
|
|
|
|
conn.commit()
|
|
|
|
print("\n" + "="*60)
|
|
print("SCHRITT 6: Verifikation")
|
|
print("="*60)
|
|
|
|
# Kredit prüfen
|
|
cursor.execute("SELECT * FROM kredite WHERE id = %s", (kredit_id,))
|
|
kredit = cursor.fetchone()
|
|
print(f"\nKredit in DB:")
|
|
print(f" Name: {kredit[1]}")
|
|
print(f" Ursprungsschuld: {float(kredit[4]):.2f} €")
|
|
print(f" Restschuld: {float(kredit[5]):.2f} €")
|
|
print(f" Richtung: {kredit[12]}")
|
|
print(f" Status: {kredit[13]}")
|
|
|
|
# Zahlungen prüfen
|
|
cursor.execute("SELECT * FROM kredit_zahlungen WHERE kredit_id = %s ORDER BY datum", (kredit_id,))
|
|
imported_zahlungen = cursor.fetchall()
|
|
print(f"\nZahlungen in DB: {len(imported_zahlungen)}")
|
|
for z in imported_zahlungen[:10]:
|
|
print(f" {z[2]} | {z[4]:12} | {float(z[3]):10.2f} € | {z[5][:40] if z[5] else ''}")
|
|
if len(imported_zahlungen) > 10:
|
|
print(f" ... und {len(imported_zahlungen)-10} weitere")
|
|
|
|
# Summen prüfen
|
|
cursor.execute("""
|
|
SELECT
|
|
COALESCE(SUM(CASE WHEN typ = 'monatsrate' THEN betrag ELSE 0 END), 0) as sum_raten,
|
|
COALESCE(SUM(CASE WHEN typ = 'auslage' THEN betrag ELSE 0 END), 0) as sum_auslagen,
|
|
COALESCE(SUM(betrag), 0) as sum_total
|
|
FROM kredit_zahlungen
|
|
WHERE kredit_id = %s
|
|
""", (kredit_id,))
|
|
result = cursor.fetchone()
|
|
sum_raten_db = float(result[0])
|
|
sum_auslagen_db = float(result[1])
|
|
sum_total_db = float(result[2])
|
|
|
|
print(f"\nSummen in DB:")
|
|
print(f" Raten: {sum_raten_db:.2f} €")
|
|
print(f" Auslagen: {sum_auslagen_db:.2f} €")
|
|
print(f" Gesamt: {sum_total_db:.2f} €")
|
|
|
|
# Erwartete Werte (Excel hat negative Werte für Raten)
|
|
sum_raten_excel = abs(summe_raten) # Negativ in Excel = positiv in DB
|
|
sum_auslagen_excel = summe_auslagen # Positiv in Excel = positiv in DB
|
|
|
|
print(f"\nVergleich mit Excel:")
|
|
print(f" Excel Raten: {sum_raten_excel:.2f} € | DB: {sum_raten_db:.2f} €")
|
|
print(f" Excel Auslagen: {sum_auslagen_excel:.2f} € | DB: {sum_auslagen_db:.2f} €")
|
|
|
|
# Prüfe Restschuld
|
|
expected_restschuld = kredit_betrag - sum_raten_db + sum_auslagen_db
|
|
print(f"\nRestschuld-Check:")
|
|
print(f" Erwartet: {expected_restschuld:.2f} €")
|
|
print(f" In DB: {float(kredit[5]):.2f} €")
|
|
|
|
if abs(sum_raten_excel - sum_raten_db) < 0.01 and abs(sum_auslagen_excel - sum_auslagen_db) < 0.01:
|
|
print(f"\n✅ VERIFIKATION ERFOLGREICH: Alle Daten stimmen überein!")
|
|
else:
|
|
print(f"\n⚠️ Hinweis: Kleinere Rundungsdifferenzen möglich")
|
|
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
print("\n" + "="*60)
|
|
print("IMPORT ABGESCHLOSSEN")
|
|
print("="*60)
|
|
print(f"Kredit 'Kerstin Schulden' ist jetzt in der Datenbank.")
|
|
print(f"Dashboard sollte jetzt 'Meine Schulden' (rot) anzeigen.")
|