From 1290bb7c0ee4c8fa6f962712b43207a0323b18a1 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Sat, 17 Aug 2019 12:57:07 -0600 Subject: [PATCH] add option to convert budget --- bin/homebank2ledger | 11 +++++++- lib/App/HomeBank2Ledger.pm | 56 ++++++++++++++++++++++++++++++++++++-- lib/File/HomeBank.pm | 4 +++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/bin/homebank2ledger b/bin/homebank2ledger index 92af63d..1139928 100644 --- a/bin/homebank2ledger +++ b/bin/homebank2ledger @@ -117,6 +117,15 @@ Enables commodity declarations. Defaults to enabled; use C<--no-commodities> to disable. +=head2 --budget + +Enables budget transactions. + +Budget transactions are only supported by the Ledger format (for now). This option is silently +ignored otherwise. + +Defaults to enabled; use C<--no-budget> to disable. + =head2 --opening-date DATE Specify the opening date for the "opening balances" transaction. This transaction is created (if @@ -199,7 +208,7 @@ it's just plain text. =for :list * I didn't intend to make this a releasable robust product, so it's lacking tests. -* Budgets and scheduled transactions are not (yet) converted. +* Scheduled transactions are not (yet) converted. * There are some minor formatting tweaks I will make (e.g. consolidate transaction tags and payees) =cut diff --git a/lib/App/HomeBank2Ledger.pm b/lib/App/HomeBank2Ledger.pm index 022f2c3..8b29dee 100644 --- a/lib/App/HomeBank2Ledger.pm +++ b/lib/App/HomeBank2Ledger.pm @@ -131,6 +131,7 @@ sub convert_homebank_to_ledger { my $transactions = $homebank->sorted_transactions; my $accounts = $homebank->accounts; my $categories = $homebank->categories; + my @budget; # determine full Ledger account names for my $account (@$accounts) { @@ -141,6 +142,15 @@ sub convert_homebank_to_ledger { my $type = $category->{flags}{income} ? 'Income' : 'Expenses'; my $full_name = $homebank->full_category_name($category->{key}); $category->{ledger_name} = "${type}:${full_name}"; + + if ($opts->{budget} && $category->{flags}{budget}) { + for my $month_num ($category->{flags}{custom} ? (1 .. 12) : 0) { + my $amount = $category->{budget_amounts}[$month_num] || 0; + next if !$amount && !$category->{flags}{forced}; + + $budget[$month_num]{$category->{ledger_name}} = $amount; + } + } } # handle renaming and marking excluded accounts @@ -202,9 +212,10 @@ sub convert_homebank_to_ledger { $ledger->add_commodities($commodity) if $opts->{commodities}; } + my $first_date; if ($has_initial_balance) { # transactions are sorted, so the first transaction is the oldest - my $first_date = $opts->{opening_date} || $transactions->[0]{date}; + $first_date = $opts->{opening_date} || $transactions->[0]{date}; if ($first_date !~ /^\d{4}-\d{2}-\d{2}$/) { die "Opening date must be in the form YYYY-MM-DD.\n"; } @@ -233,6 +244,44 @@ sub convert_homebank_to_ledger { }); } + if ($opts->{budget}) { + my ($first_year) = $first_date =~ /^(\d{4})/; + + for my $month_num (0 .. 12) { + next if !$budget[$month_num]; + + my $payee = 'Monthly'; + if (0 < $month_num) { + my $year = $first_year; + $year += 1 if sprintf('%04d-%02d-99', $first_year, $month_num) lt $first_date; + my $date = sprintf('%04d-%02d', $year, $month_num); + $payee = "Every 12 months from ${date}"; + } + # my @MONTHS = qw(ALL Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + # $payee = "Monthly this $MONTHS[$month_num]" if 0 < $month_num; + + my @postings; + + for my $account (sort keys %{$budget[$month_num]}) { + my $amount = $budget[$month_num]{$account}; + push @postings, { + account => $account, + amount => -$amount, + commodity => $commodities{$homebank->base_currency}, + } + } + push @postings, { + account => 'Assets', + }; + + $ledger->add_transactions({ + date => '~', + payee => $payee, + postings => \@postings, + }); + } + } + my %seen; TRANSACTION: @@ -393,6 +442,7 @@ sub parse_args { payees => 1, tags => 1, commodities => 1, + budget => 1, opening_date => '', rename_accounts => {}, exclude_accounts => [], @@ -410,12 +460,14 @@ sub parse_args { 'payees!' => \$opts{payees}, 'tags!' => \$opts{tags}, 'commodities!' => \$opts{commodities}, + 'budget!' => \$opts{budget}, 'opening-date=s' => \$opts{opening_date}, 'rename-account|r=s' => \%{$opts{rename_accounts}}, 'exclude-account|x=s' => \@{$opts{exclude_accounts}}, ) or pod2usage(-exitval => 1, -verbose => 99, -sections => [qw(SYNOPSIS OPTIONS)]); - $opts{input} = shift @args if !$opts{input}; + $opts{input} = shift @args if !$opts{input}; + $opts{budget} = 0 if lc($opts{format}) ne 'ledger'; return \%opts; } diff --git a/lib/File/HomeBank.pm b/lib/File/HomeBank.pm index c786fdd..0034ac8 100644 --- a/lib/File/HomeBank.pm +++ b/lib/File/HomeBank.pm @@ -575,6 +575,10 @@ sub parse_string { $attr{flags}{$name} = $flags & (1 << $shift) ? 1 : 0; } + for my $bnum (0 .. 12) { + $attr{budget_amounts}[$bnum] = delete $attr{"b$bnum"} if $attr{"b$bnum"}; + } + push @categories, \%attr; } elsif ($node eq 'ope') { # transaction -- 2.43.0