From 273de80ab3956d507cde0f4cddc6b2c3fc18c295 Mon Sep 17 00:00:00 2001 From: bernd Date: Mon, 3 May 2021 22:43:37 +0200 Subject: [PATCH] =?UTF-8?q?version=20in=20python=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.txt | 6 +- rechnung.py | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 378 insertions(+), 3 deletions(-) create mode 100755 rechnung.py diff --git a/readme.txt b/readme.txt index 54130e7..1ea13d9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ -rechnung.sh ------------ +rechnung.sh / rechnung.py +------------------------- Erstellt aus einer Kundendatei Rechnungen. @@ -16,6 +16,6 @@ Installation ------------ Kopieren Sie die Dateien template.ltx, octorech.lco, rechnung.ini und -rechnung.sh in ein Verzeichnis Ihrer Wahl. Passen Sie die Dateien +rechnung.{sh|py} in ein Verzeichnis Ihrer Wahl. Passen Sie die Dateien rechnung.ini und octorech.lco an Ihre Bedürfnisse an. diff --git a/rechnung.py b/rechnung.py new file mode 100755 index 0000000..8dbe9ba --- /dev/null +++ b/rechnung.py @@ -0,0 +1,375 @@ +#!/usr/bin/python3 + +# file: rechnung.py +# date: 03.05.2021 +# user: bernd@nr18.space +# desc: Liest verschiedene Kunden und Dienstleistungen aus der Datei +# rechnung.ini und erstellt daraus Rechnungen mit Hilfe eines +# Latex-Templates. + + +import os +import sys +import locale +import subprocess +from datetime import date, timedelta + + +# Diverse Variablen +# ----------------- + + + +class DISPLAY(): + ''' + ''' + def __init__(self): + self.red = '\x1B[31m' + self.green = '\x1B[32m' + self.yellow = '\x1B[33m' + self.magenta = '\x1B[35m' + self.reset = '\x1B[39m' + self.colorify = False + self.terminal_has_color() + + def terminal_has_color(self): + try: + if os.environ["COLORTERM"] in ("256", "truecolor"): + self.colorify = True + except KeyError: + pass + + def TRUE(self): + ''' + ''' + if self.colorify == True: + print(f"{self.green}OK{self.reset}") + else: print("OK") + + def FALSE(self): + ''' + ''' + if self.colorify == True: + print(f"{self.red}Failed{self.reset}") + else: print("Failed") + + def DEKO(self, msg, end='\n'): + ''' + param 1: string + param 2: string + ''' + if self.colorify == True: + print(f"{self.magenta}{msg}{self.reset}", end=end) + else: print(f"{msg}", end=end) + + def INFO(self, msg, end='\n'): + ''' + param 1: string + param 2: string + ''' + if self.colorify == True: + print(f"{self.green}[+]{self.reset} {msg}", end=end) + else: print(f"[+] {msg}", end=end) + + def WARN(self, msg, end='\n'): + ''' + param 1: string + ''' + if self.colorify == True: + print(f"{self.yellow}[-]{self.reset} {msg}", end=end) + else: print(f"[-] {msg}", end=end) + + def ERROR(self, msg, end='\n'): + ''' + param 1: string + ''' + if self.colorify == True: + print(f"{self.red}[-]{self.reset} {msg}", end=end) + else: print(f"[-] {msg}", end=end) + + +class KUNDE(): + ''' + ''' + def __init__(self): + self.kunde = "" + self.adresse = "" + self.anrede = "" + self.anzahl = "" + self.position = "" + self.short = "" + self.betrag = "" + self.lfdnr = "" + self.error = False + + +class RECHNUNG(): + ''' + ''' + def __init__(self): + self.programm_name = "rechnung.py" + self.programm_version = "0.0.1" + self.rechnungsfile = "rechnung.ini" + self.templatefile = "template.ltx" + self.compiler = "" + + self.D = DISPLAY() + + locale.setlocale(locale.LC_TIME, "de_DE.utf8") + + + def print_header(self): + ''' + ''' + self.D.DEKO(" =============================") + self.D.DEKO(" | rechnung.py v{} |".format(self.programm_version)) + self.D.DEKO(" =============================") + + + def check_readable(self, filename): + ''' + param 1: string + ''' + self.D.INFO("Teste, ob {} ist lesbar ... ".format(filename), end="") + response = os.access(filename, os.R_OK) + if response == True: + self.D.TRUE() + else: + self.D.FALSE + sys.exit(1) + + def check_program(self, program): + ''' + param 1: string + returns: boolean + ''' + self.D.INFO(f"Teste, ob {program} installiert ist ... ", end="") + response = subprocess.run(["which", program], capture_output=True) + if response.returncode == 0: + self.D.TRUE() + self.compiler = program + self.D.INFO(f"Setze {program} als Compiler.") + return True + else: + self.D.FALSE() + return False + + def parse_buffer(self, buffer): + ''' + param 1: liste + ''' + for line in buffer: + line = line.strip() + if line == "" or line.startswith("#"): + pass + elif line.startswith("[start]"): + self.K = KUNDE() + elif line.startswith("[ende]"): + self.make_invoice() + del(self.K) + else: self.set_customer_values(line) + + def set_customer_values(self, line): + ''' + param 1: string + ''' + if line.find("=") == -1: + self.D.WARN(f"Ungültige Syntax: {line}") + return + key, value = line.split("=") + if key == "kunde": + self.K.kunde = value.strip() + elif key == "adresse": + self.K.adresse = value.strip() + elif key == "anrede": + self.K.anrede = value.strip() + elif key == "anzahl": + self.K.anzahl = value.strip() + elif key == "position": + self.K.position = value.strip() + elif key == "short": + self.K.short = value.strip() + elif key == "betrag": + self.K.betrag = value.strip() + elif key == "lfdnr": + self.K.lfdnr = value.strip() + else: + self.D.WARN(f"Fehler beim Parsen von {self.K.kunde}") + self.D.WARN(f"Ungültige Option: {key}") + self.K.error = True + + def make_invoice(self): + ''' + ''' + buffer = None + + self.D.INFO(f"Erstelle Rechung für Kunde {self.K.kunde}") + for i in self.K.__dict__: + if self.K.__dict__[i] == "": + self.D.WARN(f"Leeres Feld: {i}") + self.K.error = True + if self.K.error is True: + self.D.WARN(f"Kunde enthält einen Error. Erstelle keine Rechnung.") + return + + buffer = self.get_file_content(self.templatefile) + if buffer is None: + self.D.ERROR(f"Kann {self.templatefile} nicht lesen. Exit") + sys.exit(4) + latex = self.replace(buffer) + sys.exit(0) + + output_filename = self.build_output_filename() + if output_filename == "": + self.D.ERROR("Kann Dateinamen nicht erstellen") + sys.exit(5) + with open(output_filename, "w") as fd: + for i in latex: + fd.write(i) + fd.close() + + response = subprocess.run([self.compiler, output_filename], capture_output=True) + if response.returncode != 0: + self.D.ERROR(f"Fehler beim Compilieren für {self.K.kunde}") + else: + subprocess.run([self.compiler, output_filename], capture_output=True) + self.D.INFO(f"Rechnung für {self.K.kunde} erfolgreich erstellt.") + + def replace(self, buffer): + ''' + ''' + latex = [] + for line in buffer: + if line.find("#ANSCHRIFT#") != -1: + anschrift = self.build_anschrift() + line = line.replace("#ANSCHRIFT#", anschrift) + elif line.find("#RECHNUNGSNUMMER#") != -1: + invoice_number = self.build_invoice_number() + line = line.replace("#RECHNUNGSNUMMER#", invoice_number) + elif line.find("#SUBJECT#") != -1: + subject = self.build_subject() + line = line.replace("#SUBJECT#", subject) + elif line.find("#ANREDE#") != -1: + line = line.replace("#ANREDE#", self.K.anrede) + elif line.find("#DEADLINE#") != -1: + deadline = self.build_deadline() + line = line.replace("#DEADLINE#", deadline) + if line.find("#ANZAHL#") != -1: + line = line.replace("#ANZAHL#", self.K.anzahl) + if line.find("#POSITION#") != -1: + line = line.replace("#POSITION#", self.K.position) + if line.find("#BETRAG#") != -1: + line = line.replace("#BETRAG#", self.K.betrag) + latex.append(line) + return latex + + def build_output_filename(self): + ''' + returns: string + ''' + filename = "" + kunde = self.K.kunde.replace(" ", "_") + today = time.strftime("%Y%m%d") + month = time.strftime("%B") + filename = "_".join((today, "Rechnung", month, kunde)) + filename = ".".join((filename, "ltx")) + return filename + + def build_anschrift(self): + ''' + returns: string + ''' + anschrift = "" + anschrift = self.K.kunde.strip() + adressliste = self.K.adresse.split(";") + for i in adressliste: + anschrift = "\\\\".join((anschrift, i.strip())) + return anschrift + + def build_subject(self): + ''' + returns: string + ''' + try: + subject = " ".join(("Rechnung zu", self.K.short.strip())) + except Exception as e: + self.D.ERROR("Fehler beim Erstellen des Subjects") + self.D.ERROR(f"Error: {e}") + self.K.error = True + subject = "" + return subject + + def build_invoice_number(self): + ''' + returns: string + ''' + try: + today = date.today().strftime("%Y%m%d") + invoice_number = "-".join((today, self.K.lfdnr)) + except Exception as e: + self.D.ERROR("Fehler beim Erstellen der Rechnungsnummer") + self.D.ERROR(f"Error: {e}") + self.K.error = True + invoice_number = "" + return invoice_number + + def build_deadline(self): + ''' + returns: string + ''' + try: + delta = timedelta(days=14) + deadline = date.today() + deadline = deadline + delta + tupel = date.timetuple(deadline) + delta = timedelta(days=1) + while tupel.tm_wday in (5, 6): + deadline = deadline + delta + tupel = date.timetuple(deadline) + deadline = str(deadline.strftime("%d.%m.%Y")) + except Exception as e: + self.D.ERROR("Fehler beim Erstellen der Deadline") + self.D.ERROR(f"Error: {e}") + self.K.error = True + deadline = "" + return deadline + + def get_file_content(self, filename): + ''' + param 1: string + returns: liste + ''' + buffer = None + + with open(filename, "r") as fd: + buffer = fd.readlines() + fd.close() + return buffer + + def run(self): + ''' + ''' + + self.print_header() + + for i in (self.rechnungsfile, self.templatefile): + self.check_readable(i) + + for program in ('pdflatex', 'xelatex', 'luatex'): + if self.check_program(program) == True: + break + if self.compiler == "": + self.D.ERROR("Kann keinen Compiler finden. Exit") + sys.exit(2) + + buffer = self.get_file_content(self.rechnungsfile) + if buffer is None: + self.D.ERROR(f"Kann {self.rechnungsfile} nicht lesen. Exit") + sys.exit(3) + self.parse_buffer(buffer) + + + +if __name__ == '__main__': + r = RECHNUNG() + r.run()