X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank2ledger;a=blobdiff_plain;f=lib%2FApp%2FHomeBank2Ledger.pm;h=cd3e0dbfd26d5b1e0dc0c8a1e6fec39f16794bfa;hp=2075bd80e2227547b41cde202b2a7b62f0f120f0;hb=2cb8cd6b61921ef8f051f9066411deb4c829b68d;hpb=acbf9960f68f612f5ba8cfaaec2fb7fda27e20e2 diff --git a/lib/App/HomeBank2Ledger.pm b/lib/App/HomeBank2Ledger.pm index 2075bd8..cd3e0db 100644 --- a/lib/App/HomeBank2Ledger.pm +++ b/lib/App/HomeBank2Ledger.pm @@ -11,7 +11,7 @@ This module is part of the L script. =cut -use warnings FATAL => 'all'; # temp fatal all +use warnings; use strict; use App::HomeBank2Ledger::Formatter; @@ -66,6 +66,10 @@ sub main { if ($opts->{manual}) { pod2usage(-exitval => 0, -verbose => 2); } + if (!$opts->{input}) { + print STDERR "Input file is required.\n"; + exit(1); + } my $homebank = File::HomeBank->new(file => $opts->{input}); @@ -119,11 +123,15 @@ sub convert_homebank_to_ledger { my $homebank = shift; my $opts = shift || {}; + my $default_account_income = 'Income:Unknown'; + my $default_account_expenses = 'Expenses:Unknown'; + my $ledger = App::HomeBank2Ledger::Ledger->new; my $transactions = $homebank->sorted_transactions; my $accounts = $homebank->accounts; my $categories = $homebank->categories; + my @budget; # determine full Ledger account names for my $account (@$accounts) { @@ -134,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 @@ -145,13 +162,18 @@ sub convert_homebank_to_ledger { $item->{excluded} = 1 if $item->{ledger_name} =~ /$re/; } } + while (my ($re, $replacement) = each %{$opts->{rename_accounts}}) { + $default_account_income =~ s/$re/$replacement/; + $default_account_expenses =~ s/$re/$replacement/; + } my $has_initial_balance = grep { $_->{initial} && !$_->{excluded} } @$accounts; if ($opts->{accounts}) { my @accounts = map { $_->{ledger_name} } grep { !$_->{excluded} } @$accounts, @$categories; - push @accounts, $opts->{default_account}; + push @accounts, $default_account_income if !grep { $_ eq $default_account_income } @accounts; + push @accounts, $default_account_expenses if !grep { $_ eq $default_account_expenses } @accounts; push @accounts, $OPENING_BALANCES_ACCOUNT if $has_initial_balance; $ledger->add_accounts(@accounts); @@ -190,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"; } @@ -221,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: @@ -242,7 +303,7 @@ sub convert_homebank_to_ledger { amount => $amount, commodity => $commodities{$account->{currency}}, payee => $payee->{name}, - memo => $memo, + note => $memo, status => $status, tags => $tags, }; @@ -271,7 +332,7 @@ sub convert_homebank_to_ledger { amount => $paired_transaction->{amount} || -$transaction->{amount}, commodity => $commodities{$dst_account->{currency}}, payee => $paired_payee->{name}, - memo => $paired_transaction->{wording} || '', + note => $paired_transaction->{wording} || '', status => $STATUS_SYMBOLS{$paired_transaction->{status} || ''} || $status, tags => _split_tags($paired_transaction->{tags}), }; @@ -282,31 +343,37 @@ sub convert_homebank_to_ledger { my @categories = split(/\|\|/, $transaction->{split_category} || ''); for (my $i = 0; $amounts[$i]; ++$i) { - my $amount = -$amounts[$i]; - my $category = $homebank->find_category_by_key($categories[$i]); - my $memo = $memos[$i] || ''; - my $other_account = $category ? $category->{ledger_name} : $opts->{default_account}; + my $amount = -$amounts[$i]; + my $category = $homebank->find_category_by_key($categories[$i]); + my $memo = $memos[$i] || ''; + my $other_account = $category ? $category->{ledger_name} + : $amount < 0 ? $default_account_income + : $default_account_expenses; push @postings, { account => $other_account, commodity => $commodities{$account->{currency}}, amount => $amount, payee => $payee->{name}, - memo => $memo, + note => $memo, status => $status, tags => $tags, }; } } else { # with or without category - my $category = $homebank->find_category_by_key($transaction->{category}); - my $other_account = $category ? $category->{ledger_name} : $opts->{default_account}; + my $amount = -$transaction->{amount}; + my $category = $homebank->find_category_by_key($transaction->{category}); + my $other_account = $category ? $category->{ledger_name} + : $amount < 0 ? $default_account_income + : $default_account_expenses; + push @postings, { account => $other_account, commodity => $commodities{$account->{currency}}, - amount => -$transaction->{amount}, + amount => $amount, payee => $payee->{name}, - memo => $memo, + note => $memo, status => $status, tags => $tags, }; @@ -375,8 +442,8 @@ sub parse_args { payees => 1, tags => 1, commodities => 1, + budget => 1, opening_date => '', - default_account => 'Expenses:No Category', rename_accounts => {}, exclude_accounts => [], ); @@ -393,17 +460,14 @@ sub parse_args { 'payees!' => \$opts{payees}, 'tags!' => \$opts{tags}, 'commodities!' => \$opts{commodities}, + 'budget!' => \$opts{budget}, 'opening-date=s' => \$opts{opening_date}, - 'default-account=s' => \$opts{default_account}, '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}; - if (!$opts{input}) { - print STDERR "Input file is required.\n"; - exit(1); - } + $opts{input} = shift @args if !$opts{input}; + $opts{budget} = 0 if lc($opts{format}) ne 'ledger'; return \%opts; }