]> Dogcows Code - chaz/homebank2ledger/blobdiff - lib/File/HomeBank.pm
internal xfers no longer use a paymode
[chaz/homebank2ledger] / lib / File / HomeBank.pm
index 31ca39d097ea8ab771418d117d7f37085fdec86c..184cb8928193beb804cf9bc031fc768dbde2f20c 100644 (file)
@@ -30,15 +30,13 @@ use App::HomeBank2Ledger::Util qw(commify);
 use Exporter qw(import);
 use Scalar::Util qw(refaddr);
 use Time::Piece;
+use XML::Entities;
 use XML::Parser::Lite;
 
 our $VERSION = '9999.999'; # VERSION
 
 our @EXPORT_OK = qw(parse_string parse_file);
 
-sub _croak { require Carp; Carp::croak(@_) }
-sub _usage { _croak("Usage: @_\n") }
-
 my %ACCOUNT_TYPES = (
     0   => 'none',
     1   => 'bank',
@@ -104,6 +102,9 @@ my %TRANSACTION_PAYMODES = (
     11  => 'directdebit',
 );
 
+sub _croak { require Carp; Carp::croak(@_) }
+sub _usage { _croak("Usage: @_\n") }
+
 =method new
 
     $homebank = File::HomeBank->new(string => $str);
@@ -151,6 +152,18 @@ sub file {
     shift->{file};
 }
 
+=method file_version
+
+    $version = $homebank->file_version;
+
+Get the file format version.
+
+=cut
+
+sub file_version {
+    shift->{homebank}{version};
+}
+
 =method title
 
     $title = $homebank->title;
@@ -225,7 +238,7 @@ sub tags {
 
     $account = $homebank->find_account_by_key($key);
 
-Find a account with the given key.
+Find an account with the given key.
 
 =cut
 
@@ -364,7 +377,7 @@ sub find_transaction_transfer_pair {
     my $self = shift;
     my $transaction = shift;
 
-    return if $transaction->{paymode} ne 'internaltransfer';
+    return if !$transaction->{dst_account};
 
     my $transfer_key = $transaction->{transfer_key};
 
@@ -384,7 +397,7 @@ sub find_transaction_transfer_pair {
     my @candidates;
 
     for my $t (@{$self->transactions}) {
-        next if $t->{paymode} ne 'internaltransfer';
+        next if !$t->{dst_account};
         next if $t->{account} != $transaction->{dst_account};
         next if $t->{dst_account} != $transaction->{account};
         next if $t->{amount} != -$transaction->{amount};
@@ -399,9 +412,9 @@ sub find_transaction_transfer_pair {
 
     # sort the candidates so we can pick the nearest one by date
     my @ordered_candidates =
-        map { $_->[1] }
+        map  { $_->[1] }
         sort { $a->[0] <=> $b->[0] }
-        map { [abs($transaction_day - _ymd_to_julian($_->{date})), $_] } @candidates;
+        map  { [abs($transaction_day - _ymd_to_julian($_->{date})), $_] } @candidates;
 
     if (my $winner = $ordered_candidates[0]) {
         my $key1 = $transfer_key || '[no key]';
@@ -415,7 +428,7 @@ sub find_transaction_transfer_pair {
 
     $transations = $homebank->sorted_transactions;
 
-Get an arrayref of transactions sorted by date (earliest first).
+Get an arrayref of transactions sorted by date (oldest first).
 
 =cut
 
@@ -519,6 +532,7 @@ Parse a HomeBank file from a string.
 sub parse_string {
     my $str = shift or die _usage(q{parse_string($str)});
 
+    my %homebank;
     my %properties;
     my @accounts;
     my @payees;
@@ -532,7 +546,17 @@ sub parse_string {
                 shift;
                 my $node = shift;
                 my %attr = @_;
-                if ($node eq 'properties') {
+
+                # decode all attribute values
+                for my $key (keys %attr) {
+                    $attr{$key} = _decode_xml_entities($attr{$key});
+                }
+
+                if ($node eq 'homebank') {
+                    $attr{version} = delete $attr{v} if $attr{v};
+                    %homebank = %attr;
+                }
+                elsif ($node eq 'properties') {
                     $attr{currency} = delete $attr{curr} if $attr{curr};
                     %properties = %attr;
                 }
@@ -568,11 +592,15 @@ 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
-                    $attr{paymode} = $TRANSACTION_PAYMODES{$attr{paymode} || ''} || 'unknown';
-                    $attr{status}  = $TRANSACTION_STATUSES{delete $attr{st}} || 'unknown';
+                    $attr{paymode} = $TRANSACTION_PAYMODES{$attr{paymode}   || ''} || 'unknown';
+                    $attr{status}  = $TRANSACTION_STATUSES{delete $attr{st} || ''} || 'unknown';
 
                     $attr{transfer_key}   = delete $attr{kxfer} if $attr{kxfer};
                     $attr{split_amount}   = delete $attr{samt}  if $attr{samt};
@@ -594,6 +622,7 @@ sub parse_string {
     $xml_parser->parse($str);
 
     return {
+        homebank        => \%homebank,
         properties      => \%properties,
         accounts        => \@accounts,
         payees          => \@payees,
@@ -603,6 +632,14 @@ sub parse_string {
     };
 }
 
+sub _decode_xml_entities {
+    my $str = shift;
+    # decoding entities can be extremely slow, so don't bother if it doesn't look like there are any
+    # entities to decode
+    return $str if $str !~ /&(?:#\d+)|[A-Za-z0-9]+;/;
+    return XML::Entities::decode('all', $str);
+}
+
 sub _rdn_to_unix_epoch {
     my $rdn = shift;
     my $jan01_1970 = 719163;
This page took 0.027313 seconds and 4 git commands to generate.