Merge branch 'main' of gitea:berhsi/rechnung

This commit is contained in:
qbi 2021-07-28 19:26:24 +02:00
commit bb884d5e62
7 changed files with 336 additions and 83 deletions

View file

@ -1,13 +1,12 @@
TODO
----
* Bessere Fehlerkontrolle
* Ausgabe (besonders farbige) im Terminal verbessern.
* Ausgabe deaktivierbar machen.
* Parameterübergabe um einzelne Kunden zu bearbeiten.
* Parameter für den Versand der Rechnung.
* Bessere Fehlerkontrolle (besonders beim Einlesen)
* Längenprüfung
* Backticks
* Parameterübergabe für ein Ausgabeverzeichnis.
* Ansonsten vielleicht PDF verschieben
* Nicht mehr benötigte Dateien (*.log, *.aux, etc.) der Rechnung löschen.
* Den Begleittext dürch eine Variable ersetzen.
* Den Begleittext im Latex-File durch eine Variable ersetzen.
* Das Program in einem temporären Verzeichnis arbeiten lassen.

23
absender.lco Normal file
View file

@ -0,0 +1,23 @@
% eigene Variablen definieren und belegen
\newkomavar[stadt]{fromtown}
\newkomavar[adresse]{fromstreet}
\newkomavar[amtsgericht]{fromcourt}
\newkomavar[ustid]{fromustid}
\newkomavar[iban1]{fromiban1}
\newkomavar[iban2]{fromiban2}
\setkomavar{fromtown}{01234 Musterstadt}
\setkomavar{fromstreet}{Musterstr.\,1}
\setkomavar{fromcourt}{Amtsgericht Musterstadt}
\setkomavar{fromustid}{DE123456789}
\setkomavar{fromiban1}{DE01 2345 6789}
\setkomavar{fromiban2}{2345 6789 01}
\setkomavar{fromname}{Musterfirma GmbH}
\setkomavar{fromaddress}{Musterstr.\,1\\01234 Musterstadt}
\setkomavar{frommobilephone}[\faMobilePhone~]{01\,23 / 456 78 90}
\setkomavar{fromurl}{https://musterfirma.com/}
\setkomavar{fromemail}[\faEnvelopeO~]{rechnung@musterfirma.com}
\setkomavar{place}{Musterstadt}
\setkomavar{placeseparator}{, }
\setkomavar{signature}{Max Mustermann}
\setkomavar{frombank}{Musterbank AG Musterstadt}

View file

@ -1,9 +0,0 @@
\setkomavar{fromname}{OctoPi.Consulting}
\setkomavar{fromaddress}{Zitzmannstr.\,15\\07743 Jena}
\setkomavar{frommobilephone}[\faMobilePhone~]{01\,63 / 615 61 98}
\setkomavar{fromurl}{https://octopi.consulting/}
\setkomavar{fromemail}[\faEnvelopeO~]{rechnung@octopi.consulting}
\setkomavar{place}{Jena}
\setkomavar{placeseparator}{, }
\setkomavar{signature}{Jens Kubieziel}
\setkomavar{frombank}{Commerzbank AG Jena}

View file

@ -9,13 +9,40 @@ Beschreibung
Aus einer Kundendatei (rechnung.ini) werden blockweise Kundendaten gelesen
und in Variablen gespeichert. Am Ende des Blocks wird eine Kopie des
Templates (template.ltx) erstellt und die Platzhalter in der Kopie durch die
entsprechenden Variablen erstellt. Diese Kopie wird zweimal mittels pdflatex
übersetzt.
entsprechenden Variablen ersetzt. Aus dieser Kopie wird durch einen
Latexcompiler eine Rechnung im PDF-Format erstellt.
Installation
------------
Kopieren Sie die Dateien template.ltx, octorech.lco, rechnung.ini und
Kopieren Sie die Dateien template.ltx, absender.lco, rechnung.ini und
rechnung.{sh|py} in ein Verzeichnis Ihrer Wahl. Passen Sie die Dateien
rechnung.ini und octorech.lco an Ihre Bedürfnisse an.
rechnung.ini und absender.lco an Ihre Bedürfnisse an.
Optionen
--------
-h
Gibt eine kurze Hilfe aus und beendet das Programm.
-f kundendatei
Die Kundendaten werden aus 'kundendatei' gelesen.
-i
Installiert bei Bedarf das Latex-Paket rechnung.sty. Das Paket wird aus
dem Repo geklont, mittels latex gebaut und in das Verzeichnis
$HOME/texmf/latex/tex/rechnung/ kopiert.
-l lco-datei
Verwendet 'lco-datei' für verschiedene Variablen des Absenders.
-q
Es werden nur noch Fehlermeldungen ausgegeben.
-v
Zeigt die Programmversion an.

View file

@ -5,7 +5,7 @@
[start]
kunde=Firma A
adresse1= Strasse A; 012345 Stadt A;
adresse= Strasse A; 012345 Stadt A;
anrede=Sehr geehrte Damen und Herren,
anzahl=1
position=Dienstleistung A mit etwas mehr Text

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/sh -
# file: rechnung.sh
# date: 02.05.2021
@ -10,6 +10,19 @@
set -u
set -e
# ein paar Sicherheitsvorkehrungen
# --------------------------------
OLDIFS=$IFS
IFS=' '
unset -f command
unset -f unalias
\unalias -a
OLDPATH=$PATH
PATH=/bin:/usr/bin
export PATH
# Benötigte Programme
# -------------------
@ -20,13 +33,14 @@ CUT_BIN=$(which cut 2>/dev/null || echo "cut")
DATE_BIN=$(which date 2>/dev/null || echo "date")
TPUT_BIN=$(which tput 2>/dev/null || echo "tput")
PDFLATEX_BIN=$(which pdflatex 2>/dev/null || echo "pdflatex")
KPSEWHICH_BIN=$(which kpsewhich 2>/dev/null || echo "kpsewhich")
GIT_BIN=$(which git 2>/dev/null || echo "git")
LATEX_BIN=$(which latex 2>/dev/null || echo "latex")
RUBBER_BIN=$(which rubber 2>/dev/null || echo "rubber")
# Nicht auf rubber prüfen; Programm ist optional
# Nicht auf git prüfen; wird nur benötigt, wenn rechnung.sty nicht existiert.
# Nicht auf latex prüfen; wird nur benötigt, wenn rechnung.sty nicht texistiert.
EXECUTABLES_TO_CHECK="$BC_BIN $TR_BIN $SED_BIN $CUT_BIN $TPUT_BIN $DATE_BIN $PDFLATEX_BIN"
EXECUTABLES_TO_CHECK="$BC_BIN $TR_BIN $SED_BIN $CUT_BIN $TPUT_BIN $DATE_BIN $PDFLATEX_BIN $KPSEWHICH_BIN"
GENPDF=""
if [ -x "$RUBBER_BIN" ]; then
@ -39,17 +53,20 @@ fi
# Variablen definieren
# --------------------
PROGRAMM_NAME=$0
PROGRAMM_NAME=$(basename "$0")
PROGRAMM_VERSION="0.0.1"
#VERBOSITY=0
VERBOSITY=1
STY_HOME="$HOME/texmf/tex/latex"
STY_RECHNUNG="rechnung.sty"
RECHNUNGSFILE="rechnung.ini"
TEMPLATEFILE="template.ltx"
LCOFILE="absender.lco"
LCO=$(basename $LCOFILE .lco)
INSTALL_LCO_FILE=0
SEDFILE="sed.txt"
LINE_BUFFER=""
KEY_BUFFER=""
VALUE_BUFFER=""
ERROR=0
KUNDE=""
ADRESSE=""
ANREDE=""
@ -78,28 +95,45 @@ print_start() {
msg=$*
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s[+]%s %s" "$GREEN" "$NORMAL" "$msg"
if [ "$VERBOSITY" -eq 0 ]; then
return
else
printf " Failed"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s[+]%s %s" "$GREEN" "$NORMAL" "$msg"
else
printf " Failed"
fi
fi
}
print_failed() {
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s Failed%s\n" "$RED" "$NORMAL"
msg=$*
if [ "$VERBOSITY" -eq 0 ]; then
return
else
printf " Failed\n"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s Failed%s\n" "$RED" "$NORMAL"
else
printf " Failed\n"
fi
if [ -n "$msg" ]; then
print_error "$msg"
fi
fi
}
print_ok() {
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s OK%s\n" "$GREEN" "$NORMAL"
if [ "$VERBOSITY" -eq 0 ]; then
return
else
printf " OK\n"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
printf "%s OK%s\n" "$GREEN" "$NORMAL"
else
printf " OK\n"
fi
fi
}
@ -107,10 +141,14 @@ print_deko() {
msg=$*
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${BLUE}${msg}${NORMAL}"
if [ "$VERBOSITY" -eq 0 ]; then
return
else
echo "$msg"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${BLUE}${msg}${NORMAL}"
else
echo "$msg"
fi
fi
}
@ -118,10 +156,14 @@ print_info() {
msg=$*
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${GREEN}[+]$NORMAL $msg"
if [ "$VERBOSITY" -eq 0 ]; then
return
else
echo "[-] $msg"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${GREEN}[+]$NORMAL $msg"
else
echo "[-] $msg"
fi
fi
}
@ -129,10 +171,14 @@ print_warn() {
msg=$*
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${YELLOW}[-]$NORMAL $msg"
if [ "$VERBOSITY" -eq 0 ]; then
return
else
echo "[-] $msg"
if [ "$COLORED_OUTPUT" -eq 1 ]; then
echo "${YELLOW}[-]$NORMAL $msg"
else
echo "[-] $msg"
fi
fi
}
@ -147,39 +193,96 @@ print_error() {
fi
}
failed_and_exit() {
print_failed
exit "$*"
}
# Diverse Checks
# --------------
check_readable() {
# testet, ob die übergebene datei kein symlink ist, existiert und lesbar
# ist. schlägt einer der tests fehl, wird das programm beendet.
print_start "Datei $1 ist lesbar ..."
if [ -f "$1" ] && [ -r "$1" ]; then
if test -h "$1"; then
target=$(get_link_target "$1")
print_failed "$1 ist ein symbolischer Link auf $target"
exit 1
fi
if test -f "$1" && test -r "$1"; then
print_ok
else
print_failed
quit 1
failed_and_exit 1
fi
}
check_binaries() {
check_sty() (
program=""
# sucht in einer subshell nach dem paket rechnung.sty. gibt true oder
# false zurück.
print_start "Suche nach $STY_RECHNUNG ..."
if kpsewhich "$STY_RECHNUNG" > /dev/null 2>&1 ; then
print_ok
return 0
else
print_failed
return 1
fi
)
check_binaries() (
# die funktion prüft in einer subshell, ob alle benötigten binaries
# installiert sind. sollte das binary ein symlink auf eine ander datei
# sein, wird geprüft, ob sich diese im PATH befindet und nicht ebenfalls
# ein symlink ist. schlägt eine der prüfungen fehl, wird das programm
# beendet.
program=
binaries=$*
for program in $binaries; do
print_start "Suche nach $program ..."
if [ ! -x "$program" ]; then
print_failed
print_error "Das Programm $program wird benötigt, aber nicht gefunden."
if test ! -x "$program"; then
print_failed "Das Programm $program wird benötigt, aber nicht gefunden."
quit 3
else
if [ -h "$program" ]; then
program=$(get_link_target "$program")
fi
if ! which "$program" > /dev/null 2>&1 ; then
print_failed "$program ist nicht im Pfad"
fi
print_ok
fi
done
}
)
get_link_target() (
# die funktion bekommt einen symbolischen link und folgt ihm bis zum
# ende. mit dem echo-befehl wird das ziel an die aufrufende funktion
# zurück gegeben.
program=$1
while [ -h "$program" ]; do
program=$(readlink "$program")
done
echo "$program"
)
is_option() (
# die funktion überprüft in einer subshell, ob die übergebene zeile ein
# = als trennzeichen enthält. gibt ja oder falsch zurück.
line="$*"
if [ "${line#*=}" != "$line" ]; then
@ -189,6 +292,43 @@ is_option() (
fi
)
check_rechnunsdaten() (
# die funktion überprüft in einer subshell, ob alle variablen belegt
# sind. ist eine variable leer, wird der name der variablen ausgegeben
# und ein fehler (1) zurück gegeben.
print_start "Prüfe Rechnungsdaten ..."
if test -z "$KUNDE"; then
print_failed "Das Feld Kunde ist leer"
return 1
elif test -z "$ADRESSE"; then
print_failed "Das Feld Adresse ist leer"
return 1
elif test -z "$ANREDE"; then
print_failed "Das Feld Anrede ist leer"
return 1
elif test -z "$ANZAHL"; then
print_failed "Das Feld Anzahl ist leer"
return 1
elif test -z "$POSITION"; then
print_failed "Das Feld Position ist leer"
return 1
elif test -z "$SHORT"; then
print_failed "Das Feld Short ist leer"
return 1
elif test -z "$BETRAG"; then
print_failed "Das Feld Betrag ist leer"
return 1
elif test -z "$LFDNR"; then
print_failed "Das Feld LfdNr ist leer"
return 1
else
print_ok
return 0
fi
)
# Whitespaces entfernen
# in Buffer speichern
# ---------------------
@ -217,16 +357,53 @@ trim_value() {
set +f
}
# LaTeX-Paket rechnung.sty installieren
# -------------------------------------
install_sty() (
old_pwd=$(pwd)
check_binaries "$GIT_BIN $LATEX_BIN"
print_start "Hole Repo von $STY_URL ..."
if $GIT_BIN clone $STY_URL >/dev/null 2>&1; then
print_ok
cd rechnung
mkdir -p "$STY_HOME/rechnung/"
print_start "Kompiliere $STY_RECHNUNG ..."
if $LATEX_BIN rechnung.ins >/dev/null 2>&1; then
print_ok
print_start "Kopiere $STY_RECHNUNG nach $STY_HOME/rechnung ..."
if cp rechnung.sty "$STY_HOME/rechnung/"; then
print_ok
else
failed_and_exit 3
fi
cd "$old_pwd"
rm -rf rechnung/
print_info "Paket $STY_RECHNUNG erfolgreich installiert"
else
print_error "Installation fehlgeschlagen"
exit 4
fi
else
print_error "Klonen des Repos fehlgeschlagen"
exit 5
fi
)
# wenn ein neuer Kunde beginnt, alle Variablen neu initialisieren
# ---------------------------------------------------------------
reset_values() {
print_info "Initialisiere Felder für Rechnung neu."
print_info "Beginne neue Rechnung"
LINE_BUFFER=""
KEY_BUFFER=""
VALUE_BUFFER=""
ERROR=0
KUNDE=""
ADRESSE=""
ANREDE=""
@ -255,19 +432,29 @@ set_value() {
betrag) BETRAG="$VALUE_BUFFER";;
lfdnr) LFDNR="$VALUE_BUFFER";;
short) SHORT="$VALUE_BUFFER";;
*) print_warn "Unbekanntes Feld: $KEY_BUFFER"; ERROR=1;;
*) print_warn "Unbekanntes Feld: $KEY_BUFFER";;
esac
}
make_invoice() {
make_invoice() (
# die funktion erstellt in einer subshell die rechnung. schlägt die
# prüfung der rechnungsdaten fehl, kehrt sie zur aufrufenden shell
# zurück. ansonst wird eine kopie des latex-templates erstellt, die
# darin befindlichen platzhalter durch die entsprechenden variablen zu
# ersetzen und das ganze zu kompilieren.
if ! check_rechnunsdaten ; then
return 1
fi
dead_days="14"
if [ "$(date +%u)" -gt 5 ]; then
dead_days="16"
dead_days="16"
fi
month_ascii=$(date '+%B')
# inv_day=$(date '+%j')
inv_date=$(date '+%Y%m%d')
inv_dead=$(date -d "+${dead_days}days" '+%d.\\,%m.\\,%Y')
rechnungsnummer="$inv_date--$LFDNR"
@ -278,6 +465,7 @@ make_invoice() {
print_info "Erstelle Rechnung: $filename"
{
printf "s!#LCOFILE#!%s!\n" "$LCO"
printf "s!#SUBJECT#!%s!\n" "$subject"
printf "s!#ANREDE#!%s!\n" "$ANREDE"
printf "s!#ANSCHRIFT#!%s!\n" "$anschrift"
@ -286,20 +474,22 @@ make_invoice() {
printf "s!#POSITION#!%s!\n" "$POSITION"
printf "s!#BETRAG#!%s!\n" "$BETRAG"
printf "s!#DEADLINE#!%s!\n" "$inv_dead"
} > $SEDFILE
} >$SEDFILE
cp "$TEMPLATEFILE" "$filename"
$SED_BIN -i -f "$SEDFILE" "$filename"
if ! $GENPDF "$filename"; then
print_error "Rechnung fehlgeschlagen"
if ! $GENPDF "$filename" 1>&2 >/dev/null; then
print_error "Kompilierung von $filename fehlgeschlagen"
logfile=$(echo "$filename" | sed -e s/ltx/log/)
print_error "$(grep "Error" "$logfile")"
else
print_info "Rechnung erfolgreich erstellt"
print_info "Rechnung $filename erfolgreich erstellt"
fi
if [ -x "$RUBBER_BIN" ]; then
"$RUBBER_BIN" --clean "$filename"
"$RUBBER_BIN" --clean "$filename"
fi
}
)
proced_ini_line() {
@ -320,12 +510,7 @@ proced_ini_line() {
# ende einer rechnung
elif [ "$line" = "[ende]" ]; then
if [ $ERROR -ne 0 ]; then
print_warn "Kunde \"$KUNDE\" enthält einen Fehler. Erstelle keine Rechung."
return
else
make_invoice
fi
make_invoice
# alle anderen zeilen
else
@ -338,6 +523,16 @@ proced_ini_line() {
fi
}
display_help() (
echo "Usage: $PROGRAMM_NAME [-f rechnungsfile] [-h] [-l lco-file]"
)
display_version() (
echo "$PROGRAMM_NAME version $PROGRAMM_VERSION"
)
quit() {
printf "\n"
printf "%s" "$BLUE"
@ -348,7 +543,21 @@ quit() {
# --- Programmstart --- #
# --------------------- #
# Verschiedene Basic Checks
# CLI Optionen auswerten
while getopts f:hi:l:qv opt
do
case $opt in
f) RECHNUNGSFILE=$OPTARG;;
h) display_help; exit 0;;
i) INSTALL_LCO_FILE=1;;
l) LCOFILE=$OPTARG;;
q) VERBOSITY=0;;
v) display_version; exit 0;;
*) display_help; exit 1;;
esac
done
# Farbe aktivieren wenn vorhanden
if [ -n "$NUMBER_OF_COLORS" ] && [ "$NUMBER_OF_COLORS" -ge 8 ]; then
COLORED_OUTPUT=1
fi
@ -357,6 +566,7 @@ print_deko " ========================"
print_deko " = $PROGRAMM_NAME v$PROGRAMM_VERSION ="
print_deko " ========================"
# Verschiedene Basic Checks
if [ "$(id -u)" -eq 0 ]; then
print_error "$0: Das Skript darf nicht als Root ausgeführt werden."
exit 1
@ -364,13 +574,16 @@ fi
check_binaries "$EXECUTABLES_TO_CHECK"
check_readable "$RECHNUNGSFILE"
check_readable "$TEMPLATEFILE"
if ! kpsewhich $STY_RECHNUNG > /dev/null; then
print_error "$STY_RECHNUNG nicht gefunden."
exit
check_readable "$LCOFILE"
if ! check_sty; then
print_warn "Verwenden sie -i um $STY_RECHNUNG zu installieren"
if [ $INSTALL_LCO_FILE -eq 0 ]; then
exit 7
else
install_sty
fi
fi
# Kundendatei einlesen und rechnungen erstellen
while IFS= read -r rawline
do
@ -378,4 +591,3 @@ do
proced_ini_line "$LINE_BUFFER"
done < "$RECHNUNGSFILE"

