#!/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 # 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") 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" 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=0 STY_HOME="$HOME/texmf/tex/latex" STY_RECHNUNG="rechnung.sty" STY_URL="https://github.com/tomka/rechnung" RECHNUNGSFILE="rechnung.ini" TEMPLATEFILE="template.ltx" LCOFILE="absender.lco" LCO=$(basename $LCOFILE .lco) SEDFILE="sed.txt" LINE_BUFFER="" KEY_BUFFER="" VALUE_BUFFER="" ERROR=0 KUNDE="" ADRESSE="" ANREDE="" ANZAHL="" POSITION="" 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 [ "$COLORED_OUTPUT" -eq 1 ]; then printf "%s[+]%s %s" "$GREEN" "$NORMAL" "$msg" else printf " Failed" fi } print_failed() { if [ "$COLORED_OUTPUT" -eq 1 ]; then printf "%s Failed%s\n" "$RED" "$NORMAL" else printf " Failed\n" fi } print_ok() { if [ "$COLORED_OUTPUT" -eq 1 ]; then printf "%s OK%s\n" "$GREEN" "$NORMAL" else printf " OK\n" fi } print_deko() { msg=$* if [ "$COLORED_OUTPUT" -eq 1 ]; then echo "${BLUE}${msg}${NORMAL}" else echo "$msg" fi } print_info() { msg=$* if [ "$COLORED_OUTPUT" -eq 1 ]; then echo "${GREEN}[+]$NORMAL $msg" else echo "[-] $msg" fi } print_warn() { msg=$* if [ "$COLORED_OUTPUT" -eq 1 ]; then echo "${YELLOW}[-]$NORMAL $msg" else echo "[-] $msg" fi } print_error() { msg=$* if [ "$COLORED_OUTPUT" -eq 1 ]; then echo "${RED}[-]$NORMAL $msg" else echo "[-] $msg" fi } # Diverse Checks # -------------- check_readable() { print_start "Datei $1 ist lesbar ..." if test -f "$1" && test -r "$1"; then print_ok else print_failed quit 1 fi } check_sty() ( print_start "Suche nach $STY_RECHNUNG ..." rechnung=$(find "$STY_HOME" -type f -name "$STY_RECHNUNG") if test ! -f "$rechnung"; then print_failed return 1 else print_ok return 0 fi ) check_binaries() { program="" binaries=$* for program in $binaries; do print_start "Suche nach $program ..." if test ! -x "$program"; then print_failed print_error "Das Programm $program wird benötigt, aber nicht gefunden." quit 3 else print_ok fi done } is_option() ( line="$*" if [ "${line#*=}" != "$line" ]; then return 0 else return 1 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) printf " LaTeX Paket '%s' installieren? (y/N): " $STY_RECHNUNG; read -r REPLY if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ] || [ "$REPLY" = "j" ] || [ "$REPLY" = "J" ] then check_binaries "$GIT_BIN $LATEX_BIN" print_start "Hole Repo von $STY_URL ..." if git 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 print_failed exit 1 fi cd "$old_pwd" rm -rf rechnung/ print_info "Paket $STY_RECHNUNG erfolgreich installiert" else print_error "Installation fehlgeschlagen" exit 5 fi else print_error "Klonen des Repos fehlgeschlagen" exit 6 fi else print_info "Beende Progrann" exit 7 fi ) # wenn ein neuer Kunde beginnt, alle Variablen neu initialisieren # --------------------------------------------------------------- reset_values() { print_info "Initialisiere Felder für Rechnung neu." LINE_BUFFER="" KEY_BUFFER="" VALUE_BUFFER="" ERROR=0 KUNDE="" ADRESSE="" ANREDE="" ANZAHL="" POSITION="" SHORT="" BETRAG="" LFDNR="" } set_value() { key=$(echo "$*" | $CUT_BIN -d "=" -f1) value=$(echo "$*" | $CUT_BIN -d "=" -f2) trim_key "$key" trim_value "$value" print_info "Set $key to $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";; betrag) BETRAG="$VALUE_BUFFER";; lfdnr) LFDNR="$VALUE_BUFFER";; short) SHORT="$VALUE_BUFFER";; *) print_warn "Unbekanntes Feld: $KEY_BUFFER"; ERROR=1;; esac } make_invoice() { dead_days="14" if [ "$(date +%u)" -gt 5 ]; then 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" 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" "$anschrift" printf "s!#RECHNUNGSNUMMER#!%s!\n" "$rechnungsnummer" printf "s!#ANZAHL#!%s!\n" "$ANZAHL" printf "s!#POSITION#!%s!\n" "$POSITION" printf "s!#BETRAG#!%s!\n" "$BETRAG" printf "s!#DEADLINE#!%s!\n" "$inv_dead" } >$SEDFILE cp "$TEMPLATEFILE" "$filename" $SED_BIN -i -f "$SEDFILE" "$filename" if ! $GENPDF "$filename"; then print_error "Rechnung fehlgeschlagen" else print_info "Rechnung 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 if [ $ERROR -ne 0 ]; then print_warn "Kunde \"$KUNDE\" enthält einen Fehler. Erstelle keine Rechung." return else make_invoice fi # alle anderen zeilen else if is_option "$line"; then set_value "$line" else print_warn "Invalid syntax: $line" fi fi } display_help() ( echo "Usage: rechnung.sh [-f rechnungsfile] [-h] [-l lco-file]" ) quit() { printf "\n" printf "%s" "$BLUE" printf "[-] Exit%s\n" "$NORMAL" exit 1 } # --- Programmstart --- # # --------------------- # # CLI Optionen auswerten while getopts f:hl: opt do case $opt in f) RECHNUNGSFILE=$OPTARG;; h) display_help; exit 0;; l) LCOFILE=$OPTARG;; *) display_help; exit 1;; esac done # Verschiedene Basic Checks 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 " ========================" 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 "$STY_HOME"; then print_warn "$STY_RECHNUNG nicht gefunden." install_sty fi # Kundendatei einlesen und rechnungen erstellen while IFS= read -r rawline do trim_line "$rawline" proced_ini_line "$LINE_BUFFER" done < "$RECHNUNGSFILE"