]> Dogcows Code - chaz/homebank2ledger/commitdiff
add option to convert budget
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Sat, 17 Aug 2019 18:57:07 +0000 (12:57 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Sat, 17 Aug 2019 18:57:07 +0000 (12:57 -0600)
bin/homebank2ledger
lib/App/HomeBank2Ledger.pm
lib/File/HomeBank.pm

index 92af63d4477e447337d229ad45cea48458a3ea90..1139928ea98c30dd8b8bdc86ef4f5a94e3903f09 100644 (file)
@@ -117,6 +117,15 @@ Enables commodity declarations.
 
 Defaults to enabled; use C<--no-commodities> to disable.
 
 
 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
 =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.
 
 =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
 * There are some minor formatting tweaks I will make (e.g. consolidate transaction tags and payees)
 
 =cut
index 022f2c310ecae417437bde7f5223bd5702c6b39e..8b29dee923bf25ace43fa4bd9250e5b156dcf004 100644 (file)
@@ -131,6 +131,7 @@ sub convert_homebank_to_ledger {
     my $transactions    = $homebank->sorted_transactions;
     my $accounts        = $homebank->accounts;
     my $categories      = $homebank->categories;
     my $transactions    = $homebank->sorted_transactions;
     my $accounts        = $homebank->accounts;
     my $categories      = $homebank->categories;
+    my @budget;
 
     # determine full Ledger account names
     for my $account (@$accounts) {
 
     # 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}";
         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
     }
 
     # handle renaming and marking excluded accounts
@@ -202,9 +212,10 @@ sub convert_homebank_to_ledger {
         $ledger->add_commodities($commodity) if $opts->{commodities};
     }
 
         $ledger->add_commodities($commodity) if $opts->{commodities};
     }
 
+    my $first_date;
     if ($has_initial_balance) {
         # transactions are sorted, so the first transaction is the oldest
     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";
         }
         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:
     my %seen;
 
     TRANSACTION:
@@ -393,6 +442,7 @@ sub parse_args {
         payees              => 1,
         tags                => 1,
         commodities         => 1,
         payees              => 1,
         tags                => 1,
         commodities         => 1,
+        budget              => 1,
         opening_date        => '',
         rename_accounts     => {},
         exclude_accounts    => [],
         opening_date        => '',
         rename_accounts     => {},
         exclude_accounts    => [],
@@ -410,12 +460,14 @@ sub parse_args {
         'payees!'               => \$opts{payees},
         'tags!'                 => \$opts{tags},
         'commodities!'          => \$opts{commodities},
         '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)]);
 
         '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;
 }
 
     return \%opts;
 }
index c786fddcf419a607d8c07f0f3eb78280a51bd93a..0034ac831edd49ecb4b7cfab9b3519e4bbe367ad 100644 (file)
@@ -575,6 +575,10 @@ sub parse_string {
                         $attr{flags}{$name} = $flags & (1 << $shift) ? 1 : 0;
                     }
 
                         $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
                     push @categories, \%attr;
                 }
                 elsif ($node eq 'ope') {    # transaction
This page took 0.028466 seconds and 4 git commands to generate.