#!/bin/sh # # fio_splitter.sh # # Batch splitter of yearly account statements into monthly ones. # # ISC License # # Copyright 2023 Brmlab, z.s. # Dominik Pantůček # # Permission to use, copy, modify, and/or distribute this software # for any purpose with or without fee is hereby granted, provided # that the above copyright notice and this permission notice appear # in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Configuration defaults - none CONFIG_FILE="$HOME/.hackerbaserc" CFG_BANK_DIR= CFG_APIKEYS_FILE= ARG_BANK_DIR= ARG_APIKEYS_FILE= # Argument parsing while ! [ -z "$1" ] ; do case "$1" in -apikey) ARG_APIKEYS_FILE="$2" shift shift ;; -bankdir) ARG_BANK_DIR="$2" shift shift ;; -config) CONFIG_FILE="$2" shift shift ;; *) echo "Usage: $0 [-config file] [-apikey file] [-bankdir dir]" exit 1 ;; esac done # Configuration parsing - assumes "dumb" sed which cannot execute # multiple statements get_config_value() { if [ -z "$1" ] ; then echo "get_config_value() requires parameter name" exit 1 fi if [ -r "$CONFIG_FILE" ] ; then cat "$CONFIG_FILE" \ | sed 's/#.*//' \ | sed 's/^[ \t]*//' \ | grep "^$1" \ | sed 's/^[^ \t]*//' \ | sed 's/^[ \t]*//' \ | sed 's/[ \t]*$//' fi } CFG_BANK_DIR=`get_config_value bank-dir` CFG_APIKEYS_FILE=`get_config_value apikeys-file` # Configuration merging if [ -z "$ARG_BANK_DIR" ] ; then BANK_DIR="$CFG_BANK_DIR" else BANK_DIR="$ARG_BANK_DIR" fi if [ -z "$ARG_APIKEYS_FILE" ] ; then APIKEYS_FILE="$CFG_APIKEYS_FILE" else APIKEYS_FILE="$ARG_APIKEYS_FILE" fi # Storage for partial account statements BANK_DIR_PARTS="$BANK_DIR/parts" if ! [ -d "$BANK_DIR_PARTS" ] ; then mkdir -p "$BANK_DIR_PARTS" fi # # Very simple "logging" function (stdout should be redirected to log anyway) log() { echo `date '+%Y-%m-%d %H:%M:%S'` "$@" } # # Gets only given header # $1 - path to file # $2 - header name get_header_field() { egrep "^$2|^.$2" "$1" } # # Returns the last day of given month in Fio format end_of_month() { YEAR="$1" MONTH="$2" case $MONTH in 01) DAY=31 ;; 02) DAY=$( if [ $YEAR = 2012 -o $YEAR = 2016 -o $YEAR = 2020 ] ; then echo 29 else echo 28 fi ) ;; 03) DAY=31 ;; 04) DAY=30 ;; 05) DAY=31 ;; 06) DAY=30 ;; 07) DAY=31 ;; 08) DAY=31 ;; 09) DAY=30 ;; 10) DAY=31 ;; 11) DAY=30 ;; 12) DAY=31 ;; esac echo "$DAY.$MONTH.$YEAR" } # # Given as 1/100 of currency format_100() { AMT="$1" INT=`echo $AMT|sed 's/..$//'` FRA=`echo $AMT|sed 's/^.*\(..\)$/\1/'` echo $INT,$FRA } # # Splits given file into twelve months split_year() { # Examine name CSVNAME="$1" bname=${CSVNAME##*/} ACCNO=${bname%%-*} rest=${bname##*-} YEAR=${rest%%.*} log Splitting "$CSVNAME" year "$YEAR" account "$ACCNO" # Read header accountId=`get_header_field $CSVNAME accountId` bankId=`get_header_field $CSVNAME bankId` currency=`get_header_field $CSVNAME currency` iban=`get_header_field $CSVNAME iban` openingBalance=`get_header_field $CSVNAME openingBalance` closingBalance=`get_header_field $CSVNAME closingBalance` dateStart=`get_header_field $CSVNAME dateStart` dateEnd=`get_header_field $CSVNAME dateEnd` idFrom=`get_header_field $CSVNAME idFrom` idTo=`get_header_field $CSVNAME idTo` bic=`get_header_field $CSVNAME bic` # Special handling of balances - in 1/100 of currency currentBalanceTmp=${openingBalance#*;} currentBalanceStr=`echo $currentBalanceTmp | sed 's/,//'` currentBalance=`expr $currentBalanceStr + 0` # Emit months for month in `seq 1 12` ; do MONTH=$month if [ $MONTH -lt 10 ] ; then MONTH=0$MONTH fi MCSVNAME="$BANK_DIR_PARTS/$ACCNO-$YEAR-$MONTH.csv" log $MCSVNAME $currentBalance { echo "$accountId" echo "$bankId" echo "$currency" echo "$iban" if ! [ -z "$bic" ] ; then echo "$bic" fi OPENINGBALANCE=`format_100 $currentBalance` echo "openingBalance;$OPENINGBALANCE" for tr in `grep "^[0-9]*;...$MONTH.$YEAR;" "$CSVNAME" | cut -f3 -d';'|sed 's/,//'` ; do currentBalance=`expr $currentBalance + $tr` done CLOSINGBALANCE=`format_100 $currentBalance` echo "closingBalance;$CLOSINGBALANCE" # echo "$dateStart" echo "dateStart;01.$MONTH.$YEAR" #echo "$dateEnd" DATEEND=`end_of_month $YEAR $MONTH` echo "dateEnd;$DATEEND" if [ -z $idFrom ] ; then # Empty year echo grep "^ID" "$CSVNAME" else # Non-empty year, maybe non-empty month numrec=`grep -c "^[0-9]*;...$MONTH.$YEAR;" "$CSVNAME"` if [ $numrec = 0 ] ; then # Empty month echo grep "^ID" "$CSVNAME" else # Non-empty month IDFROM=`grep "^[0-9]*;...$MONTH.$YEAR;" "$CSVNAME"|head -1|sed 's/;.*//'` IDTO=`grep "^[0-9]*;...$MONTH.$YEAR;" "$CSVNAME"|sort -gr -t';'|head -1|sed 's/;.*//'` echo "idFrom;$IDFROM" echo "idTo;$IDTO" echo grep "^ID" "$CSVNAME" grep "^[0-9]*;...$MONTH.$YEAR;" "$CSVNAME" fi fi } >"$MCSVNAME" done } # Current year (last in seq) CYEAR=`date +%Y` # Process all known accounts log Started while read accnt ; do # Extract account number and starting year ACCNO=${accnt%% *} accrest=${accnt#* } accrest2=${accrest#* } YEAR=${accrest2%% *} # Process the account log ==== ACCNO=$ACCNO YEAR=$YEAR # Iterate over years for year in `seq $YEAR $CYEAR` ; do CSVNAME="$BANK_DIR_PARTS/$ACCNO-$year.csv" split_year $CSVNAME done done < "$APIKEYS_FILE"