#!/usr/bin/perl use strict; use warnings; # 20030513 - Initial creation # Script to generate Quicken compatible QIF files using CSV output from # SouthTrust.com. Categories are also quite absent, so I add my own. # This map is keyed on the extracted payee name, which is quite useless # and thus is replaced with the values within the array associated with # each key. The first element is the real payee and the second element # is the correct category. This won't work when a particular payee # can fall under multiple categories; My financial life isn't that busy. # Anything that's a cheque gets a check number, but no Payee name. # Since the only cheque I ever write is for rent, I just fudge it. # These are some sample entries. The hash key is the name of the # business that charged you as it appears in the CSV export. # The anonymous array's two values are the company name as you # wish it to appear in Quicken[0] and the category name as it # appears in Quicken, including a colon between subcategories[1]. # Here's an example from a recent CSV export I did for Staples. # Match up the fields with %map below and what I do in the # code and you can take it from here. # 08/05/2003,CkCd STAPLES #235 ALA FL,,-$90.89,, my %map = ( 'cheque' => [ 'Company That Owns Me', 'Rent' ], 'DEPOSIT' => [ 'Deposit', 'Deposit' ], 'GLEIM PUB GLEIM PAY' => [ 'Gleim Publications', 'Salary' ], 'SERVICE CHARGE' => [ 'South Trust', 'Bank Charge' ], 'CHECK CARD MONTHLY FEE' => [ 'South Trust', 'Bank Charge' ], 'ATM SHARED NETWORK FEE' => [ 'South Trust', 'Bank Charge' ], 'CkCd STAPLES #235 ALA FL' => [ 'Staples', 'Computer:Components' ], 'CkCd OFFICE MAX 0 5-885-0828 FL' => [ 'Office Max', 'Computer:Components' ], 'CkCd REGAL CINEMAS G INESVILLE FL' => [ 'Regal Cinemas', 'Entertainment:Theater' ] ); print "!Type:Bank\r\n"; # Skip the first line my $line = ; undef $line; # We want to suck in each record, which is ^\r\n terminated. #$/ = "^\r\n"; while( my $rec = ) { # my @chars = split( '', $rec ); # foreach my $z (@chars) { # print "' $z ' " . ord( $z ), "\n"; # } # last; my( $date, $desc, $checkn, $dt, $cr, undef ) = split( /,/, $rec ); #D4/30/99 #U1,156.20 #T1,156.20 #CX #POpening Balance #L[Checking] #^ my $amount; if( $dt ne '' ) { $amount = $dt; } elsif( $cr ne '' ) { $amount = $cr; } else { $amount = 0; } # If it's NULL it's a cheque; If I wrote more of these I'd # probably need to figure out the Payee based on amount if # it is recurring or just make up a guess. if( $desc eq '' ) { $desc = 'cheque'; } # If it exists in our map, replace SouthTrust's values with our # own custom values. If we don't have a map entry, we # need $cat to simply be empty. my( $payee, $cat ); if( exists $map{ $desc } ) { $payee = $map{ $desc }[0]; $cat = "L"; $cat .= $map{ $desc }[1]; $cat .= "\r\n"; } else { $payee = $desc; $cat = ''; warn "$desc has no map entry!\n"; } print "D$date\r\nU$amount\r\nP$payee\r\n$cat^\r\n"; }