From 1f23d02cfed36a4727a962883cb9e67c5de419d2 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 28 Apr 2026 22:09:23 +0200 Subject: [PATCH] change structure, added readinging miete.csv --- .gitignore | 4 + README.md | 11 + __pycache__/config.cpython-312.pyc | Bin 0 -> 465 bytes __pycache__/forderungen.cpython-312.pyc | Bin 0 -> 17193 bytes config.py | 6 + forderungen.py | 367 ++++++++++++++++++++++++ main.py | 254 ++++------------ resources/debitoren.csv | 38 --- resources/preise_familienzimmer.csv | 8 - 9 files changed, 440 insertions(+), 248 deletions(-) create mode 100644 .gitignore create mode 100644 __pycache__/config.cpython-312.pyc create mode 100644 __pycache__/forderungen.cpython-312.pyc create mode 100644 config.py create mode 100644 forderungen.py delete mode 100644 resources/debitoren.csv delete mode 100644 resources/preise_familienzimmer.csv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c55d9b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +resources/ +output/ +*.csv +*.txt diff --git a/README.md b/README.md index 1a267a1..c535feb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # Buchhaltung-Tools +Dieses Tool soll in der Buchhaltung unterstützen Prozesse automatisiert ablaufen zu lassen +Es hat folgende Funktion: + Einlesen der Mietendatei und daraus ein .txt erstellen mit allen Mietforderungen (kaltmiete, Betriebskosten, Gesamt(als Split Buchung), CWS Miete und Familienzimmer Miete. für alle Debitoren + Berechnung der Mietforderungen für das Gästezimmer aus den Buchungsdaten + +weiter Funktion in Zukunft: + Berechnung der Nebenkostenabrechnung und Erstellen der Übersichten für aller Mieterinnen + + +Im Mai sind alle Buchungen die im Mai beendet wurden, fängt der Buchungszeitraum schon im Vormonat an, wird trotzdem der gesamte Zeitraum Mai zugeschalgen. + diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..049a366faf030b881a1a12908af1192022cf9130 GIT binary patch literal 465 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 KweTjkb^bpOXG>QA literal 0 HcmV?d00001 diff --git a/config.py b/config.py new file mode 100644 index 0000000..3713e8f --- /dev/null +++ b/config.py @@ -0,0 +1,6 @@ +filename_debitoren = "resources/debitoren.csv" +encoding_debitoren = "ISO-8859-1" +filename_fz = "resources/Booking_System.csv" +filname_price_familyroom = "resources/preise_familienzimmer.csv" +seperator_price_familyroom = "," +filename_mieten = "resources/2025_05_miete.csv" diff --git a/forderungen.py b/forderungen.py new file mode 100644 index 0000000..a759deb --- /dev/null +++ b/forderungen.py @@ -0,0 +1,367 @@ +import argparse +import csv +import config + + +KN_HABEN_MIETE_NK = "4106" +KN_HABEN_MIETE = "4105" +KN_HABEN_MIETE_CWS = "4107" +KOSTENSTELLE_CWS = "100" +KN_HABEN_MIETE_FZ = "4107" +KOSTENSTELLE_FZ = "200" + + + +class Forderung: + def __init__(self )->None: + self.name:list[str] = [] + self.belegdatum:str = "" + self.amount:float = 0 + self.text:str = "" + self.soll:int|None = None + self.haben:int|None = 0 + self.kostenstelle:int = 0 + self.kostentraeger:int = 0 + + def __str__(self): + return f"Name{self.name}, Haben {self.haben}, Soll {self.soll}, Amount {self.amount},Verwendung {self.text}" + + +def print_to_file_lexware(forderungen:dict): + """ + print Forderungen to a file, that can be read by lexware according the following format: + "Belegdatum";"Buchungsdatum";"Buchungstext"; "Buchungsbetrag"; "Sollkonto";"Habenkonto";"Kostenstelle" + 01.03.2025; 19.02.2026;2; "Miete Familienzimmer"; 357,08; 12161; 4107; 200 + """ + print('Start Drucken der Forderugen') + names = read_account_names(config.filename_debitoren) + for period, forderung in forderungen.items(): + if forderung !={}: + filename = f"output/{period[0]}-{period[1]:02d}_Forderungen_Familienzimmer.csv" + print(f"\t ...{filename}") + with open(filename, "w",encoding="cp1252", newline="\r\c") as file: + file.write("\"Belegdatum\";\"Buchungstext\";\"Buchungsbetrag\";\"Sollkonto\";\"Habenkonto\";\"Kostenstelle\"\n") + for key in forderung: + f = forderung[key] + name = names[key] + file.write(f"01.{period[1]:02d}.{period[0]};\"{name} {f.text}\";{f.amount};{key};{f.haben};{f.kostenstelle}\n") + print("Fertig mit Drucken der Forderungen.") + +def create_forderungen(forderungen:dict, period:tuple, id_subject:tuple, belegdatum:str, amount:float, soll:int|None, haben:str, kostenstelle:str)->dict: + """ + create the recievables in the dict and incerting the date. + if the amount is 0 this nothings added, + if tow recievables have the same period and subject, than the amounts will be added + """ + if amount == 0: + return forderungen + if period not in forderungen.keys(): + forderungen[period] = {} + if id_subject not in forderungen[period]: + forderungen[period][id_subject] = Forderung() + forderungen[period][id_subject].amount = round(amount,2) + forderungen[period][id_subject].text = f"{id_subject[1]} {period[0]}-{period[1]:02d}" + forderungen[period][id_subject].kostenstelle = kostenstelle + forderungen[period][id_subject].haben = haben + forderungen[period][id_subject].soll = soll + forderungen[period][id_subject].belegdatum = belegdatum + else: + forderungen[period][id_subject].amount = round(amount+forderungen[period][id_subject].amount) + return forderungen + +def create_forderungen_miete(filename:str, accounts:dict, forderungen:dict, month:int, year:int)->dict: + """ + Create all recievables for one Month from the "mieten.csv" file, warm (split to Nk and kalt+Parkplatz(+soli), cws, and familyrooms + """ + print(f"{year} {month} Start Einlesen der Mietendatei von {filename}") + try: + file = list(csv.DictReader(open(filename, 'r'))) + period = (year,month) + for line in file: + kaltmiete = getFloat(line["Betrag Kaltmiete"]) + if kaltmiete > 0: + name = getWords(line["Vorname, Name"]) + id = getID(accounts, name) + if id > 0: + #calculate the Miete + betriebskosten = getFloat(line["Betrag Betriebskosten"]) + stellplatz = getFloat(line["Betrag zusätzlicher Stellpl."]) + zusatzVereinbarung = getFloat(line["Betrag Vereinbarung"]) + mieteCWS = getFloat(line["Betrag Gemeinschaftsbuero"]) + betragFz = getFloat(line["Betrag Gaestezimmer"]) + + miete = kaltmiete + zusatzVereinbarung + stellplatz + warmmiete = miete + betriebskosten + + datum = f"01.{period[1]:02d}.{period[0]}" + create_forderungen(forderungen, period, (id, "Miete Gesamt"),datum, + warmmiete, id, "0", "") + create_forderungen(forderungen, period, (id, "Miete NK"), "", + betriebskosten, None, KN_HABEN_MIETE_NK,"") + create_forderungen(forderungen, period, (id, "Miete"),"", + miete, None, KN_HABEN_MIETE,"") + create_forderungen(forderungen, period, (id, "Miete CWS"),datum, + mieteCWS, id, KN_HABEN_MIETE_CWS, KOSTENSTELLE_CWS) + create_forderungen(forderungen, period, (id, "Miete Familienzimmer"), datum, + betragFz, id, KN_HABEN_MIETE_FZ, KOSTENSTELLE_FZ) + + else: + print(f"Fehler: kein name gefunden!!!{name} {getID(accounts, name)} {line}") + print(f"Fertig, {year} {month} Erstellen von {len(forderungen[period])} Forderungen abgeschlossen.") + except IOError: + print(f"Fehler, Datei {filename} nicht gefunden.") + return forderungen + +def create_forderungen_familienzimmer(filename:str, accounts:dict, forderungen:dict)->dict: + """ + Create the recievables from the familyrooms from the file from the inputfile, + assings it to the accouts, calculate the price, stores results in forderungen + """ + print(f"Start Einlesen der Buchungen des Familienźimmers von {filename}") + priceFamilyRooms = readPriceFamilyRooms(config.filname_price_familyroom) + counter = 0 + try: + with open(filename, "r") as file: + reader = csv.DictReader(file) + for row in reader: + name = row["Created by"] + name = getWords(name) + id_subject = (getID(accounts, name[1:]), "Familienzimmer") + + month = getMonth(row["End time"].split(" ")[2]) + year = row["End time"].split(" ")[3] + period = (year, month) + if id_subject[0] > 0: + room = row["Room"].replace("\"","") + duration = getDuration(row["Duration"]) + number_person = getNumberPerson(row["Number of guests"]) + price = getPrice(room, priceFamilyRooms, duration, number_person) + #print(f"{name[1]} {id_subject} {room} dauer {duration} Tage, personen {number_person} preis {price}") + datum = f"01.{period[1]:02d}.{period[0]}" + + create_forderungen(forderungen,period,id_subject,datum, + price,id_subject[0],KN_HABEN_MIETE_FZ,KOSTENSTELLE_FZ) + counter+=1 + else: + print(f"Fehler, Name nicht gefunden\n\t{row}") + print(f"Fertig mit Erstellen von {counter} Forderungen") + except IOError: + print(f"Fehler, Datei {filename} nicht gefunden.") + return forderungen + +def getFloat(field:str)->float: + """ + converts a number from not empty string (english format e.g. 1,946.44) to a float, rounding to 2, digits after the point + if the string is empty returns 0 + """ + if field.isnumeric and len(field)>0: + return round(float(field.replace(",", "")),2) + else: + return 0 + +def getWords(sentence:str)->list: + """ + Takes a sequence of chars (sentence), replaces special signs from the alphabet and returns a list of all words without special chars + """ + result = [] + word = '' + #sentence = sentence.lower() + sentence = sentence.replace("ä", "ae").replace("ö", "oe").replace("ü", "ue").replace("ß", "ss") + for c in sentence: + if ord("a")<= ord(c) <= ord("z") or ord("A")<= ord(c) <= ord("Z") or ord("0")<= ord(c) <= ord("9"): + word = word + c + elif len(word)>0: + result.append(word) + word = "" + if len(word)>0: + result.append(word) + return result + +def getPrice(room:str, prices:dict, duration:int, numberPerson:int)->int: + """ + calculate the price for one booking, 15 Euro per night, 5 Euro extra per extra person, + if there is no specific number take the max of person + """ + if numberPerson > 0: + return min(prices["proPerson"]*numberPerson+prices["Basis"],prices[room])*duration + else: + return prices[room]*duration#or 15*duratiion if it was before Okt 2025?? + +def getNumberPerson(field:str)->int: + if len(field) >0: + return int(field) + else: + return 0 + +def getID(accounts:dict, names:list)->int: + for name in names: + if name in accounts: + return int(accounts[name]) + return 0 + +def getMonth(month:str)->int: + month = month.lower() + if month == "january" or month =="januar": + return 1 + elif month == "february" or month =="februar": + return 2 + elif month == "march" or month =="märz": + return 3 + elif month == "april": + return 4 + elif month == "may" or month =="mai": + return 5 + elif month == "june" or month =="juni": + return 6 + elif month == "july" or month =="juli": + return 7 + elif month == "august": + return 8 + elif month == "september": + return 9 + elif month == "october" or month =="oktober": + return 10 + elif month == "november": + return 11 + elif month == "december" or month =="dezember": + return 12 + else: + return 0 + +def getDuration(duration:str)->int: + duration = duration.split(" ") + if duration[1] == "weeks" or duration[1] == "Woche": + return int(duration[0]) * 7 + elif duration[1] == "days" or duration[1] == "Tage": + return int(duration[0]) + +def read_account_ids(filename:str)->dict: + """ + Create a dictionary from name to account id for first and last name couples included, + names that are not unique will be removed + """ + print(f"Start Einlesen der Namen und Account IDs von {filename}.") + accounts = {} + number_accounts = 0 + doubles = [] + try: + with open(filename, "r",encoding=config.encoding_debitoren) as file: + for line in file: + line = getWords(line) + if len(line) >=2: + id = line[-1] + names = line[:-1] + number_accounts += 1 + for name in names: + if name in accounts: + doubles.append(name) + else: + accounts[name] = int(id) + for twin in doubles: + if twin in accounts: + del accounts[twin] + print(f"Fertig mit Einlesen {number_accounts} found.") + except IOError: + print(f"Fehler, Datei {filename} nicht gefunden.") + return accounts + +def read_account_names(filename:str)->dict: + """ + Create a dictionary from account id to name + """ + print(f"Start Einlesen der Kontonummern und Namen \"{filename}\" ") + try: + names = {} + with open(filename, "r",encoding=config.encoding_debitoren) as file: + for line in file: + line = getWords(line) + if len(line) >=2: + id = int(line[-1]) + name = line[:-1] + names[id] = name + print(f"Fertig mit Einlesen, {len(names)} Kontonummer gefunden ") + except IOError: + print(f"Fehler, Datei {filename} nicht gefunden.") + return names + +def readPriceFamilyRooms(filename:str)->dict: + """ + read the max price per night for each room from the file + """ + print(f"Start Einlesen der Preise für die Familienzimmer von {filename}") + prices = {} + try: + with open(filename, "r") as file: + for line in file: + line = line.strip().replace("\"","").split(config.seperator_price_familyroom) + if len(line) == 2: + prices[line[0]] = int(line[1]) + print(f"Fertig mit Einlesen der Preise") + except IOError: + print(f"Fehler, Datei {filename} nicht gefunden.") + return prices + +def parse_args() -> argparse.Namespace: + """ + Defines and parses command line arguments for this script. + """ + parser = argparse.ArgumentParser() + return parser + parser.add_argument( + "bookings", + type=str, + help="the file from which the bookings come", + ) + parser.add_argument( + "miete", + type=str, + help="the file from which the bookings come", + ) + parser.add_argument( + "accounts", + type=str, + help="the file from which the account id are read", + ) + return parser.parse_args() + +def main(args: argparse.Namespace) -> None: + print(getWords("Hallo östereich-Ungarn !Achtung! ?ßchlange")) + +if __name__ == "__main__": + main(parse_args()) + +""" +Not used any more + + + +def create_forderungen_cws(filename:str, seperator:str, accounts:dict, forderungen:dict)->dict: + file = list(csv.DictReader(open(filename, 'r'))) + year = file[14]["Raum"] + for line in file: + name = line["Name"].strip().replace("-", "").replace("ß", "ss").split(" ") + id = getID(accounts, name) + id_subject = (id,"CWS") + if id > 0: + for key, value in line.items(): + if value !="X": + month = getMonth(key) + if month>0: + period = (year, month) + price = int(value) + if period not in forderungen.keys(): + forderungen[period] = {} + if id_subject not in forderungen[period]: + forderungen[period][id_subject]= Forderung() + forderungen[period][id_subject].amount = price + forderungen[period][id_subject].text = f"Miete CWS {period[0]}-{period[1]:02d}" + forderungen[period][id_subject].kostenstelle = "100" + forderungen[period][id_subject].haben = "4107" + forderungen[period][id_subject].soll = id_subject[0] + else: + forderungen[period][id_subject].amount += price + else: + print(line) + return forderungen + +""" \ No newline at end of file diff --git a/main.py b/main.py index 5381887..f136ecd 100644 --- a/main.py +++ b/main.py @@ -1,187 +1,6 @@ +import forderungen as f +import config import argparse -import csv - -class Forderung: - def __init__(self )->None: - self.name:list[str] = [] - self.buchungsdatum:str = "" - self.belegdatum:str = "" - #Buchungstext, Buchungsbetrag, Sollkonto, Habenkonto, Steuerschl�ssel, - # Kostenstelle,Kostentr�ger, Zusatzangaben - self.amount:int = 0 - self.text:str = "" - self.soll:int = 0 - self.haben:int = 0 - self.steuerschluessel:int = 0 - self.amount:int = 0 - self.kostenstelle:int = 0 - self.kostentraeger:int = 0 - self.zusatzangaben:int = 0 - - def __str__(self): - return f"Haben {self.haben}, Soll {self.soll}, Amount {self.amount}, Verwendung {self.text}" - - -def print_to_file_lexware(forderungen:dict): - """ - print Forderungen to a file, that can be read by lexware according the following format: - "Belegdatum";"Buchungsdatum";"Buchungstext"; "Buchungsbetrag"; "Sollkonto";"Habenkonto";"Kostenstelle" - 01.03.2025; 19.02.2026;2; "Miete Familienzimmer"; 357,08; 12161; 4107; 200 - """ - names = read_account_names("resources/debitoren.csv",";") - for period, forderung in forderungen.items(): - if forderung !={}: - filename = f"output/{period[0]}-{period[1]:02d}_Forderungen_Familienzimmer.csv" - with open(filename, "w") as file: - file.write("\"Belegdatum\";\"Buchungsdatum\";\"Buchungstext\";\"Buchungsbetrag\";\"Sollkonto\";\"Habenkonto\";\"Kostenstelle\"\n") - for key in forderung: - f= forderung[key] - name = names[key] - file.write(f"01.{period[1]:02d}.{period[0]};01.{period[1]:02d}.{period[0]};\"{name} {f.text}\";{f.amount};{key};{f.haben};{f.kostenstelle}\n") - -def create_forderungen_familienzimmer(filename:str, seperator:str, accounts:dict)->list: - forderungen = {} - priceFamilyRooms = readPriceFamilyRooms("resources/preise_familienzimmer.csv", ",") - with open(filename, "r") as file: - reader = csv.DictReader(file) - for row in reader: - name = row["Created by"] - name = name.replace("(", "").replace(")","").replace("ß", "ss").split(" ") - id = getID(accounts, name[1:]) - - month = getMonth(row["End time"].split(" ")[2]) - year = row["End time"].split(" ")[3] - period = (year,month) - if id > 0: - room = row["Room"].replace("\"","") - duration = getDuration(row["Duration"]) - number_person = getNumberPerson(row["Number of guests"]) - price = getPrice(room, priceFamilyRooms, duration, number_person) - print(f"{name[1]} {id} {room} dauer {duration} Tage, personen {number_person} preis {price}") - if period not in forderungen.keys(): - forderungen[period] = {} - if id not in forderungen[period]: - forderungen[period][id]= Forderung() - forderungen[period][id].amount = price - forderungen[period][id].text = f"Miete Familienzimmer {period[0]}-{period[1]:02d}" - forderungen[period][id].kostenstelle = "200" - forderungen[period][id].haben = "4107" - forderungen[period][id].soll = id - else: - forderungen[period][id].amount += price - return forderungen - -def getPrice(room:str, prices:dict, duration:int, numberPerson:int)->int: - """ - calculate the price for one booking, 15 Euro per night, 5 Euro extra per extra person, - if there is no specific number take the max of person - """ - if numberPerson > 0: - return min(prices["proPerson"]*numberPerson+prices["Basis"],prices[room])*duration - else: - return prices[room]*duration - -def readPriceFamilyRooms(filename:str, seperator:str)->dict: - prices = {} - with open(filename, "r") as file: - for line in file: - line = line.strip().replace("\"","").split(seperator) - if len(line) == 2: - prices[line[0]] = int(line[1]) - return prices - -def getNumberPerson(field:str)->int: - if len(field) >0: - return int(field) - else: - return 0 - -def getID(accounts:dict, name:list)->int: - if name[0] in accounts: - return accounts[name[0]] - elif name[1] in accounts: - return accounts[name[1]] - return 0 - -def getMonth(month:str)->int: - month = month.lower() - if month == "january" or month =="januar": - return 1 - elif month == "february" or month =="febuar": - return 2 - elif month == "march" or month =="märz": - return 3 - elif month == "april": - return 4 - elif month == "may" or month =="mai": - return 5 - elif month == "june" or month =="juni": - return 6 - elif month == "july" or month =="juli": - return 7 - elif month == "august": - return 8 - elif month == "september": - return 9 - elif month == "october" or month =="oktober": - return 10 - elif month == "november": - return 11 - elif month == "december" or month =="dezember": - return 12 - else: - return 0 - -def getDuration(duration:str)->int: - duration = duration.split(" ") - if duration[1] == "weeks" or duration[1] == "Woche": - return int(duration[0]) * 7 - elif duration[1] == "days" or duration[1] == "Tage": - return int(duration[0]) - -def read_account_ids(file_name:str, seperator:str)->dict: - """ - Create a dictionary from name to account id for first and last name couples included, - names that are not uniquw will be removed - """ - accounts = {} - doubles = [] - with open(file_name, "r",encoding="ISO-8859-1") as file: - for line in file: - line = line.strip().replace("\"", "").split(seperator) - if len(line) >=2: - id = line[1] - name = line[0].strip() - names = name.replace("-","").replace(";","").replace(",","").split(" ") - for name in names: - if name in accounts: - doubles.append(name) - elif name[0].isupper(): - accounts[name] = int(id) - for twin in doubles: - if twin in accounts: - del accounts[twin] - return accounts - -def read_account_names(file_name:str, seperator:str)->dict: - """ - Create a dictionary from account id to name - """ - names = {} - with open(file_name, "r",encoding="ISO-8859-1") as file: - for line in file: - line = line.strip().replace("\"", "").split(seperator) - if len(line) >=2: - id = int(line[1]) - name = line[0].strip() - names[id] = name - return names - -def list_to_line(data:list)->str: - line = "" - for element in data: - line +=element+"," - return line[:-1] def parse_args() -> argparse.Namespace: """ @@ -189,30 +8,61 @@ def parse_args() -> argparse.Namespace: """ parser = argparse.ArgumentParser() parser.add_argument( - "bookings", + "config", type=str, - help="the file from which the bookings come", - ) - parser.add_argument( - "accounts", - type=str, - help="the file from which the account id are read", + help="the file from which the config for the tool come", ) + return parser.parse_args() def main(args: argparse.Namespace) -> None: - accounts = read_account_ids(args.accounts, ";") - forderungen = create_forderungen_familienzimmer(args.bookings, ",", accounts) - for f in forderungen.values(): - for i in f.keys(): - print(f"{i} {f[i]}") - print_to_file_lexware(forderungen) - - - # seperator = ';' - #bookings = Booking() - #bookings.read_from_file(args.bookings, seperator) - #bookings.print_to_file(f"2025-{args.month}_Forderungen_Familienzimmer.csv", bookings[1],bookings[1:]) + print("Willkommen zum Mieten Tool der Buchhaltung. Es könne folgende Aufgaben erledigt werden:\n\n" \ + "\t1\t aus der Mietendatai eine in lexware importierbare csv Datei erstellen\n" \ + "\t2\t alle ausgelesen Accounts mit Mame und Kontonummer anzeigen\n"\ + "\t3\t aus der Buchungsdatei der Familienzimmer die Forderungen berechnen und in eine csv datei schreiben\n"\ + "\t4\t Tool verlassen\n\n" + "Gib die Nummer welche Aufgabe gemacht werden soll.\n") + while True: + forderung = {} + line = input() + if line == "4": + break + else: + #accounts + filename_debitoren = config.filename_debitoren + newFilename = input(f"Aktuell werden die Debitoren aus der Datei {filename_debitoren} gelesen, wenn du eine andere Datei einlesen möchtest gib nun den neuen Pfad an:") + if len(newFilename)>=2: + filename_debitoren = newFilename + accounts = f.read_account_ids(filename_debitoren) + if line =="1": + #print accounts + #for name, id in accounts.items(): + # print(f"{name} {id}") + names = f.read_account_names(filename_debitoren) + for id, name in names.items(): + print(f"{id} {name}") + print("Bitte gib die Nummer einer weitern Aufgabe ein:\n") + elif line == "2": + #Miete + filename_miete = config.filename_mieten + newFilename = input(f"Aktuell werden die Mietforderungen aus der Datei {filename_miete} gelesen.\n Wenn du eine andere Datei einlesen möchtest gib nun den neuen Pfad an:") + if len(newFilename)>=2: + filename_miete = newFilename + forderung = f.create_forderungen_miete(filename_miete, accounts, forderung, 5, 2025) + f.print_to_file_lexware(forderung) + print("Bitte gib die Nummer einer Weitern Aufgabe ein:\n") + elif line == "3": + #familyrooms + filename_fz = config.filename_fz + newFilename = input(f"Aktuell werden die Mietforderungen aus der Datei {filename_fz} gelesen.\n Wenn du eine andere Datei einlesen möchtest gib nun den neuen Pfad an:") + if len(newFilename)>=2: + filename_fz = newFilename + forderung = f.create_forderungen_familienzimmer(filename_fz, accounts, forderung) + f.print_to_file_lexware(forderung) + print("Bitte gib die Nummer einer Weitern Aufgabe ein:\n") + else: + print("Ungültige Eingabe, bitte versuch es erneut!\n") + print("Auf Wiedersehen!") if __name__ == "__main__": main(parse_args()) diff --git a/resources/debitoren.csv b/resources/debitoren.csv deleted file mode 100644 index 2128795..0000000 --- a/resources/debitoren.csv +++ /dev/null @@ -1,38 +0,0 @@ -"Brühl, Clara";"12020" -"Braungardt Simon, Färber Verena";"12021" -"Classen, Jona";"12030" -"Eilers Maria und Karl-Heinz";"12050" -"Fokuhl, Esther";"12060" -"Fröschen- Ludwig, Brigitte";"12061" -"Hababi Hashim und Mayyadah";"12080" -"Hellgardt Maria";"12081" -"Cuervo Hellgardt, Frida ";"12082" -"Jahnel Svende, Lohmar Martin";"12100" -"Janetzky, Birgit";"12101" -"Kniep Martin und Jana";"12110" -"Khoroshenko Olena, Fomin Andrii";"12111" -"Kottmeier, Anna Paula";"12112" -"Kiser, Gabriele";"12113" -"Lüth, Kyra";"12120" -"Laurenz Eric, Wolf Jennyfer";"12121" -"Mohamed, Youssef";"12130" -"Meinzer Niklas, Deye Christin";"12131" -"Markus, Felix";"12132" -"Niemann Christa, Heitbrink Rolf";"12140" -"Orth Astrid und Stefan";"12150" -"Oelschlaegel Hauke, Nesswetter Hannah";"12151" -"Oberhoffner, Nicolai";"12152" -"Ponomarenko Ivan und Anna";"12160" -"Röder, Brigitte";"12180" -"Reiners Nils, Peter Nina";"12181" -"Schindler Jens, Weiss Rebecca";"12190" -"Scholz, Rotraut";"12191" -"Schultheiss Achim und Brigitte";"12192" -"Taxis, Susanne";"12200" -"Von Keutz, Pia";"12220" -"Vereskun Elena und Valerii";"12221" -"Vogelsang, Leona";"12222" -"Wulff, Svenja";"12230" -"Forderungen Bauleistungsversicherung BHV";"13000" -"Forderungen an Hausverein allmende e.V";"13100" -"Prymak, Valentyna";"10001" diff --git a/resources/preise_familienzimmer.csv b/resources/preise_familienzimmer.csv deleted file mode 100644 index ac507bc..0000000 --- a/resources/preise_familienzimmer.csv +++ /dev/null @@ -1,8 +0,0 @@ -Basis, 10 -proPerson, 5 -Familienzimmer 1.OG, 25 -1. OG, 20 -2. OG, 20 -3. OG, 20 - -