]> Dogcows Code - chaz/homebank2ledger/blobdiff - lib/App/HomeBank2Ledger.pm
skip posting payee if it matches transaction
[chaz/homebank2ledger] / lib / App / HomeBank2Ledger.pm
index 52f426783dbbc815757b1f661953ebcd7df421a8..cd3e0dbfd26d5b1e0dc0c8a1e6fec39f16794bfa 100644 (file)
@@ -11,14 +11,7 @@ This module is part of the L<homebank2ledger> script.
 
 =cut
 
-# TODO - add posting memo
-# TODO - transaction description ("narration" in beancount)
-# TODO - payees
-# TODO - budget/scheduled
-# TODO - consolidate tags on transaction
-# TODO - consolidate payees on transaction
-
-use warnings FATAL => 'all';    # temp fatal all
+use warnings;
 use strict;
 
 use App::HomeBank2Ledger::Formatter;
@@ -73,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});
 
@@ -126,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) {
@@ -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
@@ -152,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);
@@ -197,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 earliest
-        my $first_date = $opts->{opening_date} || $transactions->[0]{date};
+        # transactions are sorted, so the first transaction is the oldest
+        $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";
         }
@@ -228,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:
@@ -249,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,
         };
@@ -278,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}),
             };
@@ -289,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        => '',  # TODO
+                note        => $memo,
                 status      => $status,
                 tags        => $tags,
             };
@@ -328,7 +388,8 @@ sub convert_homebank_to_ledger {
 
         $ledger->add_transactions({
             date        => $transaction->{date},
-            payee       => 'Payee TODO',
+            payee       => $payee->{name},
+            memo        => $memo,
             postings    => \@postings,
         });
     }
@@ -381,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    => [],
     );
@@ -399,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;
 }
This page took 0.024386 seconds and 4 git commands to generate.