295 lines
No EOL
6.8 KiB
Python
295 lines
No EOL
6.8 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from pathlib import Path
|
|
from datetime import date, timedelta, datetime
|
|
from urllib.request import urlopen
|
|
from fints.client import FinTS3PinTanClient
|
|
import csv
|
|
import json
|
|
import sys
|
|
import logging
|
|
import platform
|
|
from pathlib import Path
|
|
|
|
if platform.system() == "Windows":
|
|
log_dir = Path(__file__).parent / "logs"
|
|
else:
|
|
log_dir = Path.home() / "logs"
|
|
|
|
log_dir.mkdir(exist_ok=True)
|
|
|
|
log_file = log_dir / "ing.log"
|
|
|
|
logging.basicConfig(
|
|
filename=log_file,
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
|
force=True
|
|
)
|
|
|
|
logging.info("[transactions] === Scriptstart ===")
|
|
|
|
# --------------------------------------------------
|
|
# Konfiguration laden
|
|
# --------------------------------------------------
|
|
|
|
config = {}
|
|
|
|
config_file = Path.home() / ".ing.conf"
|
|
|
|
if not config_file.exists():
|
|
config_file = Path(__file__).parent / ".ing.conf"
|
|
|
|
with open(config_file, encoding="utf-8") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
|
|
if "=" not in line:
|
|
continue
|
|
|
|
key, value = line.split("=", 1)
|
|
config[key] = value
|
|
|
|
# --------------------------------------------------
|
|
# ING-Konfiguration
|
|
# --------------------------------------------------
|
|
|
|
USER = config["ING_USER"]
|
|
PIN = config["ING_PIN"]
|
|
TARGET_IBAN = config["ING_IBAN"]
|
|
|
|
# --------------------------------------------------
|
|
# ioBroker-Konfiguration
|
|
# --------------------------------------------------
|
|
|
|
IOBROKER_HOST = config["IOBROKER_HOST"]
|
|
IOBROKER_PORT = config["IOBROKER_PORT"]
|
|
|
|
IOBROKER_DP_TRANSACTIONS = config["IOBROKER_DP_TRANSACTIONS"]
|
|
IOBROKER_DP_WEEKSUM = config["IOBROKER_DP_WEEKSUM"]
|
|
IOBROKER_DP_WEEKEXPENSES = config["IOBROKER_DP_WEEKEXPENSES"]
|
|
|
|
# --------------------------------------------------
|
|
# Bank
|
|
# --------------------------------------------------
|
|
|
|
BLZ = "50010517"
|
|
|
|
# --------------------------------------------------
|
|
# Exportverzeichnis bestimmen
|
|
# --------------------------------------------------
|
|
|
|
# --------------------------------------------------
|
|
# Exportverzeichnis bestimmen
|
|
# --------------------------------------------------
|
|
|
|
base_dir = Path(__file__).parent / "Transaktionen"
|
|
# --------------------------------------------------
|
|
# Kalenderwoche bestimmen
|
|
# --------------------------------------------------
|
|
|
|
if len(sys.argv) == 3:
|
|
|
|
year = int(sys.argv[1])
|
|
week = int(sys.argv[2])
|
|
|
|
else:
|
|
|
|
year, week, _ = datetime.now().isocalendar()
|
|
|
|
# Montag der Kalenderwoche
|
|
|
|
start_date = datetime.strptime(
|
|
f"{year}-W{week:02d}-1",
|
|
"%G-W%V-%u"
|
|
).date()
|
|
|
|
# Sonntag der Kalenderwoche
|
|
|
|
end_date = start_date + timedelta(days=6)
|
|
today = date.today()
|
|
|
|
if end_date > today:
|
|
end_date = today
|
|
|
|
if start_date > today:
|
|
logging.error(
|
|
"[transactions] Die angeforderte "
|
|
"Kalenderwoche liegt vollständig "
|
|
"in der Zukunft."
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
year_dir = base_dir / str(year)
|
|
csv_dir = year_dir / "csv"
|
|
json_dir = year_dir / "json"
|
|
|
|
csv_dir.mkdir(parents=True, exist_ok=True)
|
|
json_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
CSV_FILE = csv_dir / f"transactions_{year}_KW{week:02d}.csv"
|
|
JSON_FILE = json_dir / f"transactions_{year}_KW{week:02d}.json"
|
|
|
|
# --------------------------------------------------
|
|
# ING-Verbindung
|
|
# --------------------------------------------------
|
|
|
|
client = FinTS3PinTanClient(
|
|
BLZ,
|
|
USER,
|
|
PIN,
|
|
"https://fints.ing.de/fints/",
|
|
product_id="PythonFinTS"
|
|
)
|
|
|
|
export_data = []
|
|
transactions = []
|
|
|
|
# --------------------------------------------------
|
|
# Transaktionen abrufen
|
|
# --------------------------------------------------
|
|
|
|
with client:
|
|
account_found = False
|
|
|
|
for account in client.get_sepa_accounts():
|
|
|
|
if account.iban != TARGET_IBAN:
|
|
continue
|
|
|
|
account_found = True
|
|
|
|
transactions = client.get_transactions(
|
|
account,
|
|
start_date=start_date,
|
|
end_date=end_date
|
|
)
|
|
|
|
logging.info(
|
|
f"[transactions] Lade Transaktionen für KW{week:02d}/{year}"
|
|
)
|
|
|
|
logging.info(
|
|
f"[transactions] Zeitraum: {start_date} bis {end_date}"
|
|
)
|
|
|
|
for transaction in transactions:
|
|
|
|
data = transaction.data
|
|
amount = data.get("amount")
|
|
|
|
row = {
|
|
"date": str(data.get("date", "")),
|
|
"amount": round(float(amount.amount), 2) if amount else 0.0,
|
|
"posting_text": data.get("posting_text", ""),
|
|
"applicant_name": data.get("applicant_name", ""),
|
|
"purpose": data.get("purpose", "")
|
|
}
|
|
|
|
export_data.append(row)
|
|
|
|
break
|
|
|
|
if not account_found:
|
|
raise RuntimeError(
|
|
f"IBAN nicht gefunden: {TARGET_IBAN}"
|
|
)
|
|
|
|
# --------------------------------------------------
|
|
# CSV schreiben
|
|
# --------------------------------------------------
|
|
|
|
with open(CSV_FILE, "w", newline="", encoding="utf-8") as csvfile:
|
|
|
|
writer = csv.DictWriter(
|
|
csvfile,
|
|
fieldnames=[
|
|
"date",
|
|
"amount",
|
|
"posting_text",
|
|
"applicant_name",
|
|
"purpose"
|
|
],
|
|
delimiter=";"
|
|
)
|
|
|
|
writer.writeheader()
|
|
writer.writerows(export_data)
|
|
|
|
# --------------------------------------------------
|
|
# JSON schreiben
|
|
# --------------------------------------------------
|
|
|
|
with open(JSON_FILE, "w", encoding="utf-8") as jsonfile:
|
|
|
|
json.dump(
|
|
export_data,
|
|
jsonfile,
|
|
ensure_ascii=False,
|
|
indent=2
|
|
)
|
|
|
|
# --------------------------------------------------
|
|
# Statistiken berechnen
|
|
# --------------------------------------------------
|
|
|
|
transaction_count = len(export_data)
|
|
|
|
week_sum = 0.0
|
|
week_expenses = 0.0
|
|
|
|
for transaction in transactions:
|
|
|
|
amount = float(transaction.data["amount"].amount)
|
|
|
|
week_sum += amount
|
|
|
|
if amount < 0:
|
|
week_expenses += abs(amount)
|
|
|
|
week_sum = round(week_sum, 2)
|
|
week_expenses = round(week_expenses, 2)
|
|
|
|
# --------------------------------------------------
|
|
# An ioBroker senden
|
|
# --------------------------------------------------
|
|
|
|
base_url = f"http://{IOBROKER_HOST}:{IOBROKER_PORT}/set"
|
|
|
|
urlopen(
|
|
f"{base_url}/{IOBROKER_DP_TRANSACTIONS}"
|
|
f"?value={transaction_count}"
|
|
).read()
|
|
|
|
urlopen(
|
|
f"{base_url}/{IOBROKER_DP_WEEKSUM}"
|
|
f"?value={week_sum}"
|
|
).read()
|
|
|
|
urlopen(
|
|
f"{base_url}/{IOBROKER_DP_WEEKEXPENSES}"
|
|
f"?value={week_expenses}"
|
|
).read()
|
|
|
|
# --------------------------------------------------
|
|
# Erfolgsmeldung
|
|
# --------------------------------------------------
|
|
|
|
logging.info(
|
|
f"[transactions] Export abgeschlossen | "
|
|
f"Transaktionen={transaction_count} | "
|
|
f"Wochensumme={week_sum:.2f} EUR | "
|
|
f"Ausgaben={week_expenses:.2f} EUR"
|
|
)
|
|
|
|
logging.info(
|
|
f"[transactions] CSV={CSV_FILE}"
|
|
)
|
|
|
|
logging.info(
|
|
f"[transactions] JSON={JSON_FILE}"
|
|
) |