Version 0.005
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Sat, 17 Aug 2019 22:26:56 +0000 (16:26 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Sat, 17 Aug 2019 22:26:56 +0000 (16:26 -0600)
14 files changed:
Changes
META.json
META.yml
Makefile.PL
README
bin/homebank2ledger
lib/App/HomeBank2Ledger.pm
lib/App/HomeBank2Ledger/Formatter.pm
lib/App/HomeBank2Ledger/Formatter/Beancount.pm
lib/App/HomeBank2Ledger/Formatter/Ledger.pm
lib/App/HomeBank2Ledger/Ledger.pm
lib/App/HomeBank2Ledger/Util.pm
lib/File/HomeBank.pm
t/00-report-prereqs.dd

diff --git a/Changes b/Changes
index 24f288fbfaf9774be3626f8de8c1abfa2f68f142..3a74157d2e44700b5e321ab4170e665b071aa7d4 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,13 @@
 Revision history for App-HomeBank2Ledger.
 
+0.005     2019-08-17 16:26:33-06:00 MST7MDT
+  * Add --budget option for converting HomeBank budget to Ledger.
+  * Support quoting commodities when needed.
+  * The formatter now adds posting notes when they differ from the transaction
+    memo and avoids printing posting payees if not different from the
+    transaction payee.
+  * Fix bug which could add duplicate ":Unknown" accounts.
+
 0.004     2019-06-17 23:28:10-06:00 MST7MDT
   * Remove --default-account option. Instead the default account(s) can be
     customized using the --rename-account option, like this:
index 277ec80be6548e33ddf5f1ef0eca713da3f67b94..15836eefe093d305de4cbc8ce90d407ea5193670 100644 (file)
--- a/META.json
+++ b/META.json
@@ -63,6 +63,7 @@
             "XML::Entities" : "0",
             "XML::Parser::Lite" : "0",
             "parent" : "0",
+            "perl" : "v5.10.1",
             "strict" : "0",
             "warnings" : "0"
          }
             "File::Spec" : "0",
             "IO::Handle" : "0",
             "IPC::Open3" : "0",
-            "Test::More" : "0",
-            "perl" : "5.006"
+            "Test::More" : "0"
          }
       }
    },
    "provides" : {
       "App::HomeBank2Ledger" : {
          "file" : "lib/App/HomeBank2Ledger.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "App::HomeBank2Ledger::Formatter" : {
          "file" : "lib/App/HomeBank2Ledger/Formatter.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "App::HomeBank2Ledger::Formatter::Beancount" : {
          "file" : "lib/App/HomeBank2Ledger/Formatter/Beancount.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "App::HomeBank2Ledger::Formatter::Ledger" : {
          "file" : "lib/App/HomeBank2Ledger/Formatter/Ledger.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "App::HomeBank2Ledger::Ledger" : {
          "file" : "lib/App/HomeBank2Ledger/Ledger.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "App::HomeBank2Ledger::Util" : {
          "file" : "lib/App/HomeBank2Ledger/Util.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       },
       "File::HomeBank" : {
          "file" : "lib/File/HomeBank.pm",
-         "version" : "0.004"
+         "version" : "0.005"
       }
    },
    "release_status" : "stable",
          "web" : "https://github.com/chazmcgarvey/homebank2ledger"
       }
    },
-   "version" : "0.004",
+   "version" : "0.005",
    "x_authority" : "cpan:CCM",
    "x_generated_by_perl" : "v5.28.0",
    "x_serialization_backend" : "Cpanel::JSON::XS version 4.08"
index 863d993e2d6c987f53112f98357ab4643ac15091..cc85c06c163c0531964f7085bd5a90754d0c5c95 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -8,7 +8,6 @@ build_requires:
   IO::Handle: '0'
   IPC::Open3: '0'
   Test::More: '0'
-  perl: '5.006'
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 0
@@ -28,25 +27,25 @@ no_index:
 provides:
   App::HomeBank2Ledger:
     file: lib/App/HomeBank2Ledger.pm
-    version: '0.004'
+    version: '0.005'
   App::HomeBank2Ledger::Formatter:
     file: lib/App/HomeBank2Ledger/Formatter.pm
-    version: '0.004'
+    version: '0.005'
   App::HomeBank2Ledger::Formatter::Beancount:
     file: lib/App/HomeBank2Ledger/Formatter/Beancount.pm
