I’ve recently started to use Quicken an application to keep track of your spending by allowing you to tag items and categorize them, schedule payments and bills and generally have a better overview of your accounts.

If your American Quicken seems to have agreements with all the major banks to allow you to download and upload files directly to their sites making it easy to stay up to date. Being British this was no help to me and so I was stuck with downloading files and importing them manually into Quicken. This worked just fine with my Natwest account as they offer an OFX file of your statements that Quicken can deal with but unfortunately LloydsTSB don’t! They’ve not kept up with the times and only offer the options of CSV or QIF, QIF being the pre 2001 Quicken file. This left me we a bit of a problem as I was enjoying using Quicken to take control of my finances and wanted to include my second account and so I set out to write a conversion tool to take my CSV file and produce an OFX one.

require_once($_POST['php']); // include the bank file submitted

$bank = new Bank(); // initisialise the bank file
$transactions = $bank->transArray($_POST['csv']); // send file submitted to the bank function for putting into an array.

$institute = $_POST['institute']; // get the institute number being used
$currency = $_POST['currency']; // get the currency being used
$numberoftrans = $_POST['numf']; // get the number of items per transaction for the bank
$accounttype = $_POST['accountt']; // get the account type for the bank

if ($_POST['sortc'] == "first"){ // get the sort code from the array
 $sortcode = $transactions[(int)$_POST['sortr']]; // look at the first row of the array
}else{
 $sortcode = $transactions[count($transactions)-($numberoftrans - (int)$_POST['sortr'])]; // look at the last row of the array
}
if ($_POST['accountc'] == "first"){ // get the account number from the array
 $accountnumber = $transactions[(int)$_POST['accountr']]; // look at the first row of the array
}else{
 $accountnumber = $transactions[count($transactions)-($numberoftrans - (int)$_POST['accountr'])]; // look at the last row of the array
}

if ($_POST['startc'] == "first"){ // get the start date from the array
 $startdate = $transactions[(int)$_POST['startr']]; // look at the first row of the array
}else{
 $startdate = $transactions[count($transactions)-($numberoftrans - (int)$_POST['startr'])]; // look at the last row of the array
}

if ($_POST['endc'] == "first"){ // get the end date from the array
 $enddate = $transactions[(int)$_POST['endr']]; // look at the first row of the array
}else{
 $enddate = $transactions[count($transactions)-($numberoftrans - (int)$_POST['endr'])]; // look at the last row of the array
}

if ($_POST['finalc'] == "first"){ // get the final balance from the array
 $balance = $transactions[(int)$_POST['finalr']]; // look at the first row of the array
}else{
 $balance = $transactions[count($transactions)-($numberoftrans - (int)$_POST['finalr'])]; // look at the last row of the array
}

$filename = "./robmcghee" . $sortcode . $accountnumber . ".ofx"; // create a new OFX for the user
$fp = fopen($filename,"w"); // open the file for write access

/*
Below is a bog standard layout for an OFX file that Quicken will except
The data from the csv file we have parsed and the data entered onto the front screen will be entered in
*/
fwrite($fp, "OFXHEADER:100\n"); // header of the file
fwrite($fp, "DATA:OFXSGML\n");
fwrite($fp, "VERSION:102\n");
fwrite($fp, "SECURITY:NONE\n");
fwrite($fp, "ENCODING:USASCII\n");
fwrite($fp, "CHARSET:1252\n");
fwrite($fp, "COMPRESSION:NONE\n");
fwrite($fp, "OLDFILEUID:NONE\n");
fwrite($fp, "NEWFILEUID:NONE\n");

fwrite($fp, "\n"); // bank information
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "<code>0</code>\n"); // error code 0 is all ok
fwrite($fp, "INFO\n");
fwrite($fp, "\n");
fwrite($fp, "" . date(YmdHis) . "\n"); // 20100302093319 YYYYMMDDHHMMSS
fwrite($fp, "ENG\n");
fwrite($fp, "" . $institute  . "\n"); // The financial institute number given by quicken. We use Natwests(00508) it's valid
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "1\n");
fwrite($fp, "\n");
fwrite($fp, "<code>0</code>\n"); // error code 0 is all ok
fwrite($fp, "INFO\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "" . $currency . "\n"); // currency
fwrite($fp, "\n");
fwrite($fp, "" . $sortcode . "\n"); // sort code
fwrite($fp, "" . $accountnumber . "\n"); // account number
fwrite($fp, "" . $accounttype . "\n"); // type of account
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "" . $startdate . "\n"); // start date YYYYMMDD
fwrite($fp, "" . $enddate . "\n"); // end date YYYYMMDD

