import psycopg2 import psycopg2.extras import datetime import os from dotenv import load_dotenv # ANSI Escape Codes für Farben und Unterstreichung RESET = "\033[0m" # Zurücksetzen auf Standard RED = "\033[31m" # Rote Schrift GREEN = "\033[32m" # Grüne Schrift BLUE = "\033[34m" # Blaue Schrift BOLD = "\033[1m" # Fett ITALIC = "\033[3m" # Kursiv UNDERLINE = "\033[4m" # Unterstreichen # .env-Datei laden load_dotenv() # Zugriff auf die Umgebungsvariablen DB_HOST = os.getenv("DB_HOST") DB_PORT = os.getenv("DB_PORT") DB_NAME = os.getenv("DB_NAME") DB_USER = os.getenv("DB_USER") DB_PASSWORD = os.getenv("DB_PASSWORD") # Verbindung zur PostgreSQL-Datenbank herstellen def connect_db(): return psycopg2.connect( dbname=DB_NAME, user=DB_USER, password=DB_PASSWORD, host=DB_HOST, port=DB_PORT ) def create_table(): conn = connect_db() cur = conn.cursor() cur.execute(''' CREATE TABLE IF NOT EXISTS kasse ( id SERIAL PRIMARY KEY, beschreibung TEXT NOT NULL, wert NUMERIC(10,2) NOT NULL, typ TEXT CHECK (typ IN ('Einnahme', 'Ausgabe')) NOT NULL, bar BOOLEAN NOT NULL, timestamp TIMESTAMP NOT NULL DEFAULT NOW(), rechnungsnummer TEXT, kommentar TEXT, anlass TEXT ); ''') conn.commit() cur.close() conn.close() def initial_setup(): conn = connect_db() cur = conn.cursor() cur.execute("SELECT COUNT(*) FROM kasse;") count = cur.fetchone()[0] if count == 0: start_konto = float(input("Startwert Konto: ")) start_bar = float(input("Startwert Bar: ")) cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar) VALUES (%s, %s, %s, %s)", ('Startwert Konto', start_konto, 'Einnahme', False)) cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar) VALUES (%s, %s, %s, %s)", ('Startwert Bar', start_bar, 'Einnahme', True)) conn.commit() cur.close() conn.close() def parse_timestamp(date_str, time_str): if not date_str: date_str = datetime.datetime.now().strftime("%d.%m.%y") if not time_str: time_str = datetime.datetime.now().strftime("%H:%M") return datetime.datetime.strptime(f"{date_str} {time_str}", "%d.%m.%y %H:%M") def erfassen(typ): beschreibung = input("Beschreibung: ") wert = float(input("Wert: ")) bar = input("Bar? (1 = Ja, 0 = Nein): ") == '1' rechnungsnummer = input("Rechnungsnummer (optional): ") or None kommentar = input("Kommentar (optional): ") or None anlass = input("Anlass (optional): ") or None date_str = input("Datum (TT.MM.JJ, leer für heute): ") time_str = input("Uhrzeit (HH:MM, leer für jetzt): ") timestamp = parse_timestamp(date_str, time_str) conn = connect_db() cur = conn.cursor() cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar, timestamp, rechnungsnummer, kommentar, anlass) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)", (beschreibung, wert, typ, bar, timestamp, rechnungsnummer, kommentar, anlass)) conn.commit() cur.close() conn.close() print("Buchung erfasst!") exit = input(f"\nEnter -> zurück zum Menü ") def umbuchung(): wert = float(input("Betrag der Umbuchung: ")) richtung = input("Umbuchung Richtung? (1 = Bar -> Konto, 0 = Konto -> Bar): ") == '1' date_str = input("Datum (TT.MM.JJ, leer für heute): ") time_str = input("Uhrzeit (HH:MM, leer für jetzt): ") timestamp = parse_timestamp(date_str, time_str) conn = connect_db() cur = conn.cursor() if richtung: cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar, timestamp, anlass, gegenueber) VALUES (%s, %s, %s, %s, %s, %s, %s)", ('Umbuchung nach Konto', wert, 'Ausgabe', True, timestamp, 'Orga', 'SJTKD')) cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar, timestamp, anlass, gegenueber) VALUES (%s, %s, %s, %s, %s, %s, %s)", ('Umbuchung von Bar', wert, 'Einnahme', False, timestamp, 'Orga', 'SJTKD')) else: cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar, timestamp, anlass, gegenueber) VALUES (%s, %s, %s, %s, %s, %s, %s)", ('Umbuchung nach Bar', wert, 'Ausgabe', False, timestamp, 'Orga', 'SJTKD')) cur.execute("INSERT INTO kasse (beschreibung, wert, typ, bar, timestamp, anlass, gegenueber) VALUES (%s, %s, %s, %s, %s, %s, %s)", ('Umbuchung von Konto', wert, 'Einnahme', True, timestamp, 'Orga', 'SJTKD')) conn.commit() cur.close() conn.close() print("Umbuchung erfasst!") exit = input(f"\nEnter -> zurück zum Menü ") def kassenbestand(): conn = connect_db() cur = conn.cursor() cur.execute("SELECT SUM(wert) FROM kasse WHERE typ='Einnahme' AND bar=True") einnahmen_bar = cur.fetchone()[0] or 0 cur.execute("SELECT SUM(wert) FROM kasse WHERE typ='Ausgabe' AND bar=True") ausgaben_bar = cur.fetchone()[0] or 0 cur.execute("SELECT SUM(wert) FROM kasse WHERE typ='Einnahme' AND bar=False") einnahmen_konto = cur.fetchone()[0] or 0 cur.execute("SELECT SUM(wert) FROM kasse WHERE typ='Ausgabe' AND bar=False") ausgaben_konto = cur.fetchone()[0] or 0 barbestand = einnahmen_bar - ausgaben_bar kontostand = einnahmen_konto - ausgaben_konto summe = barbestand + kontostand print(UNDERLINE + "Kassenbestand:" + RESET) print(BOLD + "Bar: " + RESET + f"{einnahmen_bar - ausgaben_bar:>10.2f} €") print(BOLD + "Konto: " + RESET + f"{einnahmen_konto - ausgaben_konto:>10.2f} €\n==============") print(BOLD + "Summe: " + RESET + f"{summe:>10.2f} €") cur.close() conn.close() exit = input(f"\nEnter -> zurück zum Menü ") def anzeigen(filter_typ=None): conn = connect_db() cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) if filter_typ: cur.execute("SELECT * FROM kasse WHERE typ = %s ORDER BY timestamp ASC", (filter_typ,)) else: cur.execute("SELECT * FROM kasse ORDER BY timestamp ASC") rows = cur.fetchall() for row in rows: formatted_date = row['timestamp'].strftime("%d.%m.%y") print(f"{row['id']:<4} {row['beschreibung']:<35} | " + f"{GREEN if row['typ']=='Einnahme' else RED}" + f"{row['wert']:>7} € " + RESET + f" | {'Bar' if row['bar'] else 'Konto':<6} | {formatted_date} | {row['rechnungsnummer'] or '':<15} | {row['kommentar'] or '':<10} | {row['anlass'] or '':<10}") cur.close() conn.close() exit = input(f"\nEnter -> zurück zum Menü ") def anzeigen_anlass(): filter_typ = input("Anlass: ") conn = connect_db() cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute("SELECT * FROM kasse WHERE anlass = %s ORDER BY timestamp ASC", (filter_typ,)) rows = cur.fetchall() for row in rows: formatted_date = row['timestamp'].strftime("%d.%m.%y") print(f"{row['id']:<4} {row['beschreibung']:<35} | " + f"{GREEN if row['typ']=='Einnahme' else RED}" + f"{row['wert']:>7} € " + RESET + f" | {'Bar' if row['bar'] else 'Konto':<6} | {formatted_date} | {row['rechnungsnummer'] or '':<10} | {row['kommentar'] or '':<10}") cur.execute("SELECT SUM(wert) FROM kasse WHERE typ = 'Einnahme' AND anlass = %s", (filter_typ,)) einnahmen = cur.fetchone()[0] or 0 cur.execute("SELECT SUM(wert) FROM kasse WHERE typ='Ausgabe' AND anlass = %s", (filter_typ,)) ausgaben = cur.fetchone()[0] or 0 print(UNDERLINE + "\nBilanz:" + RESET) text="Einnahmen:" print(BOLD + f"{text:<8}" + RESET + GREEN + f"{einnahmen:>10.2f} €" + RESET) text="Ausgaben: " print(BOLD + f"{text:<8}" + RESET + RED + f"{ausgaben:>10.2f} €" + RESET + f"\n===================") summe = einnahmen - ausgaben COLOR = GREEN if summe<0: COLOR = RED text="Summe: " print(BOLD + f"{text:<8}" + RESET + COLOR + f"{summe:>10.2f} €" + RESET) cur.close() conn.close() exit = input(f"\nEnter -> zurück zum Menü ") def menu(): create_table() initial_setup() while True: print(UNDERLINE + "\nKassenbuch Menü:" + RESET) print(BOLD + "(1)" + RESET + " Ausgabe erfassen") print(BOLD + "(2)" + RESET + " Einnahme erfassen") print(BOLD + "(3)" + RESET + " Kassenbestand anzeigen") print(BOLD + "(4)" + RESET + " Umsätze anzeigen") print(BOLD + "(5)" + RESET + " Ausgaben anzeigen") print(BOLD + "(6)" + RESET + " Einnahmen anzeigen") print(BOLD + "(7)" + RESET + " Umbuchung durchführen") print(BOLD + "(8)" + RESET + " Bilanz Veranstaltung") print(BOLD + "(0)" + RESET + " Beenden") auswahl = input("Auswahl: ") print("\n") if auswahl == '1': erfassen('Ausgabe') elif auswahl == '2': erfassen('Einnahme') elif auswahl == '3': kassenbestand() elif auswahl == '4': anzeigen() elif auswahl == '5': anzeigen('Ausgabe') elif auswahl == '6': anzeigen('Einnahme') elif auswahl == '7': umbuchung() elif auswahl == '8': anzeigen_anlass() elif auswahl == '0': break if __name__ == "__main__": menu()