-    version: '0.004'
+    version: '0.005'
   App::HomeBank2Ledger::Formatter::Ledger:
     file: lib/App/HomeBank2Ledger/Formatter/Ledger.pm
-    version: '0.004'
+    version: '0.005'
   App::HomeBank2Ledger::Ledger:
     file: lib/App/HomeBank2Ledger/Ledger.pm
-    version: '0.004'
+    version: '0.005'
   App::HomeBank2Ledger::Util:
     file: lib/App/HomeBank2Ledger/Util.pm
-    version: '0.004'
+    version: '0.005'
   File::HomeBank:
     file: lib/File/HomeBank.pm
-    version: '0.004'
+    version: '0.005'
 requires:
   Carp: '0'
   Exporter: '0'
@@ -59,13 +58,14 @@ requires:
   XML::Entities: '0'
   XML::Parser::Lite: '0'
   parent: '0'
+  perl: v5.10.1
   strict: '0'
   warnings: '0'
 resources:
   bugtracker: https://github.com/chazmcgarvey/homebank2ledger/issues
   homepage: https://github.com/chazmcgarvey/homebank2ledger
   repository: https://github.com/chazmcgarvey/homebank2ledger.git
-version: '0.004'
+version: '0.005'
 x_authority: cpan:CCM
 x_generated_by_perl: v5.28.0
 x_serialization_backend: 'YAML::Tiny version 1.73'
index 115a98f5bc92cef942bf9c5500318f3687aa54e8..83b1aa4b389c5860a1e32cbd0c943968707c41d1 100644 (file)
@@ -2,7 +2,7 @@
 use strict;
 use warnings;
 
-use 5.006;
+use 5.010001;
 
 use ExtUtils::MakeMaker;
 
@@ -17,7 +17,7 @@ my %WriteMakefileArgs = (
     "bin/homebank2ledger"
   ],
   "LICENSE" => "mit",
-  "MIN_PERL_VERSION" => "5.006",
+  "MIN_PERL_VERSION" => "5.010001",
   "NAME" => "App::HomeBank2Ledger",
   "PREREQ_PM" => {
     "Carp" => 0,
@@ -41,7 +41,7 @@ my %WriteMakefileArgs = (
     "IPC::Open3" => 0,
     "Test::More" => 0
   },
-  "VERSION" => "0.004",
+  "VERSION" => "0.005",
   "test" => {
     "TESTS" => "t/*.t"
   }
diff --git a/README b/README
index 8ee36730b196ad08f0a75eba0f98fc1a8594d834..220932963ff1569b29cd1e8ec123acc6b4a8059a 100644 (file)
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ NAME
 
 VERSION
 
-    version 0.004
+    version 0.005
 
 SYNOPSIS
 
@@ -131,6 +131,15 @@ OPTIONS
 
     Defaults to enabled; use --no-commodities to disable.
 
+ --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 --no-budget to disable.
+
  --opening-date DATE
 
     Specify the opening date for the "opening balances" transaction. This
@@ -224,7 +233,7 @@ CAVEATS
       * 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)
index bc5b00754e4c4c16cc14fddf3b3b90df9d5142d4..933243fceaac13ad6f816d9c2668ffdede530d6c 100644 (file)
@@ -8,7 +8,7 @@ use strict;
 
 use App::HomeBank2Ledger;
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 App::HomeBank2Ledger->main(@ARGV);
 
@@ -24,7 +24,7 @@ homebank2ledger - A tool to convert HomeBank files to Ledger format
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 SYNOPSIS
 
@@ -160,6 +160,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
@@ -265,7 +274,7 @@ I didn't intend to make this a releasable robust product, so it's lacking tests.
 
 =item *
 
-Budgets and scheduled transactions are not (yet) converted.
+Scheduled transactions are not (yet) converted.
 
 =item *
 
index 5a61f7d5369df135de452f8c5a44ab49876164b9..9d6ce603793ba95a58bddc6b08de47b6c97f012e 100644 (file)
@@ -2,7 +2,7 @@ package App::HomeBank2Ledger;
 # ABSTRACT: A tool to convert HomeBank files to Ledger format
 
 
-use warnings FATAL => 'all';    # temp fatal all
+use warnings;
 use strict;
 
 use App::HomeBank2Ledger::Formatter;
