/************************* * * * * * * * * * * * * *************************** Copyright (c) 1999-2005 Ryan Bobko ryan@ostrich-emulators.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ************************** * * * * * * * * * * * * **************************/ #include "qifplugin.h" #include "qhacc.h" #include "qhaccutils.h" #include "qhacctable.h" #include #include #include // plugin factory calls extern "C" { QHaccPlugin * create(){ return new QIFImporter; } void destroy( QIFImporter * p ){ delete p; } } const QIFInfo QIFImporter::pinfo; QIFImporter::QIFImporter() : QHaccIOPlugin() { accounts=0; splits=0; transactions=0; } QIFImporter::~QIFImporter(){ delete accounts; delete splits; delete transactions; } const PluginInfo& QIFImporter::info() const { return pinfo; } bool QIFImporter::connect( QHacc * e, const QString& h, QString& err ){ engine=e; transactions=new QHaccTable( QC::TCOLS, QC::TCOLTYPES, QC::TABLENAMES[QC::TRANT] ); accounts =new QHaccTable( QC::ACOLS, QC::ACOLTYPES, QC::TABLENAMES[QC::ACCTT] ); splits =new QHaccTable( QC::SCOLS, QC::SCOLTYPES, QC::TABLENAMES[QC::SPLTT] ); transactions->setPK( QC::TID ); accounts->setPK( QC::AID ); splits->setPK( QC::SID ); // the home format we need is QIF:: // the QIF part is removed by the engine, so we just need // the account name and filename int delim=h.find( ":" ); if( delim<0 ){ err="no account name given"; return false; // no account name } aname=h.left( delim ); fname=h.mid( delim+1 ); return true; } bool QIFImporter::load( QString& err ){ bool loaded=false; const MonCon& conv=engine->converter(); // find out the engine's max aid, in case we need to add more accounts uint maxaid=engine->max( ACCOUNTS, QC::AID ).getu(); // this is our main import account: all the transactions in the qif file // should have one transaction in this account, and one somewhere else Account mainacct=engine->getA( aname ); // if the engine has this account, we don't need to add it // if it's not in the engine, make a new account if( mainacct.isNull() ){ mainacct=engine->getBlankA(); mainacct.set( QC::AID, TableCol( ++maxaid ) ); mainacct.set( QC::ANAME, aname ); accounts->add( mainacct ); } QFile qiff( fname ); if( qiff.exists() && qiff.open( IO_ReadOnly ) ){ QTextStream in( &qiff ); // the file is open, so start making transactions--qif files // only keep some information that QHacc can use, so ignore the // other stuff. uint rr=0; vector v; auto_ptr js=engine->getWhere( JOURNALS, v, rr ); journal=js->at( engine->getIP( "JOURNALINDEX" ) ); // this is the information we'll keep QDate tdate=QDate::currentDate(); QString payee, num, memo, sum; uint tid=0, sid=0, aid=0, reco=QC::NREC; // if we run into splits, keep track of aid, sum, reconcile for each split ColType cts[]={ CTUINT, CTSTRING, CTUINT }; QHaccTable asplits( 3, cts ); // this is the transaction creator loop while( !in.atEnd() ) { QString line=in.readLine(); const QString TYPE=line.left( 1 ); // trans info type identifier const QString INFO=line.mid( 1 ); // trans info if( TYPE=="D" ) tdate=Utils::dateFromString( INFO, "/", QC::EUROPEAN ); else if( TYPE=="P" ) payee=INFO; else if( TYPE=="M" ) memo=INFO; else if( TYPE=="T" ) sum=INFO; else if( TYPE=="N" ) num=INFO; else if( TYPE=="C" ){ if( INFO=="R" ) reco=QC::YREC; } else if( TYPE=="$" ){ // when splits come up, the account is entered first, and then the // split sum, so by the time we get the $ line, we've already // added a split to the asplits table (see below). We need to update // that row, and we can use the current aid to find it asplits.updateWhere( TableSelect( 0, TableCol( aid ) ), TableUpdate( 1, TableCol( conv.convert( INFO, Engine, Engine ) ) ) ); } else if( TYPE=="L" || TYPE=="S" ){ // splits // have we seen this account already? Account newa=accounts->getWhere( TableSelect( QC::ANAME, TableCol( INFO ) ) ); if( newa.isNull() ){ // haven't see this acct before, so see // if it exists in the engine's database newa=engine->getA( INFO ); if( newa.isNull() ){ // doesn't exist in the engine either, so make a new account newa=engine->getBlankA(); newa.set( QC::AID, TableCol( ++maxaid ) ); newa.set( QC::ANAME, TableCol( INFO ) ); } // else exists in the engine already // add this account to our cache accounts->add( newa ); } // keep the current aid for any splits that follow (see above) aid=newa[QC::AID].getu(); // our sum should be the reverse of the transaction's sum TableCol cols[]={ TableCol( aid ), TableCol( conv.convert( -conv.converti( sum, Engine, Engine ), Engine, Engine ) ), TableCol( QC::NREC ) }; asplits+=TableRow( cols, 3 ); } else if( TYPE=="^" ){ // this is the transaction delimiter, so add what we have so far Transaction trans( QC::TCOLS ); trans.set( QC::TID, ++tid ); trans.set( QC::TNUM, num ); trans.set( QC::TPAYEE, payee ); trans.set( QC::TMEMO, memo ); trans.set( QC::TDATE, tdate ); trans.set( QC::TLID, "1" ); trans.set( QC::TTYPE, QC::REGULAR ); transactions->add( trans ); // add a row to asplit for the main account TableCol cols[]={ mainacct[QC::AID], TableCol( conv.convert( sum, Engine, Engine ) ), TableCol( reco ) }; asplits+=TableRow( cols, 3 ); // now add a split for every row in asplits Split s( QC::SCOLS ); for( uint i=0; iadd( s ); } // reset all data for the next transaction asplits.clear(); payee=memo=sum=num=QString(); reco=QC::NREC; tdate=QDate::currentDate(); } } qiff.close(); loaded=true; } else err="could not open file: "+fname; return loaded; } bool QIFImporter::exprt( QHaccResultSet * rslts ){ rslts[QC::TRANT]=*transactions; rslts[QC::SPLTT]=*splits; rslts[QC::ACCTT]=*accounts; rslts[QC::JRNLT]+=journal; return true; } // this is just an importer, so we don't need these functions, // but they must be defined to instatiate this class bool QIFImporter::imprt( QHaccResultSet * ){ return false; } bool QIFImporter::save( QString& ){ return false; } QIFInfo::QIFInfo(){ targ=SINGLEFILE; description="Quicken"; stubby="QIF"; }