// here is a loop that enters all the transaction from the csv file
$inc = 1;
$i = 0;
while($i < count($transactions)){

 fwrite($fp, "\n");
 fwrite($fp, "" . $transactions[$i + (int)$_POST['ttr']] . "\n"); // transaction type
 fwrite($fp, "" . $transactions[$i + (int)$_POST['tdr']] . "\n"); // transaction date YYYYMMDD
 fwrite($fp, "" . $transactions[$i + (int)$_POST['tar']] . "\n"); // amount if credit then - goes in front
 if ($i >= $numberoftrans){ // work out if this is another transaction on the same day as the last
 if(    $transactions[$i + (int)$_POST['tdr']] == $transactions[($i + (int)$_POST['tdr'])-$numberoftrans]){
 $inc++;
 }else{
 $inc = 1;
 }
 }
 fwrite($fp, "" . $transactions[$i + (int)$_POST['tdr']] . substr($inc+10000,1) . "\n"); // date plus incremental number
 fwrite($fp, "" . $transactions[$i + (int)$_POST['tnr']] . "\n"); // transaction name
 if (!$transactions[$i + (int)$_POST['tmr']] == null){ // if a memo exists
 fwrite($fp, "" . $transactions[$i + (int)$_POST['tmr']] . "\n"); // transaction memo
 }
 fwrite($fp, "\n");

 $i = $i + $numberoftrans;
}

fwrite($fp, "\n"); // closing section of an OFX file
fwrite($fp, "\n");
fwrite($fp, "" . $balance  . "\n"); // ledger balance - use balance of last transaction
fwrite($fp, "" . $enddate . "\n"); // date - put the last date of the transaction in there
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "" . $balance . "\n"); // amount availabe this takes into account pending payments which this won't deal with
fwrite($fp, "" . $enddate . "\n"); // date - put the last date of the transaction in there
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fwrite($fp, "\n");
fclose($fp);

?>

Above is my main PHP file which takes parameters about the CSV file and writes and OFX file from it.

include('function.php');

class Bank {

 //initialise the class
 public function __construct() {

 }

 //RETURNS an array of transacion data
 public function transArray($fileName){
 $ofxFunction = new ofxFunction(); // initisialise the function file
 $i = 0; // count number of items
 $items = array();
 $file = fopen($fileName,'r');
 while(!feof($file)){
 $line = fgets($file); // read file in line by line
 $fields = $ofxFunction->splitStringtoArray($line, ",");
 $j = 0;
 while($j < count($fields)){ // count the number of variables
 if ($j == 0){
 $items[$i] = $ofxFunction->dateReFormat($fields[$j], "/", "DDMMYYYY", "YYYYMMDD"); // insert the formatted date
 }elseif ($j == 2) {
 $items[$i] = $ofxFunction->stripAllDelim("-", "", $fields[$j]); // insert the formatted sort code
 }elseif ($j == 4) { // contains the name and memo field
 $namememo = $ofxFunction->splitStringtoArray($fields[$j], "."); // split the field by .
 $items[$i] = $ofxFunction->stripAllDelim("\"", "", $namememo[0]); // blank the quote and add name into the array
 $i++; // advance i

 if (count($namememo)>2){ // if we have more than 2 items then there is a memo
 $items[$i] = $ofxFunction->stripAllDelim("\"", "", $namememo[1]); // blank the quote and insert memo into array
 } else {
 $items[$i] = null; // no memo so insert blank this is key to keep record lengths normal.
 }
 } elseif ($j == 5) { // check to see if it's a debit or a credit
 if (!$fields[$j] == ""){ // if 5 is not empty then its a debit
 $val = $ofxFunction->createDebit($fields[$j]); // turn the positive value into a negative
 $items[$i] = $ofxFunction->decimalise($val); // insert a formatted value
 $j++;
 } else { // its a credit
 $j++;
 $items[$i] = $ofxFunction->decimalise($fields[$j]); //add the item no processing necessary
 }
 } else {
 $items[$i] = $fields[$j]; //add the item no processing necessary
 }
 $i++; // advance i
 $j++; // advance j
 }
 }
 fclose($file);
 return $items;
 }
}
?>

Above is my Lloyds.php file which deals specifically with LloydsTSB csv file and splits it up into an array for writing to the file.