@@ -11,7 +11,7 @@ use File::HomeBank;
 use Getopt::Long 2.38 qw(GetOptionsFromArray);
 use Pod::Usage;
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 my %ACCOUNT_TYPES = (   # map HomeBank account types to Ledger accounts
     bank        => 'Assets:Bank',
@@ -101,6 +101,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) {
@@ -111,6 +112,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
@@ -132,7 +142,8 @@ sub convert_homebank_to_ledger {
     if ($opts->{accounts}) {
         my @accounts = map { $_->{ledger_name} } grep { !$_->{excluded} } @$accounts, @$categories;
 
-        push @accounts, $default_account_income, $default_account_expenses;
+        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);
@@ -171,9 +182,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";
         }
@@ -202,6 +214,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:
@@ -223,7 +273,7 @@ sub convert_homebank_to_ledger {
             amount      => $amount,
             commodity   => $commodities{$account->{currency}},
             payee       => $payee->{name},
-            memo        => $memo,
+            note        => $memo,
             status      => $status,
             tags        => $tags,
         };
@@ -252,7 +302,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}),
             };
@@ -275,7 +325,7 @@ sub convert_homebank_to_ledger {
                     commodity   => $commodities{$account->{currency}},
                     amount      => $amount,
                     payee       => $payee->{name},
-                    memo        => $memo,
+                    note        => $memo,
                     status      => $status,
                     tags        => $tags,
                 };
@@ -293,7 +343,7 @@ sub convert_homebank_to_ledger {
                 commodity   => $commodities{$account->{currency}},
                 amount      => $amount,
                 payee       => $payee->{name},
-                memo        => $memo,
+                note        => $memo,
                 status      => $status,
                 tags        => $tags,
             };
@@ -347,6 +397,7 @@ sub parse_args {
         payees              => 1,
         tags                => 1,
         commodities         => 1,
+        budget              => 1,
         opening_date        => '',
         rename_accounts     => {},
         exclude_accounts    => [],
@@ -364,12 +415,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;
 }
@@ -393,7 +446,7 @@ App::HomeBank2Ledger - A tool to convert HomeBank files to Ledger format
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 SYNOPSIS
 
index 94ee0eccecd5b9c2d1b26491f52252311f3b5c49..f5299436a9f55525cd1681f400a47ad4596c55a2 100644 (file)
@@ -9,7 +9,7 @@ use Module::Load;
 use Module::Pluggable search_path   => [__PACKAGE__],
                       sub_name      => 'available_formatters';
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 sub _croak { require Carp; Carp::croak(@_) }
 
@@ -59,7 +59,7 @@ App::HomeBank2Ledger::Formatter - Abstract class for formatting a ledger
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 SYNOPSIS
 
index 73fd7a248bfe69af3118ab40e3a392dfc5b07abf..2ec6a2c89437bc4b40b39909cce98356ecc12906 100644 (file)
@@ -2,6 +2,7 @@ package App::HomeBank2Ledger::Formatter::Beancount;
 # ABSTRACT: Beancount formatter
 
 
+use v5.10.1;    # defined-or
 use warnings;
 use strict;
 
