mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-10 14:16:41 +03:00
275 lines
10 KiB
Makefile
Executable File
275 lines
10 KiB
Makefile
Executable File
#!/usr/bin/env just -f
|
||
# * financial reports/scripts, runnable with https://github.com/casey/just
|
||
# (like make but simpler and more suitable for running commands.)
|
||
# ** PREAMBLE ------------------------------------------------------------
|
||
# XXX we don't quote HLEDGERARGS properly, so each one must be free of spaces
|
||
|
||
just := "just -f " + justfile()
|
||
|
||
# list the commands available
|
||
@help:
|
||
{{just}} -lu --list-heading=$'{{ file_name(justfile()) }} commands:\n\
|
||
HLEDGERARGS can be added to customise reports.\n\
|
||
NOTCHOOSABLE is a required dummy argument, write - for it. Eg: just browse -\n\
|
||
'
|
||
|
||
# interactively pick a command with the default chooser. Eg: just pick -
|
||
pick NOTCHOOSABLE:
|
||
{{just}} --choose
|
||
|
||
# interactively view command outputs with fzf and bkt. Eg: just view - --black
|
||
view NOTCHOOSABLE *FZFARGS:
|
||
{{just}} --choose --chooser="fzf --reverse --preview='bkt --ttl=15m --stale=15s -- just {}' {{FZFARGS}}"
|
||
|
||
# rerun the given command with watchexec whenever local files change
|
||
watch CMD:
|
||
watchexec -- {{just}} {{CMD}}
|
||
|
||
# ** IMPORT ------------------------------------------------------------
|
||
|
||
TODAY := `date +%Y-%m-%d`
|
||
|
||
# where to import most hledger transactions from
|
||
IMPORTFILES := '\
|
||
wf-bchecking.csv.rules \
|
||
wf-pchecking.csv.rules \
|
||
wf-bsavings.csv.rules \
|
||
wf-psavings.csv.rules \
|
||
paypal.csv \
|
||
bofi-ichecking.csv.rules \
|
||
'
|
||
|
||
# download auto-downloadable CSVs (paypal)
|
||
@get-csv NOTCHOOSABLE:
|
||
paypaljson | paypaljson2csv > paypal.csv
|
||
|
||
# import new downloaded transactions to the main journal, dry run
|
||
@import-dry:
|
||
hledger import --dry-run {{IMPORTFILES}}
|
||
|
||
# import new downloaded transactions to the journal, logging and not printing errors
|
||
@import NOTCHOOSABLE:
|
||
date >>import.log
|
||
@hledger import {{IMPORTFILES}} 2>>import.log || echo "Failed, check import.log"
|
||
echo "Now use ledger-mode's M-q to align entries."
|
||
|
||
# show prices for main commodities (default: today's)
|
||
@get-prices NOTCHOOSABLE *PRICEHISTFETCHOPTS :
|
||
(pricehist fetch -o ledger -s {{TODAY}} alphavantage EUR/USD {{PRICEHISTFETCHOPTS}} | sed -E 's/EUR/€/') &
|
||
(pricehist fetch -o ledger -s {{TODAY}} alphavantage GBP/USD {{PRICEHISTFETCHOPTS}} | sed -E 's/GBP/£/') &
|
||
(pricehist fetch -o ledger -s {{TODAY}} alphavantage JPY/USD {{PRICEHISTFETCHOPTS}} | sed -E 's/JPY/¥/')
|
||
# Parallelised for speed; do slowest last.
|
||
# Output order varies, can be sorted with LC_COLLATE=C.UTF-8 sort or hledger -f- prices.
|
||
|
||
# ** REPORTS ------------------------------------------------------------
|
||
|
||
PERIOD := "1/1..tomorrow"
|
||
|
||
# show balance sheet
|
||
bs *HLEDGERARGS :
|
||
hledger bs --layout bare --pretty --drop 1 -p {{PERIOD}} -E -5 {{HLEDGERARGS}}
|
||
|
||
# show income statement
|
||
is *HLEDGERARGS :
|
||
hledger is --layout bare --pretty --drop 1 -p {{PERIOD}} -S {{HLEDGERARGS}}
|
||
|
||
# show assets
|
||
a *HLEDGERARGS :
|
||
hledger bal type:al -H --layout bare --pretty --drop 1 -p {{PERIOD}} -E {{HLEDGERARGS}}
|
||
|
||
# show revenues
|
||
r *HLEDGERARGS :
|
||
hledger bal type:r --layout bare --pretty --drop 1 -p {{PERIOD}} -S --invert {{HLEDGERARGS}}
|
||
|
||
# show expenses
|
||
x *HLEDGERARGS :
|
||
hledger bal type:x --layout bare --pretty --drop 1 -p {{PERIOD}} -S --invert {{HLEDGERARGS}}
|
||
|
||
# show assets bar chart
|
||
ab *HLEDGERARGS :
|
||
echo "Quarterly net worth:"
|
||
hledger-bar -v 200 -Q type:al -H {{HLEDGERARGS}}
|
||
|
||
# show revenues bar chart
|
||
rb *HLEDGERARGS :
|
||
echo "Quarterly revenues:"
|
||
hledger-bar -v 40 -Q type:r --invert {{HLEDGERARGS}}
|
||
|
||
# show expenses bar chart
|
||
xb *HLEDGERARGS :
|
||
echo "Quarterly expenses:"
|
||
hledger-bar -v 40 -Q type:x --invert {{HLEDGERARGS}}
|
||
|
||
# XXX with partial workaround for https://github.com/gooofy/drawilleplot/issues/4
|
||
# show assets line chart
|
||
al *HLEDGERARGS :
|
||
hledger plot -- bal --depth=1 type:a --historical --terminal --rcParams '{"figure.figsize":[8,3]}' --no-today -q --title "hledger assets" {{HLEDGERARGS}} | sed 's/⠀/ /g'
|
||
|
||
# show revenues line chart
|
||
rl *HLEDGERARGS :
|
||
hledger plot -- bal --depth=1 type:r --monthly --invert --terminal --rcParams '{"figure.figsize":[8,3]}' --drawstyle 'steps-mid' --no-today -q --title "hledger monthly revenues" {{HLEDGERARGS}} | sed 's/⠀/ /g'
|
||
|
||
# show expenses line chart
|
||
xl *HLEDGERARGS :
|
||
hledger plot -- bal --depth=1 type:x --monthly --terminal --rcParams '{"figure.figsize":[8,3]}' --drawstyle 'steps-mid' --no-today -q --title "hledger monthly expenses" {{HLEDGERARGS}} | sed 's/⠀/ /g'
|
||
|
||
# print transactions predicted by forecast rules from last week on
|
||
forecast *HLEDGERARGS :
|
||
hledger print --auto --forecast=lastweek.. -I tag:_generated {{HLEDGERARGS}}
|
||
|
||
# show a draft month-end household adjustment transaction for last month
|
||
household *HLEDGERARGS :
|
||
env household "$($date -v-1m +%b)"
|
||
|
||
# show consulting revenue
|
||
consulting *HLEDGERARGS :
|
||
hledger reg --invert 'revenues:(cw|ah)' -p {{PERIOD}} {{HLEDGERARGS}}
|
||
|
||
# estimated-tax *HLEDGERARGS :
|
||
# @echo "Federal estimated tax due for this year"
|
||
# $(HLEDGER) register liabilities:personal:tax:federal:$(YEAR) --width=130
|
||
# @echo State estimated tax due for this year:
|
||
# @$(HLEDGER) register liabilities:personal:tax:state:$(YEAR) --width=130
|
||
# @echo
|
||
|
||
# ** TIME REPORTS ------------------------------------------------------------
|
||
|
||
set export
|
||
|
||
# The file where actual time data is logged, for dashboard's stats.
|
||
# This might or might not be the top-level $TIMELOG file.
|
||
#TIMELOGDATA=$TIMELOG
|
||
YEAR := `date +%Y`
|
||
TIMELOGDATA := 'time-' + YEAR + '.timedot'
|
||
|
||
# This redisplays only when a file listed by `hledger -f $TIMELOG files` is modified.
|
||
# To force a per minute display as well, have $TIMELOG include a dummy file (.update)
|
||
# and configure a cron job to touch that every minute.
|
||
# (This is better than touching the timelog file itself, which confuses editors.)
|
||
#
|
||
# show time dashboard, redisplaying when timelog files change
|
||
tdash NOTCHOOSABLE *HLEDGERARGS:
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
dir=$(dirname "$TIMELOG")
|
||
cd "$dir"
|
||
opts= #--poll=10 # <- uncomment to fix symlinked files being ignored
|
||
watchexec $opts --no-vcs-ignore \
|
||
--filter-file=<(hledger -f "$TIMELOG" files | sed -E "s|$dir/||g") \
|
||
-c -r {{just}} tstatus {{HLEDGERARGS}}
|
||
|
||
# show time dashboard, redisplaying every minute with watch
|
||
# dash-1m *HLEDGERARGS:
|
||
# watch -n60 -c tt status
|
||
# }
|
||
|
||
# show current time status
|
||
tstatus *HLEDGERARGS:
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
date=$(if [ "$(builtin type -p gdate)" ]; then echo gdate; else echo date; fi)
|
||
stat=$(if [ "$(builtin type -p gstat)" ]; then echo gstat; else echo stat; fi)
|
||
curtime=$($date +'%H:%M %Z, %a %b %-e %Y')
|
||
modtime=$($date +'%H:%M %Z' -r "$TIMELOGDATA")
|
||
modsecs=$($stat -c %Y "$TIMELOGDATA")
|
||
nowsecs=$($date +%s)
|
||
agesecs=$((nowsecs - modsecs))
|
||
agemins=$(python3 -c "print($agesecs/60)")
|
||
agehrs=$(python3 -c "print($agesecs/3600.0)")
|
||
ageqtrhrs=$(python3 -c "print(round($agesecs/900.0))")
|
||
agedots=$({{just}} tdots "$ageqtrhrs")
|
||
printf "Current time: %s\n" "$curtime"
|
||
# old, for osh: use env here to run the system printf, which supports floating point
|
||
env printf "Timelog saved: %s, %.0fm / %.1fh / %s ago\n" "$modtime" "$agemins" "$agehrs" "$agedots"
|
||
# Show the current day/week/month budget status.
|
||
printf "Time plans:\n"
|
||
# calculate each period's budget from daily budget
|
||
hledger -f "$TIMELOG" bal -1 -p 'daily today' --budget=Daily {{HLEDGERARGS}} | tail +2
|
||
hledger -f "$TIMELOG" bal -1 -p 'weekly this week' --budget=Daily {{HLEDGERARGS}} | tail +2
|
||
hledger -f "$TIMELOG" bal -1 -p 'monthly this month' --budget=Daily {{HLEDGERARGS}} | tail +2
|
||
# or use each period's specific budget
|
||
# hledger -f "$TIMELOG" bal -p 'daily today' --budget=Daily -1 | tail +2
|
||
# hledger -f "$TIMELOG" bal -p 'weekly this week' --budget=Weekly -1 | tail +2
|
||
# hledger -f "$TIMELOG" bal -p 'monthly this month' --budget=Monthly -1 | tail +2
|
||
echo
|
||
hledger -f "$TIMELOG" check -s tags ordereddates || true
|
||
# this comes last because it's slow and variable length
|
||
echo
|
||
printf "Display activity:\n"
|
||
wakelog today | tail -n 6
|
||
|
||
# what happened ? Show largest time balances first, today and depth 1 by default
|
||
@twhat *HLEDGERARGS:
|
||
hledger -f "$TIMELOG" bal -S -1 -p today {{HLEDGERARGS}}
|
||
|
||
# print line of N dots, grouped in 4s (suitable for timedot)
|
||
tdots N:
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
n={{N}}
|
||
ndiv4=$((n/4))
|
||
nmod4=$((n-n/4*4))
|
||
sep=''
|
||
while [[ $ndiv4 -gt 0 ]]; do ndiv4=$((ndiv4-1)); echo -n "$sep...."; sep=' '; done
|
||
while [[ $nmod4 -gt 0 ]]; do nmod4=$((nmod4-1)); echo -n "$sep."; sep=''; done
|
||
echo
|
||
|
||
RFLAGS:='-tM' #TA
|
||
|
||
# horizontal time summary this year, monthly by default
|
||
@tx *HLEDGERARGS:
|
||
hledger -f "$TIMELOG" bal -1 "$RFLAGS" {{HLEDGERARGS}}
|
||
|
||
# vertical time summary this year, monthly by default
|
||
@ty *HLEDGERARGS:
|
||
hledger -f "$TIMELOG" bal -1 "$RFLAGS" --transpose {{HLEDGERARGS}}
|
||
|
||
# this and last week's time budgets
|
||
@tweeks *HLEDGERARGS:
|
||
printf "\nLast week, this week:\n"
|
||
timeweekly run
|
||
|
||
# recent past weeks' time budgets
|
||
@tweekspast *HLEDGERARGS:
|
||
printf "\nPast weeks:\n"
|
||
timeweekly past
|
||
|
||
# show a bar chart of daily hours
|
||
@thours *HLEDGERARGS:
|
||
hledger-bar -v 1 -f "$TIMELOG" -D {{HLEDGERARGS}}
|
||
|
||
# show unused / undeclared time accounts
|
||
@taccunused *HLEDGERARGS:
|
||
echo "Unused: (but declared)"
|
||
hledger -f "$TIMELOG" acc --unused {{HLEDGERARGS}} --directives | gsed -E 's/:(.)/.\1/g'
|
||
echo
|
||
echo "Undeclared: (but used)"
|
||
hledger -f "$TIMELOG" acc --undeclared {{HLEDGERARGS}} --directives | gsed -E 's/:(.)/.\1/g'
|
||
|
||
# show unused / undeclared time accounts by category
|
||
@taccunusedcat *HLEDGERARGS:
|
||
for a in $(tt acc -1); do line; echo "$a":; tt unused "^$a"; echo; done; line
|
||
|
||
# add declarations for all undeclared time accounts
|
||
@taccadd *HLEDGERARGS:
|
||
hledger -f "$TIMELOG" accounts --undeclared --directives | sed 's/:/./g' >>"$TIMELOG"
|
||
|
||
# show monthly time budget performance this year
|
||
@tbudgets *HLEDGERARGS:
|
||
{{just}} tx --budget=daily -M -p jan..tomorrow {{HLEDGERARGS}}
|
||
|
||
# show monthly time budget performance this year, vertically
|
||
@tbudgetsy *HLEDGERARGS:
|
||
{{just}} ty --budget=daily -M -p jan..tomorrow {{HLEDGERARGS}}
|
||
|
||
# dedicated weekly reports, needed to set proper week start date, to ensure simple headings:
|
||
|
||
# show weekly time budget performance this year
|
||
@tbudgetsw *HLEDGERARGS:
|
||
{{just}} ty --budget=daily -W -p 3/27..tomorrow {{HLEDGERARGS}}
|
||
|
||
# show weekly time budget performance this year, horizontally
|
||
@tbudgetswx *HLEDGERARGS:
|
||
{{just}} tx --budget=daily -W -p 3/27..tomorrow {{HLEDGERARGS}}
|
||
|