qbi
ef70318e30
Neben den Backslashes am Zeilenende gab es Backslashes innerhalb des Textes. Beispiel: - Datum: 05.\,02.\,2024 - Straße: Gitstr.\,42 Mit diesen Backslashes kam die Ersetzung nicht gut zurecht. Daher habe ich das so geändert, dass jedes Semikolon (';') durch einen Backslash ersetzt wird. Im Datum und Straßen muss nun auch ein Semikolon stehen. Damit klappt die Ersetzung besser.
603 lines
14 KiB
Bash
Executable file
603 lines
14 KiB
Bash
Executable file
#!/bin/sh -
|
|
|
|
# file: rechnung.sh
|
|
# date: 02.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.
|
|
|
|
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
|
|
# -------------------
|
|
|
|
TR_BIN=$(which tr 2>/dev/null || echo "tr")
|
|
BC_BIN=$(which bc 2>/dev/null || echo "bc")
|
|
SED_BIN=$(which sed 2>/dev/null || echo "sed")
|
|
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 $KPSEWHICH_BIN"
|
|
|
|
GENPDF=""
|
|
if [ -x "$RUBBER_BIN" ]; then
|
|
GENPDF="$RUBBER_BIN -d "
|
|
else
|
|
GENPDF="$PDFLATEX_BIN -halt-on-error "
|
|
fi
|
|
|
|
|
|
# Variablen definieren
|
|
# --------------------
|
|
|
|
PROGRAMM_NAME=$(basename "$0")
|
|
PROGRAMM_VERSION="0.0.1"
|
|
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=""
|
|
KUNDE=""
|
|
ADRESSE=""
|
|
ANREDE=""
|
|
ANZAHL=""
|
|
POSITION=""
|
|
EINLEITUNG=""
|
|
SHORT=""
|
|
BETRAG=""
|
|
LFDNR=""
|
|
|
|
# Farben definieren
|
|
# -----------------
|
|
|
|
NUMBER_OF_COLORS=$($TPUT_BIN colors)
|
|
COLORED_OUTPUT=0
|
|
RED="$(tput bold)$(tput setaf 1)"
|
|
GREEN="$(tput bold)$(tput setaf 2)"
|
|
YELLOW="$(tput bold)$(tput setaf 3)"
|
|
BLUE="$(tput bold)$(tput setaf 4)"
|
|
NORMAL="$(tput bold)$(tput sgr0)"
|
|
|
|
|
|
# Ausgabefunktionen für Terminal
|
|
# ------------------------------
|
|
|
|
print_start() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
printf "%s[+]%s %s" "$GREEN" "$NORMAL" "$msg"
|
|
else
|
|
printf " Failed"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
print_failed() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
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 [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
printf "%s OK%s\n" "$GREEN" "$NORMAL"
|
|
else
|
|
printf " OK\n"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
print_deko() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
echo "${BLUE}${msg}${NORMAL}"
|
|
else
|
|
echo "$msg"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
print_info() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
echo "${GREEN}[+]$NORMAL $msg"
|
|
else
|
|
echo "[-] $msg"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
print_warn() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$VERBOSITY" -eq 0 ]; then
|
|
return
|
|
else
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
echo "${YELLOW}[-]$NORMAL $msg"
|
|
else
|
|
echo "[-] $msg"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
print_error() {
|
|
|
|
msg=$*
|
|
|
|
if [ "$COLORED_OUTPUT" -eq 1 ]; then
|
|
echo "${RED}[-]$NORMAL $msg"
|
|
else
|
|
echo "[-] $msg"
|
|
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 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
|
|
failed_and_exit 1
|
|
fi
|
|
}
|
|
|
|
check_sty() (
|
|
|
|
# sucht in einer subshell nach dem paket rechnung.sty. gibt true oder
|
|
# false zurück.
|
|
|
|
print_start "Suche nach $STY_RECHNUNG ..."
|
|
|
|
if $KPSEWHICH_BIN "$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 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
|
|
return 0
|
|
else
|
|
return 1
|
|
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 "$EINLEITUNG"; then
|
|
print_failed "Das Feld Einleitung 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
|
|
# ---------------------
|
|
|
|
trim_line() {
|
|
|
|
set -f
|
|
set -- "$*"
|
|
LINE_BUFFER=$(printf '%s' "$*")
|
|
set +f
|
|
}
|
|
|
|
trim_key() {
|
|
|
|
set -f
|
|
set -- "$*"
|
|
KEY_BUFFER=$(printf '%s' "$*")
|
|
set +f
|
|
}
|
|
|
|
trim_value() {
|
|
|
|
set -f
|
|
set -- "$*"
|
|
VALUE_BUFFER=$(printf '%s' "$*")
|
|
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 "Beginne neue Rechnung"
|
|
LINE_BUFFER=""
|
|
KEY_BUFFER=""
|
|
VALUE_BUFFER=""
|
|
KUNDE=""
|
|
ADRESSE=""
|
|
ANREDE=""
|
|
ANZAHL=""
|
|
POSITION=""
|
|
EINLEITUNG=""
|
|
SHORT=""
|
|
BETRAG=""
|
|
LFDNR=""
|
|
}
|
|
|
|
|
|
|
|
set_value() {
|
|
|
|
key=$(echo "$*" | $CUT_BIN -d "=" -f1)
|
|
value=$(echo "$*" | $CUT_BIN -d "=" -f2)
|
|
|
|
trim_key "$key"
|
|
trim_value "$value"
|
|
case $KEY_BUFFER in
|
|
kunde) KUNDE="$VALUE_BUFFER";;
|
|
adresse) ADRESSE="$VALUE_BUFFER";;
|
|
anrede) ANREDE="$VALUE_BUFFER";;
|
|
anzahl) ANZAHL="$VALUE_BUFFER";;
|
|
position) POSITION="$VALUE_BUFFER";;
|
|
einleitung) EINLEITUNG="$VALUE_BUFFER";;
|
|
betrag) BETRAG="$VALUE_BUFFER";;
|
|
lfdnr) LFDNR="$VALUE_BUFFER";;
|
|
short) SHORT="$VALUE_BUFFER";;
|
|
*) print_warn "Unbekanntes Feld: $KEY_BUFFER";;
|
|
esac
|
|
}
|
|
|
|
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"
|
|
fi
|
|
|
|
# Bei der Ersetzung unten wird jedes ';' durch genau ein '\' ersetzt. Daher ist die Anzahl der ';' wichtig.
|
|
month_ascii=$(date '+%B')
|
|
inv_date=$(date '+%Y%m%d')
|
|
inv_dead=$(date -d "+${dead_days}days" '+%d.;,%m.;,%Y')
|
|
rechnungsnummer="$inv_date--$LFDNR"
|
|
subject="Rechnung zu $SHORT"
|
|
filename="$(date '+%Y%m%d')_Rechnung_${month_ascii}_$(echo "$KUNDE" | $TR_BIN " " "_").ltx"
|
|
buffer="$KUNDE;; $ADRESSE"
|
|
#anschrift=$(echo "$buffer" | sed 's/;/\\\\/g')
|
|
|
|
|
|
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" "$(echo "$buffer" | sed 's/;/\\\\/g')"
|
|
printf "s!#RECHNUNGSNUMMER#!%s!\n" "$rechnungsnummer"
|
|
printf "s!#ANZAHL#!%s!\n" "$ANZAHL"
|
|
printf "s!#POSITION#!%s!\n" "$POSITION"
|
|
printf "s!#EINLEITUNG#!%s!\n" "$EINLEITUNG"
|
|
printf "s!#BETRAG#!%s!\n" "$BETRAG"
|
|
printf "s!#DEADLINE#!%s!\n" "$(echo "$inv_dead" | sed 's/;/\\\\/g')"
|
|
} >$SEDFILE
|
|
cp "$TEMPLATEFILE" "$filename"
|
|
$SED_BIN -i -f "$SEDFILE" "$filename"
|
|
|
|
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 $filename erfolgreich erstellt"
|
|
fi
|
|
|
|
if [ -x "$RUBBER_BIN" ]; then
|
|
"$RUBBER_BIN" --clean "$filename"
|
|
fi
|
|
)
|
|
|
|
proced_ini_line() {
|
|
|
|
line=$*
|
|
first=$(echo "$line" | cut -c1-1)
|
|
|
|
# leerzeilen ignorieren
|
|
if [ "$line" = "" ]; then
|
|
return
|
|
|
|
# kommentarzeilen ignorieren
|
|
elif [ "$first" = "#" ]; then
|
|
return
|
|
|
|
# start einer neuen rechnung
|
|
elif [ "$line" = "[start]" ]; then
|
|
reset_values
|
|
|
|
# ende einer rechnung
|
|
elif [ "$line" = "[ende]" ]; then
|
|
make_invoice
|
|
|
|
# alle anderen zeilen
|
|
else
|
|
|
|
if is_option "$line"; then
|
|
set_value "$line"
|
|
else
|
|
print_warn "Invalid syntax: $line"
|
|
fi
|
|
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"
|
|
printf "[-] Exit%s\n" "$NORMAL"
|
|
exit 1
|
|
}
|
|
|
|
# --- Programmstart --- #
|
|
# --------------------- #
|
|
|
|
# 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
|
|
|
|
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
|
|
fi
|
|
check_binaries "$EXECUTABLES_TO_CHECK"
|
|
check_readable "$RECHNUNGSFILE"
|
|
check_readable "$TEMPLATEFILE"
|
|
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
|
|
trim_line "$rawline"
|
|
proced_ini_line "$LINE_BUFFER"
|
|
done < "$RECHNUNGSFILE"
|
|
|