@@ -9,7 +10,7 @@ use App::HomeBank2Ledger::Util qw(commify rtrim);
 
 use parent 'App::HomeBank2Ledger::Formatter';
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 my %STATUS_SYMBOLS = (
     cleared => '*',
@@ -24,18 +25,19 @@ sub format {
     my $ledger = shift;
 
     my @out = (
-        $self->_format_header,
-        $self->_format_accounts($ledger),
-        $self->_format_commodities($ledger),
-        # $self->_format_payees,
-        # $self->_format_tags,
-        $self->_format_transactions($ledger),
+        $self->format_header,
+        $self->format_accounts($ledger),
+        $self->format_commodities($ledger),
+        # $self->format_payees,
+        # $self->format_tags,
+        $self->format_transactions($ledger),
     );
 
     return join($/, map { rtrim($_) } @out);
 }
 
-sub _format_header {
+
+sub format_header {
     my $self = shift;
 
     my @out;
@@ -43,16 +45,17 @@ sub _format_header {
     if (my $name = $self->name) {
         push @out, "; Name: $name";
     }
-
-    my $file = $self->file;
-    push @out, "; Converted from ${file} using homebank2ledger ${VERSION}";
+    if (my $file = $self->file) {
+        push @out, "; File: $file";
+    }
 
     push @out, '';
 
     return @out;
 }
 
-sub _format_accounts {
+
+sub format_accounts {
     my $self   = shift;
     my $ledger = shift;
 
@@ -70,7 +73,8 @@ sub _format_accounts {
     return @out;
 }
 
-sub _format_commodities {
+
+sub format_commodities {
     my $self   = shift;
     my $ledger = shift;
 
@@ -89,7 +93,8 @@ sub _format_commodities {
     return @out;
 }
 
-sub _format_transactions {
+
+sub format_transactions {
     my $self   = shift;
     my $ledger = shift;
 
@@ -150,7 +155,27 @@ sub _format_transaction {
         push @line, ($posting_status_symbol ? "  $posting_status_symbol " : '    ');
         push @line, sprintf("\%-${account_width}s", $account);
         push @line, '  ';
-        push @line, $self->_format_amount($posting->{amount}, $posting->{commodity}) if defined $posting->{amount};
+        if (defined $posting->{amount}) {
+            push @line, $self->_format_amount($posting->{amount}, $posting->{commodity});
+            my $lot_price = $posting->{lot_price};
+            my $lot_date  = $posting->{lot_date};
+            my $lot_ref   = $posting->{lot_ref};
+            if ($lot_price || $lot_date || $lot_ref) {
+                push @line, ' {',
+                            join(', ',
+                                $lot_price ? $self->_format_amount($lot_price->{amount}, $lot_price->{commodity}) : (),
+                                $lot_date  ? $lot_date : (),
+                                $lot_ref   ? $self->_format_string($lot_ref) : (),
+                            ),
+                            '}';
+            }
+            if (my $cost = $posting->{total_cost} // $posting->{cost}) {
+                my $is_total = defined $posting->{total_cost};
+                my $cost_symbol = $is_total ? '@@' : '@';
+                push @line, ' ', $cost_symbol, ' ',
+                            $self->_format_amount($cost->{amount}, $cost->{commodity});
+            }
+        }
 
         push @out, join('', @line);
     }
@@ -257,12 +282,56 @@ App::HomeBank2Ledger::Formatter::Beancount - Beancount formatter
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 DESCRIPTION
 
 This is a formatter for L<Beancount|http://furius.ca/beancount/>.
 
+=head1 METHODS
+
+=head2 format_header
+
+    @lines = $formatter->format_header;
+
+Get formatted header. For example,
+
+    ; Name: My Finances
+    ; File: path/to/finances.xhb
+
+=head2 format_accounts
+
+    @lines = $formatter->format_accounts($ledger);
+
+Get formatted accounts. For example,
+
+    2003-02-14 open Assets:Bank:Credit-Union:Savings
+    2003-02-14 open Assets:Bank:Credit-Union:Checking
+    ...
+
+=head2 format_commodities
+
+    @lines = $formatter->format_commodities($ledger);
+
+Get formattted commodities. For example,
+
+    2003-02-14 commodity USD
+        name: "US Dollar"
+    ...
+
+=head2 format_transactions
+
+    @lines = $formatter->format_transactions($ledger);
+
+Get formatted transactions. For example,
+
+    2003-02-14 * "Opening Balance"
+        Assets:Bank:Credit-Union:Savings           458.21 USD
+        Assets:Bank:Credit-Union:Checking          194.17 USD
+        Equity:Opening-Balances
+
+    ...
+
 =head1 SEE ALSO
 
 L<App::HomeBank2Ledger::Formatter>
index 8bb52473d6025ff5849d5af72f79de4988ecea7e..4cf72cb895d169abaa1523139203374396c4270b 100644 (file)
@@ -2,6 +2,7 @@ package App::HomeBank2Ledger::Formatter::Ledger;
 # ABSTRACT: Ledger formatter
 
 
+use v5.10.1;    # defined-or
 use warnings;
 use strict;
 
@@ -9,7 +10,7 @@ use App::HomeBank2Ledger::Util qw(commify rtrim);
 
 use parent 'App::HomeBank2Ledger::Formatter';
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 my %STATUS_SYMBOLS = (
     cleared => '*',
@@ -23,18 +24,19 @@ sub format {
     my $ledger = shift;
 
     my @out = (
-        $self->_format_header,
-        $self->_format_accounts($ledger),
-        $self->_format_commodities($ledger),
-        $self->_format_payees($ledger),
-        $self->_format_tags($ledger),
-        $self->_format_transactions($ledger),
+        $self->format_header,
+        $self->format_accounts($ledger),
+        $self->format_commodities($ledger),
+        $self->format_payees($ledger),
+        $self->format_tags($ledger),
+        $self->format_transactions($ledger),
     );
 
     return join($/, map { rtrim($_) } @out);
 }
 
-sub _format_header {
+
+sub format_header {
     my $self = shift;
 
     my @out;
@@ -42,16 +44,17 @@ sub _format_header {
     if (my $name = $self->name) {
         push @out, "; Name: $name";
     }
-
-    my $file = $self->file;
-    push @out, "; Converted from ${file} using homebank2ledger ${VERSION}";
+    if (my $file = $self->file) {
+        push @out, "; File: $file";
+    }
 
     push @out, '';
 
     return @out;
 }
 
-sub _format_accounts {
+
+sub format_accounts {
     my $self   = shift;
     my $ledger = shift;
 
@@ -63,7 +66,8 @@ sub _format_accounts {
     return @out;
 }
 
-sub _format_commodities {
+
+sub format_commodities {
     my $self   = shift;
     my $ledger = shift;
 
@@ -81,7 +85,8 @@ sub _format_commodities {
     return @out;
 }
 
-sub _format_payees {
+
+sub format_payees {
     my $self   = shift;
     my $ledger = shift;
 
@@ -93,7 +98,8 @@ sub _format_payees {
     return @out;
 }
 
-sub _format_tags {
+
+sub format_tags {
     my $self   = shift;
     my $ledger = shift;
 
@@ -105,7 +111,8 @@ sub _format_tags {
     return @out;
 }
 
-sub _format_transactions {
+
+sub format_transactions {
     my $self   = shift;
     my $ledger = shift;
 
@@ -161,16 +168,39 @@ sub _format_transaction {
         push @line, ($posting_status_symbol ? "  $posting_status_symbol " : '    ');
         push @line, sprintf("\%-${account_width}s", $posting->{account});
         push @line, '  ';
-        push @line, $self->_format_amount($posting->{amount}, $posting->{commodity}) if defined $posting->{amount};
+        if (defined $posting->{amount}) {
+            push @line, $self->_format_amount($posting->{amount}, $posting->{commodity});
+            if (my $price = $posting->{lot_price}) {
+                my $is_fixed = $posting->{lot_fixed};
+                my $fixed_symbol = $is_fixed ? '=' : '';
+                push @line, " {${fixed_symbol}",
+                            $self->_format_amount($price->{amount}, $price->{commodity}),
+                            '}';
+            }
+            if (my $lot_date = $posting->{lot_date}) {
+                push @line, " [$posting->{lot_date}]";
+            }
+            if (my $cost = $posting->{total_cost} // $posting->{cost}) {
+                my $is_total = defined $posting->{total_cost};
+                my $cost_symbol = $is_total ? '@@' : '@';
+                push @line, ' ', $cost_symbol, ' ',
+                            $self->_format_amount($cost->{amount}, $cost->{commodity});
+            }
+        }
+        if (my $note = $posting->{note}) {
+            $note = $self->_format_string($note);
+            push @line, "  ; $note" if $note ne $memo;
+        }
 
         push @out, join('', @line);
 
-        if (my $payee = $posting->{payee}) {
-            push @out, '      ; Payee: '.$self->_format_string($payee);
+        if (my $posting_payee = $posting->{payee}) {
+            $posting_payee = $self->_format_string($posting_payee);
+            push @out, "      ; Payee: $posting_payee" if $posting_payee ne $payee;
         }
 
         if (my @tags = @{$posting->{tags} || []}) {
-            push @out, "      ; :".join(':', @tags).":";
+            push @out, '      ; :'.join(':', @tags).':';
         }
     }
 
@@ -186,6 +216,13 @@ sub _format_string {
     return $str;
 }
 
+sub _quote_string {
+    my $self = shift;
+    my $str  = shift;
+    $str =~ s/"/\\"/g;
+    return "\"$str\"";
+}
+
 sub _format_amount {
     my $self      = shift;
     my $amount    = shift;
@@ -196,7 +233,10 @@ sub _format_amount {
 
     my $num = join($commodity->{dchar}, commify($whole, $commodity->{gchar}), $fraction);
 
-    $num = $commodity->{syprf} ? "$commodity->{symbol} $num" : "$num $commodity->{symbol}";
+    my $symbol = $commodity->{symbol};
+    $symbol = $self->_quote_string($symbol) if $symbol =~ /[0-9\s]/;
+
+    $num = $commodity->{syprf} ? "$symbol $num" : "$num $symbol";
 
     return $num;
 }
@@ -215,12 +255,76 @@ App::HomeBank2Ledger::Formatter::Ledger - Ledger formatter
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 DESCRIPTION
 
 This is a formatter for L<Ledger|https://www.ledger-cli.org/>.
 
+=head1 METHODS
+
+=head2 format_header
+
+    @lines = $formatter->format_header;
+
+Get formatted header. For example,
+
+    ; Name: My Finances
+    ; File: path/to/finances.xhb
+
+=head2 format_accounts
+
+    @lines = $formatter->format_accounts($ledger);
+
+Get formatted accounts. For example,
+
+    account Assets:Bank:Credit Union:Savings
+    account Assets:Bank:Credit Union:Checking
+    ...
+
+=head2 format_commodities
+
+    @lines = $formatter->format_commodities($ledger);
+
+Get formattted commodities. For example,
+
+    commodity $
+        note US Dollar
+        format $  1,000.00
+        alias USD
+    ...
+
+=head2 format_payees
+
+    @lines = $formatter->format_payees($ledger);
+
+Get formatted payees. For example,
+
+    payee 180 Tacos
+    ...
+
+=head2 format_tags
+
+    @lines = $formatter->format_tags($ledger);
+
+Get formatted tags. For example,
+
+    tag yapc
+    ...
+
+=head2 format_transactions
+
+    @lines = $formatter->format_transactions($ledger);
+
+Get formatted transactions. For example,
+
+    2003-02-14 * Opening Balance
+        Assets:Bank:Credit Union:Savings          $  458.21
+        Assets:Bank:Credit Union:Checking         $  194.17
+        Equity:Opening Balances
+
+    ...
+
 =head1 SEE ALSO
 
 L<App::HomeBank2Ledger::Formatter>
index d61dceea555e4df8f34572a1425701fc5c457710..f89063ff7f809b719e9f03bc2900b42be5a6953e 100644 (file)
@@ -5,7 +5,7 @@ package App::HomeBank2Ledger::Ledger;
 use warnings;
 use strict;
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 
 sub new {
@@ -63,7 +63,7 @@ App::HomeBank2Ledger::Ledger - Ledger data representation
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 SYNOPSIS
 
index 0be4dd3c5114b10911c6a0197ce5f4794b5e91ac..c4d00e0750a27bd39e8e7814bea454b4d470c5ea 100644 (file)
@@ -6,7 +6,7 @@ use strict;
 
 use Exporter qw(import);
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 our @EXPORT_OK = qw(commify rtrim);
 
@@ -42,7 +42,7 @@ App::HomeBank2Ledger::Util - Miscellaneous utility functions
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 FUNCTIONS
 
index 4f38a309477bd4e27223800da7d1ebe222d18e10..5a8da62ac7bad786abe7a8ce6eccd412b2f502eb 100644 (file)
@@ -12,7 +12,7 @@ use Time::Piece;
 use XML::Entities;
 use XML::Parser::Lite;
 
-our $VERSION = '0.004'; # VERSION
+our $VERSION = '0.005'; # VERSION
 
 our @EXPORT_OK = qw(parse_string parse_file);
 
@@ -411,6 +411,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
@@ -487,7 +491,7 @@ File::HomeBank - Parse HomeBank files
 
 =head1 VERSION
 
-version 0.004
+version 0.005
 
 =head1 SYNOPSIS
 
index 4ce9265fd1250e980137c8ca9854c60a67a505de..0f6138bcc89f8c82f3ddfea43685898b7a5e6336 100644 (file)
@@ -39,6 +39,7 @@ do { my $x = {
                                       'XML::Entities' => '0',
                                       'XML::Parser::Lite' => '0',
                                       'parent' => '0',
+                                      'perl' => 'v5.10.1',
                                       'strict' => '0',
                                       'warnings' => '0'
                                     }
@@ -52,8 +53,7 @@ do { my $x = {
                                    'File::Spec' => '0',
                                    'IO::Handle' => '0',
                                    'IPC::Open3' => '0',
-                                   'Test::More' => '0',
-                                   'perl' => '5.006'
+                                   'Test::More' => '0'
                                  }
                  }
      };
This page took 0.053436 seconds and 4 git commands to generate.