diff --git a/client/docker-finance.d/container/hledger/hledger.conf.in b/client/docker-finance.d/container/hledger/hledger.conf.in
new file mode 100644
index 0000000..ef3e290
--- /dev/null
+++ b/client/docker-finance.d/container/hledger/hledger.conf.in
@@ -0,0 +1,29 @@
+# docker-finance | modern accounting for the power-user
+#
+# Copyright (C) 2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# NOTE:
+# - File is treated as a plain configuration file (non-executable)
+# - Only functional for `hledger 1.34.99-gcf0c7c2ef-20240702` or later
+# - See sample: https://github.com/simonmichael/hledger/blob/master/hledger.conf.sample
+
+[balance] not:desc:balances not:equity:balances not:archive
+[print] not:desc:balances not:equity:balances not:archive
+
+[ui] assets liabilities not:desc:balances not:equity:balances not:archive
+[web] --serve --host=0.0.0.0 --base-url http://127.0.0.1:5000 --capabilities=view assets liabilities not:desc:balances not:equity:balances not:archive
+
+# vim: syn=bash sw=2 sts=2 si ai et
diff --git a/client/docker-finance.d/container/shell/subprofile.bash.in b/client/docker-finance.d/container/shell/subprofile.bash.in
index 80217b6..bd77453 100644
--- a/client/docker-finance.d/container/shell/subprofile.bash.in
+++ b/client/docker-finance.d/container/shell/subprofile.bash.in
@@ -26,6 +26,7 @@
[ -z "$DOCKER_FINANCE_CONTAINER_CMD" ] && echo "DOCKER_FINANCE_CONTAINER_CMD not set, check installation" >&2
# NOTE: ledger-based commands ending in `\`: the escape is needed so you can add more arguments as needed
+# TODO: remove default 'not:' arguments to `ledger{-ui,-web}` once distributions support hledger 1.34.99-gcf0c7c2ef-20240702 or higher (--conf support)
# CLI ledger
alias @DOCKER_FINANCE_SUBPROFILE@_ledger='$DOCKER_FINANCE_CONTAINER_CMD @DOCKER_FINANCE_PROFILE@/@DOCKER_FINANCE_SUBPROFILE@ ledger'
diff --git a/client/src/docker/lib/internal/lib_gen.bash b/client/src/docker/lib/internal/lib_gen.bash
index dc1686b..aedb759 100644
--- a/client/src/docker/lib/internal/lib_gen.bash
+++ b/client/src/docker/lib/internal/lib_gen.bash
@@ -322,7 +322,7 @@ function lib_gen::__gen_container()
lib_utils::print_debug "Generating container"
#
- # Check to proceed
+ # Check to proceed with profile-specific
#
lib_utils::print_custom " \e[32m│\e[0m\n"
@@ -377,7 +377,7 @@ function lib_gen::__gen_container()
lib_utils::print_custom " \e[32m│ │ └─\e[34m Using subprofile:\e[0m ${_subprofile}\n"
#
- # Execute
+ # Execute profile-specific
#
lib_utils::print_custom " \e[32m│ │\e[0m\n"
@@ -394,7 +394,7 @@ function lib_gen::__gen_container()
fi
lib_utils::print_custom " \e[32m│ │\e[0m\n"
- lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) container hledger-flow configs and/or accounts? [Y/n] \e[0m"
+ lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) hledger configuration file? [Y/n] \e[0m"
read -p "" _read
_confirm="${_read:-y}"
if [[ "$_confirm" == [yY] ]]; then
@@ -403,6 +403,19 @@ function lib_gen::__gen_container()
lib_utils::print_debug "_gen_path=${_gen_path}"
lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
+ lib_gen::__gen_hledger "$_gen_path" "$_gen_conf_path"
+ fi
+
+ lib_utils::print_custom " \e[32m│ │\e[0m\n"
+ lib_utils::print_custom " \e[32m│ ├─\e[34;1m Generate (or update) container hledger-flow configs and/or accounts? [Y/n] \e[0m"
+ read -p "" _read
+ _confirm="${_read:-y}"
+ if [[ "$_confirm" == [yY] ]]; then
+ local _gen_path="${DOCKER_FINANCE_CLIENT_FLOW}/profiles/${_profile}/${_subprofile}"
+ local _gen_conf_path="${_gen_path}/docker-finance.d"
+ lib_utils::print_debug "_gen_path=${_gen_path}"
+ lib_utils::print_debug "_gen_conf_path=${_gen_conf_path}"
+
lib_gen::__gen_flow "$_gen_path" "$_gen_conf_path"
fi
}
@@ -475,6 +488,53 @@ function lib_gen::__gen_shell_write()
"${global_repo_conf_dir}/container/shell/superscript.bash.in" >"$_file"
}
+#
+# Generate hledger configuration file
+#
+
+function lib_gen::__gen_hledger()
+{
+ local -r _gen_path="$1"
+ local -r _gen_conf_path="$2"
+
+ local _dir="${_gen_conf_path}/hledger"
+ [ ! -d "$_dir" ] && mkdir -p "$_dir"
+
+ local _file="${_dir}/hledger.conf"
+ if [ -f "$_file" ]; then
+ lib_utils::print_custom " \e[32m│ │ │ └─\e[34m hledger configuration found, backup then generate new one? [Y/n] \e[0m"
+ read -p "" _read
+ _confirm="${_read:-y}"
+
+ if [[ "$_confirm" == [yY] ]]; then
+ cp -a "$_file" "${_file}_${global_suffix}" || lib_utils::die_fatal
+
+ lib_gen::__gen_hledger_write "$_file"
+ local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) hledger configuration now? [Y/n] \e[0m"
+ fi
+ else
+ lib_gen::__gen_hledger_write "$_file"
+ local _print_custom=" \e[32m│ │ │ └─\e[34m Edit (new) hledger configuration now? [Y/n] \e[0m"
+ fi
+
+ lib_utils::print_custom "$_print_custom"
+ read -p "" _read
+ _confirm="${_read:-y}"
+ [[ "$_confirm" == [yY] ]] && $EDITOR "$_file"
+}
+
+function lib_gen::__gen_hledger_write()
+{
+ local _file="$1"
+ [ -z "$_file" ] && lib_utils::die_fatal
+
+ [ -z "$DOCKER_FINANCE_VERSION" ] && lib_utils::die_fatal
+ [ -z "$global_repo_conf_dir" ] && lib_utils::die_fatal
+ sed \
+ -e "s:@DOCKER_FINANCE_VERSION@:${DOCKER_FINANCE_VERSION}:g" \
+ "${global_repo_conf_dir}/container/hledger/hledger.conf.in" >"$_file"
+}
+
#
# Generate hledger-flow
#
diff --git a/container/src/finance/lib/internal/lib_edit.bash b/container/src/finance/lib/internal/lib_edit.bash
index 7027a3d..5078cda 100644
--- a/container/src/finance/lib/internal/lib_edit.bash
+++ b/container/src/finance/lib/internal/lib_edit.bash
@@ -59,7 +59,7 @@ function lib_edit::__parse_args()
Configuration type:
- type${global_arg_delim_2}
+ type${global_arg_delim_2}
Account:
@@ -70,6 +70,9 @@ function lib_edit::__parse_args()
\e[37;2m# Edit fetch configuration\e[0m
$ $global_usage type${global_arg_delim_2}fetch
+ \e[37;2m# Edit hledger configuration\e[0m
+ $ $global_usage type${global_arg_delim_2}hledger
+
\e[37;2m# Edit meta and subprofile configurations\e[0m
$ $global_usage type${global_arg_delim_2}meta${global_arg_delim_3}shell
@@ -157,7 +160,7 @@ function lib_edit::__parse_args()
read -ra _read <<<"$_arg_type"
for _type in "${_read[@]}"; do
- if [[ ! "$_type" =~ ^fetch$|^iadd$|^manual$|^meta$|^preprocess$|^rules$|^shell$ ]]; then
+ if [[ ! "$_type" =~ ^fetch$|^hledger$|^iadd$|^manual$|^meta$|^preprocess$|^rules$|^shell$ ]]; then
lib_utils::die_usage "$_usage"
fi
if [[ ! -z "$_arg_account" ]]; then
@@ -198,18 +201,45 @@ function lib_edit::__parse_args()
function lib_edit::__edit()
{
[ -z "$EDITOR" ] && lib_utils::die_fatal
- [ -z "$global_child_profile" ] && lib_utils::die_fatal
- [ -z "$global_child_profile_flow" ] && lib_utils::die_fatal
- [ -z "$global_conf_fetch" ] && lib_utils::die_fatal
- [ -z "$global_conf_meta" ] && lib_utils::die_fatal
- [ -z "$global_conf_shell" ] && lib_utils::die_fatal
for _type in "${global_arg_type[@]}"; do
case "$_type" in
fetch)
- $EDITOR "$global_conf_fetch"
+ [ -z "$global_conf_fetch" ] && lib_utils::die_fatal
+
+ local _dir
+ _dir="$(dirname $global_conf_fetch)"
+ [ ! -d "$_dir" ] && mkdir -p "$_dir"
+
+ local _file
+ _file="$(basename $global_conf_fetch)"
+
+ local _path
+ _path="${_dir}/${_file}"
+ [ ! -f "$_path" ] && touch "$_path"
+
+ $EDITOR "$_path" || lib_utils::die_fatal
+ ;;
+ hledger)
+ [ -z "$global_conf_hledger" ] && lib_utils::die_fatal
+
+ local _dir
+ _dir="$(dirname $global_conf_hledger)"
+ [ ! -d "$_dir" ] && mkdir -p "$_dir"
+
+ local _file
+ _file="$(basename $global_conf_hledger)"
+
+ local _path
+ _path="${_dir}/${_file}"
+ [ ! -f "$_path" ] && touch "$_path"
+
+ $EDITOR "$_path" || lib_utils::die_fatal
;;
iadd | manual)
+ [ -z "$global_child_profile" ] && lib_utils::die_fatal
+ [ -z "$global_child_profile_flow" ] && lib_utils::die_fatal
+
local _path="${global_child_profile_flow}/import/${global_child_profile}/_manual_/${global_arg_year}"
[ ! -d "$_path" ] && mkdir -p "$_path"
@@ -223,16 +253,29 @@ function lib_edit::__edit()
[[ "$_type" == "manual" ]] && $EDITOR "$_path"
;;
meta)
+ [ -z "$global_conf_meta" ] && lib_utils::die_fatal
+
+ local _dir
+ _dir="$(dirname $global_conf_meta)"
+ [ ! -d "$_dir" ] && mkdir -p "$_dir"
+
+ local _file
+ _file="$(basename $global_conf_meta)"
+
+ local _path
+ _path="${_dir}/${_file}"
+ [ ! -f "$_path" ] && touch "$_path"
+
# NOTE:
# - Expects comments to begin with format: //!
# - If saved to original, opening with `--skip` will clobber the
# original file's comments.
- local -r _skip="$(grep -E "^//!" $global_conf_meta | wc -l)"
- visidata --quitguard --motd-url file:///dev/null --filetype csv --skip "$_skip" "$global_conf_meta"
+ local -r _skip="$(grep -E "^//!" $_path | wc -l)"
+ visidata --quitguard --motd-url file:///dev/null --filetype csv --skip "$_skip" "$_path"
# TODO: HACK: visidata saves w/ DOS-style carriage...
# ...but there seems to be no option out of this.
- sed -i 's:\r::g' "$global_conf_meta"
+ sed -i 's:\r::g' "$_path"
;;
preprocess | rules)
# Run all paths through one editor instance
@@ -271,10 +314,23 @@ function lib_edit::__edit()
done
# Execute
- $EDITOR "${_paths[@]}"
+ $EDITOR "${_paths[@]}" || lib_utils::die_fatal
;;
shell)
- $EDITOR "$global_conf_shell"
+ [ -z "$global_conf_shell" ] && lib_utils::die_fatal
+
+ local _dir
+ _dir="$(dirname $global_conf_shell)"
+ [ ! -d "$_dir" ] && mkdir -p "$_dir"
+
+ local _file
+ _file="$(basename $global_conf_shell)"
+
+ local _path
+ _path="${_dir}/${_file}"
+ [ ! -f "$_path" ] && touch "$_path"
+
+ $EDITOR "$_path" || lib_utils::die_fatal
;;
esac
done
diff --git a/container/src/finance/lib/internal/lib_ledger.bash b/container/src/finance/lib/internal/lib_ledger.bash
index 7eaf7d9..7c68ba3 100644
--- a/container/src/finance/lib/internal/lib_ledger.bash
+++ b/container/src/finance/lib/internal/lib_ledger.bash
@@ -30,30 +30,35 @@ source "${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/lib_utils.bash
function lib_ledger::ledger-import()
{
+ lib_ledger::__ledger "$@"
lib_ledger::__ledger-import "$@"
lib_utils::catch $?
}
function lib_ledger::ledger-cli()
{
+ lib_ledger::__ledger "$@"
lib_ledger::__ledger-cli "$@"
lib_utils::catch $?
}
function lib_ledger::ledger-ui()
{
+ lib_ledger::__ledger "$@"
lib_ledger::__ledger-ui "$@"
lib_utils::catch $?
}
function lib_ledger::ledger-vui()
{
+ lib_ledger::__ledger "$@"
lib_ledger::__ledger-vui "$@"
lib_utils::catch $?
}
function lib_ledger::ledger-web()
{
+ lib_ledger::__ledger "$@"
lib_ledger::__ledger-web "$@"
lib_utils::catch $?
}
@@ -62,7 +67,38 @@ function lib_ledger::ledger-web()
# Implementation
#
-function lib_ledger::__ledger-import()
+# Constructor
+function lib_ledger::__ledger()
+{
+ # Base arguments to hledger before end-user added
+ [ -z "$global_child_profile_journal" ] && lib_utils::die_fatal
+ declare -g global_base_args=("-f" "$global_child_profile_journal")
+
+ #
+ # Apply features to given hledger version.
+ #
+ # CLI version formats:
+ #
+ # hledger 1.34, linux-x86_64
+ # hledger 1.34.99-gcf0c7c2ef-20240702, linux-x86_64
+ #
+ # TODO: hope that container platforms will package 1.35 before 1.40 rolls out
+ #
+
+ # 1.34.99 and above
+ hledger --version \
+ | gawk '{ if ($2 !~ /^1.3(4.99|([5-9][\.[0-99]?))/) { exit 1 } }' FS=' '
+
+ if [ $? -eq 0 ]; then
+ # --conf is supported
+ [ -z "$global_conf_hledger" ] && lib_utils::die_fatal
+ global_base_args+=("--conf" "$global_conf_hledger")
+ fi
+
+ lib_utils::print_debug "${global_base_args[*]}" "$@"
+}
+
+function lib_ledger::__parse_ledger-import()
{
[ -z "$global_usage" ] && lib_utils::die_fatal
[ -z "$global_arg_delim_1" ] && lib_utils::die_fatal
@@ -80,7 +116,7 @@ function lib_ledger::__ledger-import()
\e[32mArguments:\e[0m
- Fetch year:
+ Import year:
year${global_arg_delim_2}
@@ -94,13 +130,11 @@ function lib_ledger::__ledger-import()
lib_utils::die_usage "$_usage"
fi
- # Get/Set year
if [ ! -z "$_arg" ]; then
if [[ ! "$_arg" =~ ^year[s]?${global_arg_delim_2} ]]; then
lib_utils::die_usage "$_usage"
fi
- # Parse key for value
local _key="${_arg%${global_arg_delim_2}*}"
local _len="$((${#_key} + 1))"
if [[ "$_key" =~ ^year[s]?$ ]]; then
@@ -122,31 +156,44 @@ function lib_ledger::__ledger-import()
global_arg_year="$(date +%Y)"
declare -gr global_arg_year
fi
+}
- [ -z "$global_child_profile_journal" ] && lib_utils::die_fatal
- time /usr/local/bin/hledger-flow import \
- "$(dirname $global_child_profile_journal)" --start-year "$global_arg_year"
+function lib_ledger::__ledger-import()
+{
+ lib_ledger::__parse_ledger-import "$@"
+
+ time hledger-flow import \
+ "$(dirname $global_child_profile_journal)" \
+ --start-year "$global_arg_year"
}
function lib_ledger::__ledger-cli()
{
- /usr/bin/hledger -f "$global_child_profile_journal" "$@"
+ [ -z "${global_base_args[*]}" ] && lib_utils::die_fatal
+
+ hledger "${global_base_args[@]}" "$@"
}
function lib_ledger::__ledger-ui()
{
- /usr/bin/hledger-ui -f "$global_child_profile_journal" "$@"
+ [ -z "${global_base_args[*]}" ] && lib_utils::die_fatal
+
+ hledger-ui "${global_base_args[@]}" "$@"
}
function lib_ledger::__ledger-vui()
{
- /usr/bin/hledger -f "$global_child_profile_journal" print -O csv "$@" \
+ [ -z "${global_base_args[*]}" ] && lib_utils::die_fatal
+
+ hledger "${global_base_args[@]}" print -O csv "$@" \
| visidata --motd-url file:///dev/null --filetype csv
}
function lib_ledger::__ledger-web()
{
- /usr/bin/hledger-web -f "$global_child_profile_journal" "$@"
+ [ -z "${global_base_args[*]}" ] && lib_utils::die_fatal
+
+ hledger-web "${global_base_args[@]}" "$@"
}
# vim: sw=2 sts=2 si ai et
diff --git a/container/src/finance/lib/lib_finance.bash b/container/src/finance/lib/lib_finance.bash
index 022afbc..3c872b1 100644
--- a/container/src/finance/lib/lib_finance.bash
+++ b/container/src/finance/lib/lib_finance.bash
@@ -78,6 +78,7 @@ function lib_finance::finance()
# Globals (configuration)
declare -grx global_conf_fetch="${global_child_profile_flow}/docker-finance.d/fetch/fetch.yaml"
+ declare -grx global_conf_hledger="${global_child_profile_flow}/docker-finance.d/hledger/hledger.conf"
declare -grx global_conf_meta="${global_child_profile_flow}/docker-finance.d/meta/meta.csv"
declare -grx global_conf_shell="${global_child_profile_flow}/docker-finance.d/shell/subprofile.bash"