Files
Buchhaltung-Tools/forderungen.py
2026-04-28 22:09:23 +02:00

367 lines
14 KiB
Python

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
"""