<?PHP
class ofxFunction {

 //initialise the class
 public function __construct() {

 }

 //RETURNS a date in string formatted in any order requested
 //note only deals with 4 digit year codes.
 //$date = date string we want to reformat
 //$deli = the delimitor in the date we want to blank (not needed if none used)
 //$now = the format the date is in currently e.g DDMMYYYY
 //$after = the format we want the date to be in afterwards e.g YYYYMMDD
 public function dateReFormat($date, $deli, $now, $after){
 $dte = $date;

 if ($deli != null){ // remove the date delim if supplied
 $dte = str_replace($deli, "", $date); // replace the delim with blank so e.g 01/01/2010 becomes 01012010
 }

 $daystart = stripos($now, "D"); // find position of first D in the current date format
 $dayend = strripos($now, "D"); // find position of last D in the current date format
 $monthstart = stripos($now, "M"); // find position of first M in the current date format
 $monthend = strripos($now, "M"); // find position of last M in the current date format
 $yearstart = stripos($now, "Y"); // find position of first Y in the current date format
 $yearend = strripos($now, "Y"); // find position of last Y in the current date format

 $day = substr($dte, $daystart, (($dayend + 1)-$daystart)); // day from the date passed
 $month = substr($dte, $monthstart, (($monthend + 1)-$monthstart)); // month from the date passed
 $year = substr($dte, $yearstart, (($yearend + 1)-$yearstart)); // year from the date passed

 $replace = array("DD" => $day, "MM" => $month, "YYYY" => $year); // replace these with numbers stored
 $dydd =  strtr(strtoupper($after), $replace); // Put the numbers into the new date format

 $dydd = trim($dydd); // get rid of any blank spaces at the start or end of the item
 return $dydd; // return formatted date
 }

 //RETURNS a decimalised value so a . followed by two digits and no , for thousands
 //$val = the value that we want to decimalise
 public function decimalise($val){
 $decval = number_format((float)$val,2,".",""); // variable is type float, 2 decimanl places after a . and no , in thousands
 return $decval; // return decimalised value
 }

 //RETURNS a string with all delimiters removed
 //$delim = the character that we want to remove
 //$replace = the character we're going to replace the delim with so "" o blank it
 //$line = string of text that has the delim in
 public function stripAllDelim($delim, $replace ,$line){
 $line = str_replace($delim, $replace,$line); // replace any delim with a blank
 $line = trim($line); // get rid of any blank spaces at the start or end of the item
 return $line; // return formatted string
 }

 //RETURNS a string with certain delimiters removed
 //$delim = the character that we want to remove
 //$replace = the character we're going to replace the delim with so "" to blank it
 //$line = string of text that has the delim in
 //$start = the delim number to start at
 //$stop = the delim number to stop at
 public function stripCertainDelim($delim, $replace, $line, $start, $stop){
 $i = 0; // used to count number of delims
 $substart = 0; // remember the position we last checked
 while ($i < $stop){
 $pos = strpos($line, $delim, $substart); // find delim
 if ($pos !== false){ // if we found an instance then carry on and replace it
 $substart = $pos + 1; // start next time after the postion we're at now
 $i++; // increase the count by 1
 if ($i >= $start){
 substr_replace($line, $replace , $pos, $pos);// remove the delim
 }
 }else{
 $i = $stop; // get out of the loop
 }
 }
 $line = trim($line); // get rid of any blank spaces at the start or end of the item
 return $line; // return formatted string
 }

 //RETURNS a negative value from a positive one
 //$value = item to be converted into negative/positive value
 public function createDebit($value){
 $debit = (float)$value; // convert value to float
 $debit = $debit - (2 * $debit); // value minus 2 times it self gives you the neg/pos of the value
 return $debit; // return the debit value
 }

 //RETURNS array of strings split by the delim passed
 //$line = the string to be split into an array
 //$delim = the delim that we use to split for CSV use ,
 public function splitStringtoArray($line, $delim){
 $fields = explode($delim,$line); // split the string using the delim
 return $fields; // return array of strings
 }

 //RETURNS a value with leading zeros
 //$num = item to have leading zeros a head of
 public function leadingZeros($num){
 return substr($num+10000,1);
 }

}
?>

The above is a function file that I used to do simple tasks and will allow others to use my files if they wanted.

To create your own OFX file for your bank you’d just need to write your own Lloyds.php or call it whatever and the other files will do the rest.