-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathciti_stmt_to_csv-table
executable file
·71 lines (65 loc) · 3.6 KB
/
citi_stmt_to_csv-table
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/usr/bin/perl
# run on output of `pdftotext -table Citi_CreditCardStatement.pdf Citi_CreditCardStatement.p2t-table`
# see %~dp0/CCStmtP2tToCsv.pm for details
use strict;
use warnings;
use FindBin;
use lib $FindBin::Bin;
use CCStmtP2tToCsv;
my $opts = CCStmtP2tToCsv::getopts();
my $rdt = '\d{2}/\d{2}';
my $rchnam = '[A-Z][A-Z\s]+[A-Z]'; # assume length(cardholdername) >= 3
my $rchsfx = '\s+Card\s+ending\s+in\s+\d{4}';
my $rdesc = '\w.*\w';
my $rpmdamtcapt = CCStmtP2tToCsv::rpmdamtcapt();
my $rtxn = qr"\s+($rdt)\s+($rdesc)$rpmdamtcapt";
my $retxn = "^(?:$rdt)?$rtxn";
my $reStdPurch = 'Standard\s+Purchases';
my($plus,$minus) = qw( credit charge );
for my $ifnm ( sort @ARGV ) { # sort so that write_all_csv content will be sorted (assumes stmt filenames are validly date-sortable)
my $lp_GenericCharges = sub { my $self = shift; $self->parse_new_txn( $retxn, [$minus] ); };
my $lp_Payments = sub { my $self = shift; $self->parse_new_txn( $retxn, [$plus ] ); };
my ($cardholder,$namish); # multiline parse support
my $lp_CardholderSummary = sub { my $self = shift;
if( !defined($cardholder) ) {
if( defined( $namish ) && m"^$rchsfx" ) { $cardholder = $namish; undef $namish; print "cardholder = $cardholder\n"; return; }
if( m"^($rchnam)$rchsfx" ) { $cardholder = $1 ; print "cardholder = $cardholder\n"; return; }
if( m"^($rchnam)\s" ) { $namish = $1; return; }
}
elsif( m"^New\sCharges$rpmdamtcapt" ) {
$self->rmv_section_hdr( $reStdPurch ); # if cardholders appear in stmt, $reStdPurch => $lp_GenericCharges is replaced by '$cardholder, sub ...'
$self->set_total( [$minus,$cardholder], $1 );
my $ch_capt = $cardholder; # freeze this $cardholder value for closure
$self->add_section_hdr( $cardholder, sub { my $self = shift; $self->parse_new_txn( $retxn, [$minus, $ch_capt] ); } );
undef $cardholder; # we're done with this $cardholder
}
};
my $lp_ignore = sub {;};
my $AccountSummary = sub { my $self = shift;
if( m"(?:Payments|Credits)$rpmdamtcapt" ) { $self->add_total( [$plus ], $1 ); }
elsif( m"Purchases$rpmdamtcapt" ) { $self->set_total( [$minus], $1 ); }
};
my $lp_BillPeriod = sub { my $self = shift;
if( m"^\s*Billing\s+Period:\s+\d{2}/\d{2}/(\d{2})\-(\d{2})/(\d{2})/(\d{2})" ) { print "yrMin/yrMax raw $1/$4\n";
$self->set_stmtOpenCloseDates();
my $mon = qw(JANUARY FEBRUARY MARCH APRIL MAY JUNE JULY AUGUST SEPTEMBER OCTOBER NOVEMBER DECEMBER)[($2 =~ s!^0!!r)-1];
if( 0 ) { # less strict, but works (and maybe permissive is what's needed?)
$self->add_section_hdr( "$mon STATEMENT ", $AccountSummary );
}
else { # alternative: two possible section headers, but (unresolved) is that only one will match, yielding an unmatched error
$self->add_section_hdr( "$mon STATEMENT Account Summary", $AccountSummary );
$self->add_section_hdr( "$mon STATEMENT Your next AutoPay payment of ", $AccountSummary );
}
}
};
my $init_key = '(start_of_file)';
CCStmtP2tToCsv::process_stmt_p2t( $ifnm, {
$init_key => $lp_BillPeriod,
'CARDHOLDER SUMMARY' => $lp_CardholderSummary,
'Payments, Credits and Adjustments' => $lp_Payments,
'Fees Charged' => $lp_ignore,
$reStdPurch => $lp_GenericCharges,
}, $init_key, [$minus], $opts
);
}
CCStmtP2tToCsv::write_all_csv() if @ARGV > 1;