From 2761fbe1d147a87c4294277078eb6a14080de99d Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Sat, 15 Jan 2022 16:31:46 -0700 Subject: [PATCH] add posting-specific dates --- lib/App/HomeBank2Ledger.pm | 8 +- .../HomeBank2Ledger/Formatter/Beancount.pm | 9 ++- lib/App/HomeBank2Ledger/Formatter/Ledger.pm | 79 +++++++++++++------ lib/App/HomeBank2Ledger/Ledger.pm | 21 ++++- 4 files changed, 83 insertions(+), 34 deletions(-) diff --git a/lib/App/HomeBank2Ledger.pm b/lib/App/HomeBank2Ledger.pm index c4bf5a6..9ff8b00 100644 --- a/lib/App/HomeBank2Ledger.pm +++ b/lib/App/HomeBank2Ledger.pm @@ -294,10 +294,12 @@ sub convert_homebank_to_ledger { my $memo = $transaction->{wording} || ''; my $payee = $homebank->find_payee_by_key($transaction->{payee}); my $tags = _split_tags($transaction->{tags}); + my $date = $transaction->{date}; my @postings; push @postings, { + date => $date, account => $account->{ledger_name}, amount => $amount, commodity => $commodities{$account->{currency}}, @@ -324,9 +326,11 @@ sub convert_homebank_to_ledger { $seen{$transaction->{transfer_key}}++ if $transaction->{transfer_key}; $seen{$paired_transaction->{transfer_key}}++ if $paired_transaction->{transfer_key}; + my $paired_date = $paired_transaction && $paired_transaction->{date}; my $paired_payee = $homebank->find_payee_by_key($paired_transaction->{payee}); push @postings, { + date => $paired_date, account => $dst_account->{ledger_name}, amount => $paired_transaction->{amount} || -$transaction->{amount}, commodity => $commodities{$dst_account->{currency}}, @@ -360,7 +364,7 @@ sub convert_homebank_to_ledger { }; } } - else { # with or without category + else { # normal transaction with or without category my $amount = -$transaction->{amount}; my $category = $homebank->find_category_by_key($transaction->{category}); my $other_account = $category ? $category->{ledger_name} @@ -386,7 +390,7 @@ sub convert_homebank_to_ledger { } $ledger->add_transactions({ - date => $transaction->{date}, + date => $date, payee => $payee->{name}, memo => $memo, postings => \@postings, diff --git a/lib/App/HomeBank2Ledger/Formatter/Beancount.pm b/lib/App/HomeBank2Ledger/Formatter/Beancount.pm index dc98934..ca15108 100644 --- a/lib/App/HomeBank2Ledger/Formatter/Beancount.pm +++ b/lib/App/HomeBank2Ledger/Formatter/Beancount.pm @@ -172,7 +172,7 @@ sub _format_transaction { my $date = $transaction->{date}; my $status = $transaction->{status}; my $payee = $transaction->{payee} || ''; - my $memo = $transaction->{memo} || ''; + my $memo = $transaction->{note} // $transaction->{memo} // ''; my @postings = @{$transaction->{postings}}; my @out; @@ -220,9 +220,10 @@ sub _format_transaction { push @line, ' '; 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}; + my $lot = $posting->{lot} || {}; + my $lot_price = $lot->{price} // $posting->{lot_price}; + my $lot_date = $lot->{date} // $posting->{lot_date}; + my $lot_ref = $lot->{ref} // $posting->{lot_ref}; if ($lot_price || $lot_date || $lot_ref) { push @line, ' {', join(', ', diff --git a/lib/App/HomeBank2Ledger/Formatter/Ledger.pm b/lib/App/HomeBank2Ledger/Formatter/Ledger.pm index 300a832..c6fa556 100644 --- a/lib/App/HomeBank2Ledger/Formatter/Ledger.pm +++ b/lib/App/HomeBank2Ledger/Formatter/Ledger.pm @@ -208,16 +208,18 @@ sub _format_transaction { my $account_width = $self->account_width; - my $date = $transaction->{date}; - my $status = $transaction->{status}; - my $payee = $self->_format_string($transaction->{payee} || ''); - my $memo = $self->_format_string($transaction->{memo} || ''); - my @postings = @{$transaction->{postings}}; + my $date = $transaction->{date} or _croak 'Transaction date is required'; + my $aux_date = $transaction->{aux_date} || $transaction->{effective_date} || ''; + my $status = $transaction->{status} // ''; + my $code = $transaction->{code}; + my $payee = $self->_format_string($transaction->{payee}); + my $note = $self->_format_string($transaction->{note} // $transaction->{memo}); + my @postings = @{$transaction->{postings} || _croak 'At least one transaction posting is required'}; my @out; # figure out the Ledger transaction status - my $status_symbol = $STATUS_SYMBOLS{$status || ''}; + my $status_symbol = $STATUS_SYMBOLS{$status}; if (!$status_symbol) { my %posting_statuses = map { ($_->{status} || '') => 1 } @postings; if (keys(%posting_statuses) == 1) { @@ -226,17 +228,28 @@ sub _format_transaction { } } - $payee =~ s/(?: )|\t;/ ;/g; # don't turn into a memo + $aux_date = '' if $date eq $aux_date; + $code =~ s/[\(\)]+// if defined $code; + $payee =~ s/(?: )|\t;/ ;/g if defined $payee; # don't turn into a note - push @out, sprintf('%s%s%s%s', $date, - $status_symbol && " ${status_symbol}", - $payee && " $payee", - $memo && " ; $memo", + my $has_code = defined $code && $code ne ''; + my $has_payee = defined $payee && $payee ne ''; + my $has_note = defined $note && $note ne ''; + + push @out, join('', $date, + $aux_date && "=${aux_date}", + $status_symbol && " ${status_symbol}", + $has_code && " (${code})", + $has_payee && " ${payee}", + $has_note && $has_payee && " ; ${note}", ); + if ($has_note && !$has_payee) { + push @out, " ; ${note}"; + } my $metadata = $transaction->{metadata} || {}; for my $key (sort keys %$metadata) { - my $value = $self->_format_string($metadata->{$key}); + my $value = $self->_format_string($metadata->{$key}) ; push @out, " ; ${key}: ${value}"; } @@ -253,15 +266,21 @@ sub _format_transaction { push @line, ' '; 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 $lot = $posting->{lot} || {}; + if (my $lot_price = $lot->{price} // $posting->{lot_price}) { + my $is_fixed = $lot_price->{fixed} // $posting->{lot_fixed}; my $fixed_symbol = $is_fixed ? '=' : ''; push @line, " {${fixed_symbol}", - $self->_format_amount($price->{amount}, $price->{commodity}), + $self->_format_amount($lot_price->{amount}, $lot_price->{commodity}), '}'; } - if (my $lot_date = $posting->{lot_date}) { - push @line, " [$posting->{lot_date}]"; + if (my $lot_date = $lot->{date} // $posting->{lot_date}) { + push @line, " [${lot_date}]"; + } + if (my $lot_note = $self->_format_string($lot->{note} // $posting->{lot_note} // '')) { + $lot_note =~ s/[\(\)]+//; # cleanup + $lot_note =~ s/^\@+//; + push @line, " (${lot_note})" if $lot_note; } if (my $cost = $posting->{total_cost} // $posting->{cost}) { my $is_total = defined $posting->{total_cost}; @@ -270,9 +289,22 @@ sub _format_transaction { $self->_format_amount($cost->{amount}, $cost->{commodity}); } } - if (my $note = $posting->{note}) { - $note = $self->_format_string($note); - push @line, " ; $note" if $note ne $memo; + my $posting_date = $posting->{date} || ''; + my $posting_aux_date = $posting->{aux_date} || ''; + my $posting_note = $self->_format_string($posting->{note} // $posting->{memo} // ''); + $posting_date = '' if $posting_date eq $date; + $posting_aux_date = '' if $posting_aux_date eq $aux_date; + $posting_note = '' if $has_note && $posting_note eq $note; + my $has_posting_note = defined $posting_note && $posting_note ne ''; + if ($posting_date || $posting_aux_date || $has_posting_note) { + if ($posting_date || $posting_aux_date) { + $posting_note = sprintf('[%s%s]%s', + $posting_date, + $posting_aux_date && "=${posting_aux_date}", + $has_posting_note && " ${posting_note}", + ); + } + push @line, " ; ${posting_note}"; } push @out, join('', @line); @@ -283,9 +315,8 @@ sub _format_transaction { push @out, " ; ${key}: ${value}"; } - 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 $posting_payee = $self->_format_string($posting->{payee} // '')) { + push @out, " ; Payee: $posting_payee" if !$has_payee || $posting_payee ne $payee; } if (my @tags = @{$posting->{tags} || []}) { @@ -300,7 +331,7 @@ sub _format_transaction { sub _format_string { my $self = shift; - my $str = shift; + my $str = shift // return; $str =~ s/\v//g; return $str; } diff --git a/lib/App/HomeBank2Ledger/Ledger.pm b/lib/App/HomeBank2Ledger/Ledger.pm index 72a0953..2c8f15e 100644 --- a/lib/App/HomeBank2Ledger/Ledger.pm +++ b/lib/App/HomeBank2Ledger/Ledger.pm @@ -24,7 +24,7 @@ accounts. Examples: =for :list * "Assets:Bank:Chase1234" -* "Liabilities:Credit Card:CapitalOne" +* "Liabilities:Credit Card:Capital One" =head2 commodity @@ -51,9 +51,11 @@ This is a hashref like this: { date => '2019-06-12', # required - payee => 'Malcolm Reynolds', # required + aux_date => '2019-06-13', # optional status => 'cleared', # optional; can be "cleared" or "pending" - memo => 'Medical supplies', # optional + code => '1234', # optional + payee => 'Malcolm Reynolds', # required + note => 'Medical supplies', # optional postings => [ # required { account => 'Some Account', # required @@ -69,9 +71,20 @@ This is a hashref like this: frac => 2, }, payee => 'Somebody', # optional - memo => 'Whatever', # optional + note => 'Whatever', # optional status => 'pending', # optional; can be "cleared" or "pending" tags => [qw(niska train-job)], + lot => { # optional + date => '2019-01-28', + price => { + amount => '15.00', + commodity => { ... }, + }, + }, + cost => { # optional + amount => '10.00', + commodity => { ... }, + }, }, ... ], -- 2.45.2