package Finance::OFX::Parse::Simple;
use warnings;
use strict;
=head1 NAME
Finance::OFX::Parse::Simple - Parse a simple OFX file or scalar
=head1 VERSION
Version 0.01
=cut
our $VERSION = '0.01';
=head1 SYNOPSIS
use Finance::OFX::Parse::Simple;
my $parser = Finance::OFX::Parse::Simple->new;
my $data_from_file = $parser->parse_file("myfile.ofx"); # $data is a reference to a list of hash references
my $data_from_scalar = $parser->parse_scalar($ofx_data);
=head1 METHODS
=head2 new
Create a new parser object.
=cut
sub new
{
my $class = shift;
my $self = bless {}, ref($class) || $class;
return $self;
}
=head2 parse_file
Takes a filename as an argument, slurps the file into memory, parses
it and returns a reference to a list of hash references. Each hash
reference contains two keys: 'account_id' which is the account number,
and 'transactions' which is a reference to a list of hash references,
each containing details for a single transaction.
Returns false if no filename was provided, the file is not a plain
file, or the file is not openable.
=cut
sub parse_file
{
my $self = shift;
my $file = shift or return;
-f($file) or return;
my $contents = do
{
open my $fh, "<", $file or return;
local $/;
my $c = <$fh>;
close $fh;
$c;
};
return $self->parse_scalar($contents);
}
=head2 parse_scalar
Takes a scalar as an argument containing OFX data. Returns a reference
to a list of hash references. Each hash reference contains two keys:
'account_id' which is the account number, and 'transactions' which is
a reference to a list of hash references, each containing details for
a single transaction.
Returns false if no non-empty scalar is provided.
=cut
sub parse_scalar
{
my $self = shift;
my $ofx = shift or return;
my @results = (); # to be returned
transaction_group:
while ($ofx =~ m!((.+?))!sg)
{
my ($all,$statements) = ($1,$2);
my $this = { account_id => undef, transactions => [] };
my $account_id = do
{
my $aa = 0;
if ($all =~ m:(\d+)\s*<:s)
{
$aa = $1;
}
$aa;
}
or do {warn "No ACCTID found"; next transaction_group};
$this->{account_id} = $account_id;
while ($statements =~ m/(.+?)<\/BANKTRANLIST>/sg)
{
my $trans = $1;
while ($trans =~ m/(.+?)<\/STMTTRN>/sg)
{
my $s = $1;
my ($y,$m,$d) = do
{
$s =~ m/(\d\d\d\d)(\d\d)(\d\d)/s;
($1,$2,$3);
};
my $amount = do
{
$s =~ m/\s*(-|\+)?\s*((?:\d+(?:\.\d\d)?)|\.\d\d)/s;
($1 and $1 eq '-') ? abs($2) * -1 : sprintf("%.2f", $2);
};
my $fitid = do
{
$s =~ m/([^\r\n<]+)/s;
$1;
};
my $name = do
{
$s =~ m/([^\r\n<]+)/s;
$1;
};
my $memo = do
{
my $w = "";
if ($s =~ m/([^\r\n<]+)/s)
{
$w = $1;
}
$w;
};
push @{$this->{transactions}}, {amount => $amount, date => "$y-$m-$d",
fitid => $fitid, name => $name, memo => $memo};
}
}
push @results, $this;
}
return \@results;
}
=head1 WARNING
From Finance::Bank::LloydsTSB:
This is code for online banking, and that means your money, and that
means BE CAREFUL. You are encouraged, nay, expected, to audit the
source of this module yourself to reassure yourself that I am not
doing anything untoward with your banking data. This software is
useful to me, but is provided under NO GUARANTEE, explicit or implied.
=head1 AUTHOR
Jeremy Jones, C<< >>
=head1 BUGS
This module is intended to be fit-for-purpose, that purpose being simple parsing of real-world OFX files
as provided for download by online banking systems. For more thorough and better treatment of your OFX
data see Finance::OFX::Parse.
Please report any bugs or feature requests to C, or through
the web interface at L.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Finance::OFX::Parse::Simple
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L
=item * AnnoCPAN: Annotated CPAN documentation
L
=item * CPAN Ratings
L
=item * Search CPAN
L
=back
=head1 COPYRIGHT & LICENSE
Copyright 2009 Jeremy Jones, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1; # End of Finance::OFX::Parse::Simple