File Coverage

blib/lib/Finance/Bank/Bundesschatz.pm
Criterion Covered Total %
statement 21 105 20.0
branch 0 24 0.0
condition n/a
subroutine 7 12 58.3
pod 2 3 66.6
total 30 144 20.8


line stmt bran cond sub pod time code
1             # $Id: Bundesschatz.pm,v 1.3 2003/10/12 12:03:43 florian Exp $
2              
3             package Finance::Bank::Bundesschatz;
4              
5             require 5.005_62;
6 1     1   687 use strict;
  1         2  
  1         35  
7 1     1   5 use warnings;
  1         1  
  1         25  
8              
9 1     1   4 use Carp;
  1         1  
  1         85  
10 1     1   1267 use WWW::Mechanize;
  1         257475  
  1         46  
11 1     1   15 use HTML::TokeParser;
  1         1  
  1         30  
12             use constant {
13 1         92 LOGIN_URL => 'https://www.bundesschatz.at/ebss/kunde/pages/Logon.jsp',
14 1     1   4 };
  1         2  
15             use Class::MethodMaker
16 1         12 new_hash_init => 'new',
17             get_set => [ qw/account pass coowner _agent/ ],
18 1     1   1821 boolean => [ qw/return_floats _connected/ ];
  1         23516  
19              
20             our $VERSION = '1.01';
21              
22              
23             # login into the online banking system.
24             # fail if either account or password isn't defined.
25             #
26             # XXX: catch login errors.
27             sub _connect {
28 0     0     my $self = shift;
29 0           my $content;
30              
31 0 0         croak "Need account to connect.\n" unless $self->account;
32 0 0         croak "Need password to connect.\n" unless $self->pass;
33              
34 0           $self->_agent(WWW::Mechanize->new);
35 0           $self->_agent->agent_alias('Mac Safari');
36 0           $self->_agent->get(LOGIN_URL);
37 0           $self->_agent->form_number(1);
38 0           $self->_agent->field('logon_user', $self->account);
39 0           $self->_agent->field('logon_bnr', $self->coowner);
40 0           $self->_agent->field('logon_passwd', $self->pass);
41 0           $self->_agent->click('button_ok');
42              
43 0           $content = $self->_agent->content;
44              
45 0 0         croak "The online banking system told me, that the account or the password was invalid.\n"
46             if $content =~ /Fehler: Bitte überprüfen Sie Ihre Kontonummer und Ihr Passwort/;
47              
48             # fail on everything which doesn't look like the Kontostand page.
49 0 0         croak "It seems as if the online banking system want's your attention.\n" .
50             "Please login manually and take a look.\n"
51             if $content !~ /Kontostand von:/;
52             }
53              
54              
55             # fetches and parses the summary page.
56             #
57             # returns a hashref containing the isolated data.
58             sub check_balance {
59 0     0 1   my $self = shift;
60 0           my $stream;
61             my %data;
62              
63 0 0         defined $self->_agent or $self->_connect;
64              
65 0           $self->_agent->follow_link(url_regex => qr/Kontostand/);
66 0           $stream = HTML::TokeParser->new(\($self->_agent->content));
67              
68 0           $stream->get_tag('form');
69 0           $stream->get_tag('tr') for 1 .. 5;
70 0           $stream->get_tag('td') for 1 .. 5;
71 0           $data{balance} = $stream->get_trimmed_text('/td');
72              
73 0           $stream->get_tag('tr') for 1 .. 2;
74 0           $stream->get_tag('td') for 1 .. 5;
75 0           $data{interest} = $stream->get_trimmed_text('/td');
76              
77 0 0         if($self->return_floats) {
78             $data{$_} = $self->_scalar2float($data{$_})
79 0           for qw/balance interest/;
80             }
81              
82 0           \%data;
83             }
84              
85              
86             # fetches and parses the detail page.
87             #
88             # returns a arrayref containing all entries.
89             sub get_details {
90 0     0 1   my $self = shift;
91 0           my $stream;
92             my @entries;
93 0           my $content;
94              
95 0 0         defined $self->_agent or $self->_connect;
96              
97 0           $self->_agent->follow_link(url_regex => qr/Kontodetails/);
98 0           $content = $self->_agent->content;
99 0           $content =~ /(.*)/s;
100 0           $stream = HTML::TokeParser->new(\$1);
101              
102 0           while ($stream->get_tag('tr')) {
103 0           my %data;
104              
105 0           $stream->get_tag('td') for 1 .. 5;
106 0           $data{product} = $stream->get_trimmed_text('/td');
107              
108 0           $stream->get_tag('td') for 1 .. 2;
109 0           ($data{from}, $data{to}) =
110             split / - /, $stream->get_trimmed_text('/td');
111              
112 0           for(qw/currency amount interest interest_amount
113             tax amount_after_tax/) {
114 0           $stream->get_tag('td') for 1 .. 2;
115 0           $data{$_} = $stream->get_trimmed_text('/td');
116             }
117              
118 0 0         if($self->return_floats) {
119             $data{$_} = $self->_scalar2float($data{$_})
120 0           for qw/amount interest interest_amount
121             tax amount_after_tax/;
122             }
123              
124 0           $stream->get_tag('/tr') for 1 .. 2;
125              
126 0           push @entries, \%data;
127             }
128              
129 0           \@entries;
130             }
131              
132              
133             sub check_interest {
134 0     0 0   my $self = shift;
135 0           my @entries;
136             my $stream;
137              
138 0 0         defined $self->_agent or $self->_connect;
139              
140 0           $self->_agent->follow_link(url_regex => qr/Zinschart/);
141 0           $self->_agent->follow_link(url_regex => qr/Zinsentwicklung/);
142              
143 0           $stream = HTML::TokeParser->new(\($self->_agent->content));
144              
145 0           $stream->get_tag('/form');
146 0           $stream->get_tag('table') for 1 .. 2;
147 0           $stream->get_tag('tr') for 1 .. 3;
148              
149 0           while (1) {
150 0           my %data;
151              
152 0 0         last unless $stream->get_tag('tr');
153              
154 0           $stream->get_tag('td') for 1 .. 3;
155 0           $data{published} = $stream->get_trimmed_text('/td');
156 0           $stream->get_tag('td') for 1 .. 4;
157 0           $data{valid_on} = $stream->get_trimmed_text('/td');
158 0           $stream->get_tag('td') for 1 .. 4;
159 0           $data{interest_bs1} = $stream->get_trimmed_text('/td');
160 0           $stream->get_tag('td') for 1 .. 4;
161 0           $data{interest_bs2} = $stream->get_trimmed_text('/td');
162 0           $stream->get_tag('td') for 1 .. 4;
163 0           $data{interest_bs3} = $stream->get_trimmed_text('/td');
164              
165 0 0         if($self->return_floats) {
166             $data{$_} = $self->_scalar2float($data{$_})
167 0           for qw/interest_bs1 interest_bs2 interest_bs3/;
168             }
169              
170 0           push @entries, \%data;
171             }
172              
173             # remove the last element of the entries list because it's always
174             # empty. well, kind of ugly, but i couldn't find another way.
175 0           pop @entries;
176              
177 0           \@entries;
178             }
179              
180              
181             # converts given scalar ($scalar) into a float and returns it.
182             sub _scalar2float {
183 0     0     my($self, $scalar) = @_;
184              
185 0 0         defined $scalar or return undef;
186              
187 0           $scalar =~ s/[\.%]//g;
188 0           $scalar =~ s/,/\./g;
189              
190 0           $scalar;
191             }
192              
193              
194             1;