204 lines
6.3 KiB
Python
204 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Importiert "Schulden Niki.xlsx" ueber die REST API der Buchhaltungs-App.
|
|
"""
|
|
|
|
import openpyxl
|
|
import uuid
|
|
import json
|
|
import urllib.request
|
|
import urllib.error
|
|
from datetime import datetime
|
|
|
|
# API Konfiguration
|
|
API_BASE = "http://localhost:3001/api"
|
|
|
|
def read_excel_data(filepath):
|
|
"""Liest die Excel-Daten ein"""
|
|
wb = openpyxl.load_workbook(filepath, data_only=True)
|
|
ws = wb['Tabelle1']
|
|
|
|
data = []
|
|
headers = {}
|
|
|
|
# Zeile 6: Headers
|
|
for col_idx in range(1, 9):
|
|
cell = ws.cell(row=6, column=col_idx)
|
|
if cell.value:
|
|
headers[cell.value] = col_idx
|
|
|
|
print(f"Gefundene Spalten: {headers}")
|
|
|
|
# Zeile 7+: Daten
|
|
for row_idx in range(7, ws.max_row + 1):
|
|
row_data = {}
|
|
for header, col_idx in headers.items():
|
|
row_data[header] = ws.cell(row=row_idx, column=col_idx).value
|
|
|
|
if row_data.get('Datum'):
|
|
data.append(row_data)
|
|
|
|
return data
|
|
|
|
def api_call(endpoint, method='GET', data=None):
|
|
"""Fuehrt einen API-Call aus"""
|
|
url = f"{API_BASE}{endpoint}"
|
|
headers = {'Content-Type': 'application/json'}
|
|
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=json.dumps(data).encode() if data else None,
|
|
headers=headers,
|
|
method=method
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=30) as response:
|
|
return json.loads(response.read().decode())
|
|
except urllib.error.HTTPError as e:
|
|
print(f"HTTP Error {e.code}: {e.read().decode()}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
return None
|
|
|
|
def get_monthly_rate(data):
|
|
"""Ermittelt die monatliche Rate aus den Daten"""
|
|
rates = []
|
|
for row in data:
|
|
abgezahlt = row.get('Abgezahlt')
|
|
neue_kosten = row.get('Neue Kosten')
|
|
|
|
if abgezahlt and abgezahlt > 0 and (not neue_kosten or neue_kosten == 0):
|
|
rates.append(abgezahlt)
|
|
|
|
if rates:
|
|
from collections import Counter
|
|
return Counter(rates).most_common(1)[0][0]
|
|
return 0
|
|
|
|
def import_niki_schulden():
|
|
"""Hauptimport-Funktion"""
|
|
|
|
print("=" * 60)
|
|
print("IMPORT: Schulden Niki (via API)")
|
|
print("=" * 60)
|
|
|
|
# 1. Excel-Daten lesen
|
|
print("\n1. Lese Excel-Daten...")
|
|
EXCEL_FILE = r'C:\Users\renet\.openclaw\workspace\buchhaltungs-app\Schulden Niki.xlsx'
|
|
data = read_excel_data(EXCEL_FILE)
|
|
print(f" {len(data)} Datensaetze gefunden")
|
|
|
|
if not data:
|
|
print(" KEINE DATEN GEFUNDEN!")
|
|
return False
|
|
|
|
first_row = data[0]
|
|
start_datum = first_row.get('Datum')
|
|
restschuld_start = abs(first_row.get('Restschuld', 0))
|
|
|
|
print(f" Startdatum: {start_datum}")
|
|
print(f" Ursprungsschuld: {restschuld_start}")
|
|
|
|
monatsrate = get_monthly_rate(data)
|
|
print(f" Erkannte Monatsrate: {monatsrate}")
|
|
|
|
# 2. Kredit erstellen
|
|
print("\n2. Erstelle Kredit...")
|
|
|
|
last_row = data[-1]
|
|
restschuld_aktuell = abs(last_row.get('Restschuld', 0))
|
|
|
|
kredit_data = {
|
|
'name': 'Niki Schulden',
|
|
'kreditgeber': 'Rene Taeger',
|
|
'person': 'Niki',
|
|
'ursprungsschuld': float(restschuld_start),
|
|
'restschuld': float(restschuld_aktuell),
|
|
'monatsrate': float(monatsrate) if monatsrate > 0 else 0,
|
|
'zinssatz': 10.0,
|
|
'start_datum': start_datum.strftime('%Y-%m-%d') if hasattr(start_datum, 'strftime') else str(start_datum)[:10],
|
|
'status': 'aktiv'
|
|
}
|
|
|
|
print(f" Kredit-Daten: {json.dumps(kredit_data, indent=2)}")
|
|
|
|
result = api_call('/kredite', method='POST', data=kredit_data)
|
|
|
|
if not result:
|
|
print(" [ERR] Fehler beim Erstellen des Kredits")
|
|
return False
|
|
|
|
kredit_id = result.get('id')
|
|
print(f" [OK] Kredit erstellt mit ID: {kredit_id}")
|
|
|
|
# 3. Zahlungen importieren (nutzt Endpunkt /kredite/{id}/zahlungen)
|
|
print("\n3. Importiere Zahlungen...")
|
|
zahlungen_count = 0
|
|
|
|
for idx, row in enumerate(data):
|
|
datum = row.get('Datum')
|
|
abgezahlt = row.get('Abgezahlt') or 0
|
|
neue_kosten = row.get('Neue Kosten') or 0
|
|
fuer_was = row.get('Fuer was', '') or row.get('F�r was', '')
|
|
|
|
# Abgezahlt als Zahlung
|
|
if abgezahlt and abgezahlt > 0:
|
|
zahlung_data = {
|
|
'betrag': float(abgezahlt),
|
|
'datum': datum.strftime('%Y-%m-%d') if hasattr(datum, 'strftime') else str(datum)[:10],
|
|
'typ': 'monatsrate',
|
|
'notiz': fuer_was if fuer_was else 'Monatsrate'
|
|
}
|
|
|
|
result = api_call(f'/kredite/{kredit_id}/zahlungen', method='POST', data=zahlung_data)
|
|
if result:
|
|
zahlungen_count += 1
|
|
else:
|
|
print(f" [ERR] Fehler bei Zahlung {idx + 1}")
|
|
|
|
# Neue Kosten als Auslage
|
|
if neue_kosten and neue_kosten < 0:
|
|
zahlung_data = {
|
|
'betrag': float(abs(neue_kosten)),
|
|
'datum': datum.strftime('%Y-%m-%d') if hasattr(datum, 'strftime') else str(datum)[:10],
|
|
'typ': 'auslage',
|
|
'notiz': fuer_was if fuer_was else 'Auslage'
|
|
}
|
|
|
|
result = api_call(f'/kredite/{kredit_id}/zahlungen', method='POST', data=zahlung_data)
|
|
if result:
|
|
zahlungen_count += 1
|
|
else:
|
|
print(f" [ERR] Fehler bei Auslage {idx + 1}")
|
|
|
|
print(f" [OK] {zahlungen_count} Zahlungen importiert")
|
|
|
|
# 4. Verifikation
|
|
print("\n4. Verifikation:")
|
|
result = api_call(f'/kredite/{kredit_id}/zahlungen')
|
|
if result:
|
|
print(f" Zahlungen in API: {len(result)}")
|
|
|
|
result = api_call(f'/kredite/{kredit_id}')
|
|
if result:
|
|
db_restschuld = result.get('restschuld')
|
|
print(f" Restschuld in DB: {db_restschuld}")
|
|
print(f" Restschuld in Excel: {restschuld_aktuell}")
|
|
|
|
if abs(float(db_restschuld) - float(restschuld_aktuell)) < 0.01:
|
|
print(" [OK] Restschuld stimmt ueberein!")
|
|
else:
|
|
print(" [WARN] Restschuld weicht ab!")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("IMPORT ABGESCHLOSSEN")
|
|
print("=" * 60)
|
|
|
|
return True
|
|
|
|
if __name__ == '__main__':
|
|
success = import_niki_schulden()
|
|
exit(0 if success else 1)
|