View file

@ -17,7 +17,7 @@ parskip=half,%
numericaldate=off,%% Datum numerisch ausgeben
refline=narrow,%% Geschaeftszeile im Satzspiegel
firstfoot=on,%% Footerbereich
octorech]{scrlttr2}
#LCOFILE#]{scrlttr2}
\usepackage[ngerman]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
@ -35,13 +35,14 @@ octorech]{scrlttr2}
\makeatletter
\@addtoplength[-]{firstfootvpos}{.75cm} % Fuß ein Stück nach oben
\makeatother
\setkomavar{firstfoot}{\footnotesize%
\rule[3pt]{\textwidth}{.4pt} \\
\begin{tabularx}{\textwidth}{XXl}
OctoPi.Consulting & Amtsgericht Jena & \textbf{Bankverbindung}\\
Jens Kubieziel & USt.-IdNr.: & IBAN: DE50 8204 0000\\
Zitzmannstr.\,15 & DE298453137 & \hfill{}0264 3955 00\\
07743 Jena & & Commerzbank AG Jena
\usekomavar{fromname} & \usekomavar{fromcourt} & \textbf{Bankverbindung}\\
\usekomavar{signature} & USt.-IdNr.: & IBAN: \usekomavar{fromiban1}\\
\usekomavar{fromstreet} & \usekomavar{fromustid} & \hfill{}\usekomavar{fromiban2}\\
\usekomavar{fromtown} & & \usekomavar{frombank}
\end{tabularx}%
}