/***************************************************************************
mymoneygncreader - description
- - - - - - - - - - - - - - - - - - -
begin : Wed Mar 3 2004
copyright : ( C ) 2000 - 2004 by Michael Edwardes
email : mte @ users . sourceforge . net
Javier Campos Morales < javi_c @ users . sourceforge . net >
Felix Rodriguez < frodriguez @ users . sourceforge . net >
John C < thetacoturtle @ users . sourceforge . net >
Thomas Baumgart < ipwizard @ users . sourceforge . net >
Kevin Tambascio < ktambascio @ users . sourceforge . net >
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ----------------------------------------------------------------------------
// QT Includes
# include <tqfile.h>
# include <tqmap.h>
# include <tqobject.h>
# include <tqfiledialog.h>
# include <tqinputdialog.h>
# include <tqdatetime.h>
// ----------------------------------------------------------------------------
// KDE Includes
# ifndef _GNCFILEANON
# include <klocale.h>
# include <kconfig.h>
# include <kmessagebox.h>
# endif
// ----------------------------------------------------------------------------
// Third party Includes
// ------------------------------------------------------------Box21----------------
// Project Includes
# include "mymoneygncreader.h"
# ifndef _GNCFILEANON
# include "config.h"
# include "../mymoney/storage/imymoneystorage.h"
# include "../kmymoneyutils.h"
# include "../mymoney/mymoneyfile.h"
# include "../mymoney/mymoneyprice.h"
# include "../dialogs/kgncimportoptionsdlg.h"
# include "../dialogs/kgncpricesourcedlg.h"
# include "../dialogs/keditscheduledlg.h"
# include "../widgets/kmymoneyedit.h"
# define TRY try {
# define CATCH } catch (MyMoneyException *e) {
# define PASS } catch (MyMoneyException *e) { throw e; }
# else
# include "mymoneymoney.h"
# include <tqtextedit.h>
# define i18n TQObject::tr
# define TRY
# define CATCH
# define PASS
# define MYMONEYEXCEPTION TQString
# define MyMoneyException TQString
# define PACKAGE "KMyMoney"
# endif // _GNCFILEANON
// init static variables
double MyMoneyGncReader : : m_fileHideFactor = 0.0 ;
double GncObject : : m_moneyHideFactor ;
// user options
void MyMoneyGncReader : : setOptions ( ) {
# ifndef _GNCFILEANON
KGncImportOptionsDlg dlg ; // display the dialog to allow the user to set own options
if ( dlg . exec ( ) ) {
// set users input options
m_dropSuspectSchedules = dlg . scheduleOption ( ) ;
m_investmentOption = dlg . investmentOption ( ) ;
m_useFinanceQuote = dlg . quoteOption ( ) ;
m_useTxNotes = dlg . txNotesOption ( ) ;
m_decoder = dlg . decodeOption ( ) ;
gncdebug = dlg . generalDebugOption ( ) ;
xmldebug = dlg . xmlDebugOption ( ) ;
bAnonymize = dlg . anonymizeOption ( ) ;
} else {
// user declined, so set some sensible defaults
m_dropSuspectSchedules = false ;
// investment option - 0, create investment a/c per stock a/c, 1 = single new investment account, 2 = prompt for each stock
// option 2 doesn't really work too well at present
m_investmentOption = 0 ;
m_useFinanceQuote = false ;
m_useTxNotes = false ;
m_decoder = 0 ;
gncdebug = false ; // general debug messages
xmldebug = false ; // xml trace
bAnonymize = false ; // anonymize input
}
// no dialog option for the following; it will set base currency, and print actual XML data
developerDebug = false ;
// set your fave currency here to save getting that enormous dialog each time you run a test
// especially if you have to scroll down to USD...
if ( developerDebug ) m_storage - > setValue ( " kmm-baseCurrency " , " GBP " ) ;
# endif // _GNCFILEANON
}
GncObject : : GncObject ( ) {
m_v . setAutoDelete ( true ) ;
}
// Check that the current element is of a version we are coded for
void GncObject : : checkVersion ( const TQString & elName , const TQXmlAttributes & elAttrs , const map_elementVersions & map ) {
TRY
if ( map . contains ( elName ) ) { // if it's not in the map, there's nothing to check
if ( ! map [ elName ] . contains ( elAttrs . value ( " version " ) ) ) {
TQString em = i18n ( " %1: Sorry. This importer cannot handle version %2 of element %3 " )
. arg ( __func__ ) . arg ( elAttrs . value ( " version " ) ) . arg ( elName ) ;
throw new MYMONEYEXCEPTION ( em ) ;
}
}
return ;
PASS
}
// Check if this element is in the current object's sub element list
GncObject * GncObject : : isSubElement ( const TQString & elName , const TQXmlAttributes & elAttrs ) {
TRY
uint i ;
GncObject * next = 0 ;
for ( i = 0 ; i < m_subElementListCount ; i + + ) {
if ( elName = = m_subElementList [ i ] ) {
m_state = i ;
next = startSubEl ( ) ; // go create the sub object
if ( next ! = 0 ) {
next - > initiate ( elName , elAttrs ) ; // initialize it
next - > m_elementName = elName ; // save it's name so we can identify the end
}
break ;
}
}
return ( next ) ;
PASS
}
// Check if this element is in the current object's data element list
bool GncObject : : isDataElement ( const TQString & elName , const TQXmlAttributes & elAttrs ) {
TRY
uint i ;
for ( i = 0 ; i < m_dataElementListCount ; i + + ) {
if ( elName = = m_dataElementList [ i ] ) {
m_state = i ;
dataEl ( elAttrs ) ; // go set the pointer so the data can be stored
return ( true ) ;
}
}
m_dataPtr = 0 ; // we don't need this, so make sure we don't store extraneous data
return ( false ) ;
PASS
}
// return the variable string, decoded if required
TQString GncObject : : var ( int i ) const {
return ( pMain - > m_decoder = = 0
? * ( m_v . at ( i ) )
: pMain - > m_decoder - > toUnicode ( * ( m_v . at ( i ) ) ) ) ;
}
void GncObject : : adjustHideFactor ( ) {
m_moneyHideFactor = pMain - > m_fileHideFactor * ( 1.0 + ( int ) ( 200.0 * rand ( ) / ( RAND_MAX + 1.0 ) ) ) / 100.0 ;
}
// data anonymizer
TQString GncObject : : hide ( TQString data , unsigned int anonClass ) {
TRY
if ( ! pMain - > bAnonymize ) return ( data ) ; // no anonymizing required
// counters used to generate names for anonymizer
static int nextAccount ;
static int nextEquity ;
static int nextPayee ;
static int nextSched ;
static TQMap < TQString , TQString > anonPayees ; // to check for duplicate payee names
static TQMap < TQString , TQString > anonStocks ; // for reference to equities
TQString result ( data ) ;
TQMap < TQString , TQString > : : Iterator it ;
MyMoneyMoney in , mresult ;
switch ( anonClass ) {
case ASIS : break ; // this is not personal data
case SUPPRESS : result = " " ; break ; // this is personal and is not essential
case NXTACC : result = i18n ( " Account%1 " ) . arg ( + + nextAccount , - 6 ) ; break ; // generate account name
case NXTEQU : // generate/return an equity name
it = anonStocks . find ( data ) ;
if ( it = = anonStocks . end ( ) ) {
result = i18n ( " Stock%1 " ) . arg ( + + nextEquity , - 6 ) ;
anonStocks . insert ( data , result ) ;
} else {
result = ( * it ) ;
}
break ;
case NXTPAY : // genearet/return a payee name
it = anonPayees . find ( data ) ;
if ( it = = anonPayees . end ( ) ) {
result = i18n ( " Payee%1 " ) . arg ( + + nextPayee , - 6 ) ;
anonPayees . insert ( data , result ) ;
} else {
result = ( * it ) ;
}
break ;
case NXTSCHD : result = i18n ( " Schedule%1 " ) . arg ( + + nextSched , - 6 ) ; break ; // generate a schedule name
case MONEY1 :
in = MyMoneyMoney ( data ) ;
if ( data = = " -1/0 " ) in = MyMoneyMoney ( 0 ) ; // spurious gnucash data - causes a crash sometimes
mresult = MyMoneyMoney ( m_moneyHideFactor ) * in ;
mresult . convert ( 10000 ) ;
result = mresult . toString ( ) ;
break ;
case MONEY2 :
in = MyMoneyMoney ( data ) ;
if ( data = = " -1/0 " ) in = MyMoneyMoney ( 0 ) ;
mresult = MyMoneyMoney ( m_moneyHideFactor ) * in ;
mresult . convert ( 10000 ) ;
mresult . setThousandSeparator ( ' ' ) ;
result = mresult . formatMoney ( " " , 2 ) ;
break ;
}
return ( result ) ;
PASS
}
// dump current object data values // only called if gncdebug set
void GncObject : : debugDump ( ) {
uint i ;
tqDebug ( " Object %s " , m_elementName . latin1 ( ) ) ;
for ( i = 0 ; i < m_dataElementListCount ; i + + ) {
tqDebug ( " %s = %s " , m_dataElementList [ i ] . latin1 ( ) , m_v . at ( i ) - > latin1 ( ) ) ;
}
}
//*****************************************************************
GncFile : : GncFile ( ) {
static const TQString subEls [ ] = { " gnc:book " , " gnc:count-data " , " gnc:commodity " , " price " ,
" gnc:account " , " gnc:transaction " , " gnc:template-transactions " ,
" gnc:schedxaction "
} ;
m_subElementList = subEls ;
m_subElementListCount = END_FILE_SELS ;
m_dataElementListCount = 0 ;
m_processingTemplates = false ;
m_bookFound = false ;
}
GncFile : : ~ GncFile ( ) { }
GncObject * GncFile : : startSubEl ( ) {
TRY
if ( pMain - > xmldebug ) tqDebug ( " File start subel m_state %d " , m_state ) ;
GncObject * next = 0 ;
switch ( m_state ) {
case BOOK :
if ( m_bookFound ) throw new MYMONEYEXCEPTION ( i18n ( " This version of the importer cannot handle multi-book files. " ) ) ;
m_bookFound = true ;
break ;
case COUNT : next = new GncCountData ; break ;
case CMDTY : next = new GncCommodity ; break ;
case PRICE : next = new GncPrice ; break ;
case ACCT :
// accounts within the template section are ignored
if ( ! m_processingTemplates ) next = new GncAccount ;
break ;
case TX : next = new GncTransaction ( m_processingTemplates ) ; break ;
case TEMPLATES : m_processingTemplates = true ; break ;
case SCHEDULES : m_processingTemplates = false ; next = new GncSchedule ; break ;
default : throw new MYMONEYEXCEPTION ( " GncFile rcvd invalid state " ) ;
}
return ( next ) ;
PASS
}
void GncFile : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " File end subel " ) ;
if ( ! m_processingTemplates ) delete subObj ; // template txs must be saved awaiting schedules
m_dataPtr = 0 ;
return ;
}
//****************************************** GncDate *********************************************
GncDate : : GncDate ( ) {
m_subElementListCount = 0 ;
static const TQString dEls [ ] = { " ts:date " , " gdate " } ;
m_dataElementList = dEls ;
m_dataElementListCount = END_Date_DELS ;
static const unsigned int anonClasses [ ] = { ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
}
GncDate : : ~ GncDate ( ) { }
//*************************************GncCmdtySpec***************************************
GncCmdtySpec : : GncCmdtySpec ( ) {
m_subElementListCount = 0 ;
static const TQString dEls [ ] = { " cmdty:space " , " cmdty:id " } ;
m_dataElementList = dEls ;
m_dataElementListCount = END_CmdtySpec_DELS ;
static const unsigned int anonClasses [ ] = { ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
}
GncCmdtySpec : : ~ GncCmdtySpec ( ) { }
TQString GncCmdtySpec : : hide ( TQString data , unsigned int ) {
// hide equity names, but not currency names
unsigned int newClass = ASIS ;
switch ( m_state ) {
case CMDTYID :
if ( ! isCurrency ( ) ) newClass = NXTEQU ;
}
return ( GncObject : : hide ( data , newClass ) ) ;
}
//************* GncKvp********************************************
GncKvp : : GncKvp ( ) {
m_subElementListCount = END_Kvp_SELS ;
static const TQString subEls [ ] = { " slot " } ; // kvp's may be nested
m_subElementList = subEls ;
m_dataElementListCount = END_Kvp_DELS ;
static const TQString dataEls [ ] = { " slot:key " , " slot:value " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_kvpList . setAutoDelete ( true ) ;
}
GncKvp : : ~ GncKvp ( ) { }
void GncKvp : : dataEl ( const TQXmlAttributes & elAttrs ) {
switch ( m_state ) {
case VALUE :
m_kvpType = elAttrs . value ( " type " ) ;
}
m_dataPtr = m_v . at ( m_state ) ;
if ( key ( ) . contains ( " formula " ) ) {
m_anonClass = MONEY2 ;
} else {
m_anonClass = ASIS ;
}
return ;
}
GncObject * GncKvp : : startSubEl ( ) {
if ( pMain - > xmldebug ) tqDebug ( " Kvp start subel m_state %d " , m_state ) ;
TRY
GncObject * next = 0 ;
switch ( m_state ) {
case KVP : next = new GncKvp ; break ;
default : throw new MYMONEYEXCEPTION ( " GncKvp rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncKvp : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Kvp end subel " ) ;
m_kvpList . append ( subObj ) ;
m_dataPtr = 0 ;
return ;
}
//*********************************GncLot*********************************************
GncLot : : GncLot ( ) {
m_subElementListCount = 0 ;
m_dataElementListCount = 0 ;
}
GncLot : : ~ GncLot ( ) { }
//*********************************GncCountData***************************************
GncCountData : : GncCountData ( ) {
m_subElementListCount = 0 ;
m_dataElementListCount = 0 ;
m_v . append ( new TQString ( " " ) ) ; // only 1 data item
}
GncCountData : : ~ GncCountData ( ) { }
void GncCountData : : initiate ( const TQString & , const TQXmlAttributes & elAttrs ) {
m_countType = elAttrs . value ( " cd:type " ) ;
m_dataPtr = m_v . at ( 0 ) ;
return ;
}
void GncCountData : : terminate ( ) {
int i = m_v . at ( 0 ) - > toInt ( ) ;
if ( m_countType = = " commodity " ) {
pMain - > setGncCommodityCount ( i ) ; return ;
}
if ( m_countType = = " account " ) {
pMain - > setGncAccountCount ( i ) ; return ;
}
if ( m_countType = = " transaction " ) {
pMain - > setGncTransactionCount ( i ) ; return ;
}
if ( m_countType = = " schedxaction " ) {
pMain - > setGncScheduleCount ( i ) ; return ;
}
if ( i ! = 0 ) {
if ( m_countType = = " budget " ) pMain - > setBudgetsFound ( true ) ;
else if ( m_countType . left ( 7 ) = = " gnc:Gnc " ) pMain - > setSmallBusinessFound ( true ) ;
else if ( pMain - > xmldebug ) tqDebug ( " Unknown count type %s " , m_countType . latin1 ( ) ) ;
}
return ;
}
//*********************************GncCommodity***************************************
GncCommodity : : GncCommodity ( ) {
m_subElementListCount = 0 ;
static const TQString dEls [ ] = { " cmdty:space " , " cmdty:id " , " cmdty:name " , " cmdty:fraction " } ;
m_dataElementList = dEls ;
m_dataElementListCount = END_Commodity_DELS ;
static const unsigned int anonClasses [ ] = { ASIS , NXTEQU , SUPPRESS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
}
GncCommodity : : ~ GncCommodity ( ) { }
void GncCommodity : : terminate ( ) {
TRY
pMain - > convertCommodity ( this ) ;
return ;
PASS
}
//************* GncPrice********************************************
GncPrice : : GncPrice ( ) {
static const TQString subEls [ ] = { " price:commodity " , " price:currency " , " price:time " } ;
m_subElementList = subEls ;
m_subElementListCount = END_Price_SELS ;
m_dataElementListCount = END_Price_DELS ;
static const TQString dataEls [ ] = { " price:value " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_vpCommodity = NULL ;
m_vpCurrency = NULL ;
m_vpPriceDate = NULL ;
}
GncPrice : : ~ GncPrice ( ) {
delete m_vpCommodity ; delete m_vpCurrency ; delete m_vpPriceDate ;
}
GncObject * GncPrice : : startSubEl ( ) {
TRY
GncObject * next = 0 ;
switch ( m_state ) {
case CMDTY : next = new GncCmdtySpec ; break ;
case CURR : next = new GncCmdtySpec ; break ;
case PRICEDATE : next = new GncDate ; break ;
default : throw new MYMONEYEXCEPTION ( " GncPrice rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncPrice : : endSubEl ( GncObject * subObj ) {
TRY
switch ( m_state ) {
case CMDTY : m_vpCommodity = static_cast < GncCmdtySpec * > ( subObj ) ; break ;
case CURR : m_vpCurrency = static_cast < GncCmdtySpec * > ( subObj ) ; break ;
case PRICEDATE : m_vpPriceDate = static_cast < GncDate * > ( subObj ) ; break ;
default : throw new MYMONEYEXCEPTION ( " GncPrice rcvd invalid m_state " ) ;
}
return ;
PASS
}
void GncPrice : : terminate ( ) {
TRY
pMain - > convertPrice ( this ) ;
return ;
PASS
}
//************* GncAccount********************************************
GncAccount : : GncAccount ( ) {
m_subElementListCount = END_Account_SELS ;
static const TQString subEls [ ] = { " act:commodity " , " slot " , " act:lots " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_Account_DELS ;
static const TQString dataEls [ ] = { " act:id " , " act:name " , " act:description " ,
" act:type " , " act:parent " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , NXTACC , SUPPRESS , ASIS , ASIS } ;
m_anonClassList = anonClasses ;
m_kvpList . setAutoDelete ( true ) ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_vpCommodity = NULL ;
}
GncAccount : : ~ GncAccount ( ) {
delete m_vpCommodity ;
}
GncObject * GncAccount : : startSubEl ( ) {
TRY
if ( pMain - > xmldebug ) tqDebug ( " Account start subel m_state %d " , m_state ) ;
GncObject * next = 0 ;
switch ( m_state ) {
case CMDTY : next = new GncCmdtySpec ; break ;
case KVP : next = new GncKvp ; break ;
case LOTS : next = new GncLot ( ) ;
pMain - > setLotsFound ( true ) ; // we don't handle lots; just set flag to report
break ;
default : throw new MYMONEYEXCEPTION ( " GncAccount rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncAccount : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Account end subel " ) ;
switch ( m_state ) {
case CMDTY : m_vpCommodity = static_cast < GncCmdtySpec * > ( subObj ) ; break ;
case KVP : m_kvpList . append ( subObj ) ;
}
return ;
}
void GncAccount : : terminate ( ) {
TRY
pMain - > convertAccount ( this ) ;
return ;
PASS
}
//************* GncTransaction********************************************
GncTransaction : : GncTransaction ( bool processingTemplates ) {
m_subElementListCount = END_Transaction_SELS ;
static const TQString subEls [ ] = { " trn:currency " , " trn:date-posted " , " trn:date-entered " ,
" trn:split " , " slot " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_Transaction_DELS ;
static const TQString dataEls [ ] = { " trn:id " , " trn:num " , " trn:description " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , SUPPRESS , NXTPAY } ;
m_anonClassList = anonClasses ;
adjustHideFactor ( ) ;
m_template = processingTemplates ;
m_splitList . setAutoDelete ( true ) ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_vpCurrency = NULL ;
m_vpDateEntered = m_vpDatePosted = NULL ;
}
GncTransaction : : ~ GncTransaction ( ) {
delete m_vpCurrency ; delete m_vpDatePosted ; delete m_vpDateEntered ;
}
GncObject * GncTransaction : : startSubEl ( ) {
TRY
if ( pMain - > xmldebug ) tqDebug ( " Transaction start subel m_state %d " , m_state ) ;
GncObject * next = 0 ;
switch ( m_state ) {
case CURRCY : next = new GncCmdtySpec ; break ;
case POSTED :
case ENTERED :
next = new GncDate ; break ;
case SPLIT :
if ( isTemplate ( ) ) {
next = new GncTemplateSplit ;
} else {
next = new GncSplit ;
}
break ;
case KVP : next = new GncKvp ; break ;
default : throw new MYMONEYEXCEPTION ( " GncTransaction rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncTransaction : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Transaction end subel " ) ;
switch ( m_state ) {
case CURRCY : m_vpCurrency = static_cast < GncCmdtySpec * > ( subObj ) ; break ;
case POSTED : m_vpDatePosted = static_cast < GncDate * > ( subObj ) ; break ;
case ENTERED : m_vpDateEntered = static_cast < GncDate * > ( subObj ) ; break ;
case SPLIT : m_splitList . append ( subObj ) ; break ;
case KVP : m_kvpList . append ( subObj ) ;
}
return ;
}
void GncTransaction : : terminate ( ) {
TRY
if ( isTemplate ( ) ) {
pMain - > saveTemplateTransaction ( this ) ;
} else {
pMain - > convertTransaction ( this ) ;
}
return ;
PASS
}
//************* GncSplit********************************************
GncSplit : : GncSplit ( ) {
m_subElementListCount = END_Split_SELS ;
static const TQString subEls [ ] = { " split:reconcile-date " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_Split_DELS ;
static const TQString dataEls [ ] = { " split:id " , " split:memo " , " split:reconciled-state " , " split:value " ,
" split:quantity " , " split:account " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , SUPPRESS , ASIS , MONEY1 , MONEY1 , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_vpDateReconciled = NULL ;
}
GncSplit : : ~ GncSplit ( ) {
delete m_vpDateReconciled ;
}
GncObject * GncSplit : : startSubEl ( ) {
TRY
GncObject * next = 0 ;
switch ( m_state ) {
case RECDATE : next = new GncDate ; break ;
default : throw new MYMONEYEXCEPTION ( " GncTemplateSplit rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncSplit : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Split end subel " ) ;
switch ( m_state ) {
case RECDATE : m_vpDateReconciled = static_cast < GncDate * > ( subObj ) ; break ;
}
return ;
}
//************* GncTemplateSplit********************************************
GncTemplateSplit : : GncTemplateSplit ( ) {
m_subElementListCount = END_TemplateSplit_SELS ;
static const TQString subEls [ ] = { " slot " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_TemplateSplit_DELS ;
static const TQString dataEls [ ] = { " split:id " , " split:memo " , " split:reconciled-state " , " split:value " ,
" split:quantity " , " split:account " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , SUPPRESS , ASIS , MONEY1 , MONEY1 , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_kvpList . setAutoDelete ( true ) ;
}
GncTemplateSplit : : ~ GncTemplateSplit ( ) { }
GncObject * GncTemplateSplit : : startSubEl ( ) {
if ( pMain - > xmldebug ) tqDebug ( " TemplateSplit start subel m_state %d " , m_state ) ;
TRY
GncObject * next = 0 ;
switch ( m_state ) {
case KVP : next = new GncKvp ; break ;
default : throw new MYMONEYEXCEPTION ( " GncTemplateSplit rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncTemplateSplit : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " TemplateSplit end subel " ) ;
m_kvpList . append ( subObj ) ;
m_dataPtr = 0 ;
return ;
}
//************* GncSchedule********************************************
GncSchedule : : GncSchedule ( ) {
m_subElementListCount = END_Schedule_SELS ;
static const TQString subEls [ ] = { " sx:start " , " sx:last " , " sx:end " , " gnc:freqspec " , " gnc:recurrence " , " sx:deferredInstance " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_Schedule_DELS ;
static const TQString dataEls [ ] = { " sx:name " , " sx:enabled " , " sx:autoCreate " , " sx:autoCreateNotify " ,
" sx:autoCreateDays " , " sx:advanceCreateDays " , " sx:advanceRemindDays " ,
" sx:instanceCount " , " sx:num-occur " ,
" sx:rem-occur " , " sx:templ-acct " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { NXTSCHD , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_vpStartDate = m_vpLastDate = m_vpEndDate = NULL ;
m_vpFreqSpec = NULL ;
m_vpRecurrence . clear ( ) ;
m_vpRecurrence . setAutoDelete ( true ) ;
m_vpSchedDef = NULL ;
}
GncSchedule : : ~ GncSchedule ( ) {
delete m_vpStartDate ; delete m_vpLastDate ; delete m_vpEndDate ; delete m_vpFreqSpec ; delete m_vpSchedDef ;
}
GncObject * GncSchedule : : startSubEl ( ) {
if ( pMain - > xmldebug ) tqDebug ( " Schedule start subel m_state %d " , m_state ) ;
TRY
GncObject * next = 0 ;
switch ( m_state ) {
case STARTDATE :
case LASTDATE :
case ENDDATE : next = new GncDate ; break ;
case FREQ : next = new GncFreqSpec ; break ;
case RECURRENCE : next = new GncRecurrence ; break ;
case DEFINST : next = new GncSchedDef ; break ;
default : throw new MYMONEYEXCEPTION ( " GncSchedule rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncSchedule : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Schedule end subel " ) ;
switch ( m_state ) {
case STARTDATE : m_vpStartDate = static_cast < GncDate * > ( subObj ) ; break ;
case LASTDATE : m_vpLastDate = static_cast < GncDate * > ( subObj ) ; break ;
case ENDDATE : m_vpEndDate = static_cast < GncDate * > ( subObj ) ; break ;
case FREQ : m_vpFreqSpec = static_cast < GncFreqSpec * > ( subObj ) ; break ;
case RECURRENCE : m_vpRecurrence . append ( static_cast < GncRecurrence * > ( subObj ) ) ; break ;
case DEFINST : m_vpSchedDef = static_cast < GncSchedDef * > ( subObj ) ; break ;
}
return ;
}
void GncSchedule : : terminate ( ) {
TRY
pMain - > convertSchedule ( this ) ;
return ;
PASS
}
//************* GncFreqSpec********************************************
GncFreqSpec : : GncFreqSpec ( ) {
m_subElementListCount = END_FreqSpec_SELS ;
static const TQString subEls [ ] = { " gnc:freqspec " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_FreqSpec_DELS ;
static const TQString dataEls [ ] = { " fs:ui_type " , " fs:monthly " , " fs:daily " , " fs:weekly " , " fs:interval " ,
" fs:offset " , " fs:day " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , ASIS , ASIS , ASIS , ASIS , ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
m_fsList . setAutoDelete ( true ) ;
}
GncFreqSpec : : ~ GncFreqSpec ( ) { }
GncObject * GncFreqSpec : : startSubEl ( ) {
TRY
if ( pMain - > xmldebug ) tqDebug ( " FreqSpec start subel m_state %d " , m_state ) ;
GncObject * next = 0 ;
switch ( m_state ) {
case COMPO : next = new GncFreqSpec ; break ;
default : throw new MYMONEYEXCEPTION ( " GncFreqSpec rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncFreqSpec : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " FreqSpec end subel " ) ;
switch ( m_state ) {
case COMPO : m_fsList . append ( subObj ) ; break ;
}
m_dataPtr = 0 ;
return ;
}
void GncFreqSpec : : terminate ( ) {
pMain - > convertFreqSpec ( this ) ;
return ;
}
//************* GncRecurrence********************************************
GncRecurrence : : GncRecurrence ( ) {
m_subElementListCount = END_Recurrence_SELS ;
static const TQString subEls [ ] = { " recurrence:start " } ;
m_subElementList = subEls ;
m_dataElementListCount = END_Recurrence_DELS ;
static const TQString dataEls [ ] = { " recurrence:mult " , " recurrence:period_type " } ;
m_dataElementList = dataEls ;
static const unsigned int anonClasses [ ] = { ASIS , ASIS } ;
m_anonClassList = anonClasses ;
for ( uint i = 0 ; i < m_dataElementListCount ; i + + ) m_v . append ( new TQString ( " " ) ) ;
}
GncRecurrence : : ~ GncRecurrence ( ) {
delete m_vpStartDate ;
}
GncObject * GncRecurrence : : startSubEl ( ) {
TRY
if ( pMain - > xmldebug ) tqDebug ( " Recurrence start subel m_state %d " , m_state ) ;
GncObject * next = 0 ;
switch ( m_state ) {
case STARTDATE : next = new GncDate ; break ;
default : throw new MYMONEYEXCEPTION ( " GncRecurrence rcvd invalid m_state " ) ;
}
return ( next ) ;
PASS
}
void GncRecurrence : : endSubEl ( GncObject * subObj ) {
if ( pMain - > xmldebug ) tqDebug ( " Recurrence end subel " ) ;
switch ( m_state ) {
case STARTDATE : m_vpStartDate = static_cast < GncDate * > ( subObj ) ; break ;
}
m_dataPtr = 0 ;
return ;
}
void GncRecurrence : : terminate ( ) {
pMain - > convertRecurrence ( this ) ;
return ;
}
TQString GncRecurrence : : getFrequency ( ) const {
// This function converts a gnucash 2.2 recurrence specification into it's previous equivalent
// This will all need re-writing when MTE finishes the schedule re-write
if ( periodType ( ) = = " once " ) return ( " once " ) ;
if ( ( periodType ( ) = = " day " ) and ( mult ( ) = = " 1 " ) ) return ( " daily " ) ;
if ( periodType ( ) = = " week " ) {
if ( mult ( ) = = " 1 " ) return ( " weekly " ) ;
if ( mult ( ) = = " 2 " ) return ( " bi_weekly " ) ;
if ( mult ( ) = = " 4 " ) return ( " four-weekly " ) ;
}
if ( periodType ( ) = = " month " ) {
if ( mult ( ) = = " 1 " ) return ( " monthly " ) ;
if ( mult ( ) = = " 2 " ) return ( " two-monthly " ) ;
if ( mult ( ) = = " 3 " ) return ( " quarterly " ) ;
if ( mult ( ) = = " 4 " ) return ( " tri_annually " ) ;
if ( mult ( ) = = " 6 " ) return ( " semi_yearly " ) ;
if ( mult ( ) = = " 12 " ) return ( " yearly " ) ;
if ( mult ( ) = = " 24 " ) return ( " two-yearly " ) ;
}
return ( " unknown " ) ;
}
//************* GncSchedDef********************************************
GncSchedDef : : GncSchedDef ( ) {
// process ing for this sub-object is undefined at the present time
m_subElementListCount = 0 ;
m_dataElementListCount = 0 ;
}
GncSchedDef : : ~ GncSchedDef ( ) { }
/************************************************************************************************
XML Reader
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void XmlReader : : processFile ( TQIODevice * pDevice ) {
m_source = new TQXmlInputSource ( pDevice ) ; // set up the TQt XML reader
m_reader = new TQXmlSimpleReader ;
m_reader - > setContentHandler ( this ) ;
// go read the file
if ( ! m_reader - > parse ( m_source ) ) {
throw new MYMONEYEXCEPTION ( i18n ( " Input file cannot be parsed; may be corrupt \n %s " , errorString ( ) . latin1 ( ) ) ) ;
}
delete m_reader ;
delete m_source ;
return ;
}
// XML handling routines
bool XmlReader : : startDocument ( ) {
m_os . setAutoDelete ( true ) ;
m_co = new GncFile ; // create initial object, push to stack , pass it the 'main' pointer
m_os . push ( m_co ) ;
m_co - > setPm ( pMain ) ;
m_headerFound = false ;
# ifdef _GNCFILEANON
pMain - > oStream < < " <?xml version= \" 1.0 \" ?> " ;
lastType = - 1 ;
indentCount = 0 ;
# endif // _GNCFILEANON
return ( true ) ;
}
bool XmlReader : : startElement ( const TQString & , const TQString & , const TQString & elName ,
const TQXmlAttributes & elAttrs ) {
try {
if ( pMain - > gncdebug ) tqDebug ( " XML start - %s " , elName . latin1 ( ) ) ;
# ifdef _GNCFILEANON
int i ;
TQString spaces ;
// anonymizer - write data
if ( elName = = " gnc:book " | | elName = = " gnc:count-data " | | elName = = " book:id " ) lastType = - 1 ;
pMain - > oStream < < endl ;
switch ( lastType ) {
case 0 :
indentCount + = 2 ;
// tricky fall through here
case 2 :
spaces . fill ( ' ' , indentCount ) ;
pMain - > oStream < < spaces . latin1 ( ) ;
break ;
}
pMain - > oStream < < ' < ' < < elName ;
for ( i = 0 ; i < elAttrs . count ( ) ; i + + ) {
pMain - > oStream < < ' ' < < elAttrs . qName ( i ) < < ' = ' < < ' " ' < < elAttrs . value ( i ) < < ' " ' ;
}
pMain - > oStream < < ' > ' ;
lastType = 0 ;
# else
if ( ( ! m_headerFound ) & & ( elName ! = " gnc-v2 " ) )
throw new MYMONEYEXCEPTION ( i18n ( " Invalid header for file. Should be 'gnc-v2' " ) ) ;
m_headerFound = true ;
# endif // _GNCFILEANON
m_co - > checkVersion ( elName , elAttrs , pMain - > m_versionList ) ;
// check if this is a sub object element; if so, push stack and initialize
GncObject * temp = m_co - > isSubElement ( elName , elAttrs ) ;
if ( temp ! = 0 ) {
m_os . push ( temp ) ;
m_co = m_os . top ( ) ;
m_co - > setVersion ( elAttrs . value ( " version " ) ) ;
m_co - > setPm ( pMain ) ; // pass the 'main' pointer to the sub object
// return true; // removed, as we hit a return true anyway
}
#if 0
// check for a data element
if ( m_co - > isDataElement ( elName , elAttrs ) )
return ( true ) ;
# endif
else {
// reduced the above to
m_co - > isDataElement ( elName , elAttrs ) ;
}
} catch ( MyMoneyException * e ) {
# ifndef _GNCFILEANON
// we can't pass on exceptions here coz the XML reader won't catch them and we just abort
KMessageBox : : error ( 0 , i18n ( " Import failed: \n \n %1 " ) . arg ( e - > what ( ) ) , PACKAGE ) ;
tqFatal ( " %s " , e - > what ( ) . latin1 ( ) ) ;
# else
tqFatal ( " %s " , e - > latin1 ( ) ) ;
# endif // _GNCFILEANON
}
return true ; // to keep compiler happy
}
bool XmlReader : : endElement ( const TQString & , const TQString & , const TQString & elName ) {
try {
if ( pMain - > xmldebug ) tqDebug ( " XML end - %s " , elName . latin1 ( ) ) ;
# ifdef _GNCFILEANON
TQString spaces ;
switch ( lastType ) {
case 2 :
indentCount - = 2 ; spaces . fill ( ' ' , indentCount ) ; pMain - > oStream < < endl < < spaces . latin1 ( ) ; break ;
}
pMain - > oStream < < " </ " < < elName < < ' > ' ;
lastType = 2 ;
# endif // _GNCFILEANON
m_co - > resetDataPtr ( ) ; // so we don't get extraneous data loaded into the variables
if ( elName = = m_co - > getElName ( ) ) { // check if this is the end of the current object
if ( pMain - > gncdebug ) m_co - > debugDump ( ) ; // dump the object data (temp)
// call the terminate routine, pop the stack, and advise the parent that it's done
m_co - > terminate ( ) ;
GncObject * temp = m_co ;
m_os . pop ( ) ;
m_co = m_os . top ( ) ;
m_co - > endSubEl ( temp ) ;
}
return ( true ) ;
} catch ( MyMoneyException * e ) {
# ifndef _GNCFILEANON
// we can't pass on exceptions here coz the XML reader won't catch them and we just abort
KMessageBox : : error ( 0 , i18n ( " Import failed: \n \n %1 " ) . arg ( e - > what ( ) ) , PACKAGE ) ;
tqFatal ( " %s " , e - > what ( ) . latin1 ( ) ) ;
# else
tqFatal ( " %s " , e - > latin1 ( ) ) ;
# endif // _GNCFILEANON
}
return ( true ) ; // to keep compiler happy
}
bool XmlReader : : characters ( const TQString & data ) {
if ( pMain - > xmldebug ) tqDebug ( " XML Data received - %d bytes " , data . length ( ) ) ;
TQString pData = data . stripWhiteSpace ( ) ; // data may contain line feeds and indentation spaces
if ( ! pData . isEmpty ( ) ) {
if ( pMain - > developerDebug ) tqDebug ( " XML Data - %s " , pData . latin1 ( ) ) ;
m_co - > storeData ( pData ) ; //go store it
# ifdef _GNCFILEANON
TQString anonData = m_co - > getData ( ) ;
if ( anonData . isEmpty ( ) ) anonData = pData ;
// there must be a TQt standard way of doing the following but I can't ... find it
anonData . replace ( ' < ' , " < " ) ;
anonData . replace ( ' > ' , " > " ) ;
anonData . replace ( ' & ' , " & " ) ;
pMain - > oStream < < anonData ; // write original data
lastType = 1 ;
# endif // _GNCFILEANON
}
return ( true ) ;
}
bool XmlReader : : endDocument ( ) {
# ifdef _GNCFILEANON
pMain - > oStream < < endl < < endl ;
pMain - > oStream < < " <!-- Local variables: --> " < < endl ;
pMain - > oStream < < " <!-- mode: xml --> " < < endl ;
pMain - > oStream < < " <!-- End: --> " < < endl ;
# endif // _GNCFILEANON
return ( true ) ;
}
/*******************************************************************************************
Main class for this module
Controls overall operation of the importer
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//***************** Constructor ***********************
MyMoneyGncReader : : MyMoneyGncReader ( ) {
# ifndef _GNCFILEANON
m_storage = NULL ;
m_messageList . setAutoDelete ( true ) ;
m_templateList . setAutoDelete ( true ) ;
# endif // _GNCFILEANON
// to hold gnucash count data (only used for progress bar)
m_gncCommodityCount = m_gncAccountCount = m_gncTransactionCount = m_gncScheduleCount = 0 ;
m_smallBusinessFound = m_budgetsFound = m_lotsFound = false ;
m_commodityCount = m_priceCount = m_accountCount = m_transactionCount = m_templateCount = m_scheduleCount = 0 ;
m_decoder = 0 ;
// build a list of valid versions
static const TQString versionList [ ] = { " gnc:book 2.0.0 " , " gnc:commodity 2.0.0 " , " gnc:pricedb 1 " ,
" gnc:account 2.0.0 " , " gnc:transaction 2.0.0 " , " gnc:schedxaction 1.0.0 " ,
" gnc:schedxaction 2.0.0 " , // for gnucash 2.2 onward
" gnc:freqspec 1.0.0 " , " zzz " // zzz = stopper
} ;
unsigned int i ;
for ( i = 0 ; versionList [ i ] ! = " zzz " ; + + i )
m_versionList [ versionList [ i ] . section ( ' ' , 0 , 0 ) ] . append ( versionList [ i ] . section ( ' ' , 1 , 1 ) ) ;
}
//***************** Destructor *************************
MyMoneyGncReader : : ~ MyMoneyGncReader ( ) { }
//**************************** Main Entry Point ************************************
# ifndef _GNCFILEANON
void MyMoneyGncReader : : readFile ( TQIODevice * pDevice , IMyMoneySerialize * storage ) {
TQ_CHECK_PTR ( pDevice ) ;
TQ_CHECK_PTR ( storage ) ;
m_storage = dynamic_cast < IMyMoneyStorage * > ( storage ) ;
tqDebug ( " Entering gnucash importer " ) ;
setOptions ( ) ;
// get a file anonymization factor from the user
if ( bAnonymize ) setFileHideFactor ( ) ;
//m_defaultPayee = createPayee (i18n("Unknown payee"));
MyMoneyFileTransaction ft ;
m_xr = new XmlReader ( this ) ;
try {
m_xr - > processFile ( pDevice ) ;
terminate ( ) ; // do all the wind-up things
ft . commit ( ) ;
} catch ( MyMoneyException * e ) {
KMessageBox : : error ( 0 , i18n ( " Import failed: \n \n %1 " ) . arg ( e - > what ( ) ) , PACKAGE ) ;
tqFatal ( " %s " , e - > what ( ) . latin1 ( ) ) ;
} // end catch
signalProgress ( 0 , 1 , i18n ( " Import complete " ) ) ; // switch off progress bar
delete m_xr ;
tqDebug ( " Exiting gnucash importer " ) ;
return ;
}
# else
// Control code for the file anonymizer
void MyMoneyGncReader : : readFile ( TQString in , TQString out ) {
TQFile pDevice ( in ) ;
if ( ! pDevice . open ( IO_ReadOnly ) ) tqFatal ( " Can't open input file " ) ;
TQFile outFile ( out ) ;
if ( ! outFile . open ( IO_WriteOnly ) ) tqFatal ( " Can't open output file " ) ;
oStream . setDevice ( & outFile ) ;
bAnonymize = true ;
// get a file anonymization factor from the user
setFileHideFactor ( ) ;
m_xr = new XmlReader ( this ) ;
try {
m_xr - > processFile ( & pDevice ) ;
} catch ( MyMoneyException * e ) {
tqFatal ( " %s " , e - > latin1 ( ) ) ;
} // end catch
delete m_xr ;
pDevice . close ( ) ;
outFile . close ( ) ;
return ;
}
# include <tqapplication.h>
int main ( int argc , char * * argv ) {
TQApplication a ( argc , argv ) ;
MyMoneyGncReader m ;
TQString inFile , outFile ;
if ( argc > 0 ) inFile = a . argv ( ) [ 1 ] ;
if ( argc > 1 ) outFile = a . argv ( ) [ 2 ] ;
if ( inFile . isEmpty ( ) ) {
inFile = TQFileDialog : : getOpenFileName ( " " ,
" Gnucash files(*.nc *) " ,
0 ) ;
}
if ( inFile . isEmpty ( ) ) tqFatal ( " Input file required " ) ;
if ( outFile . isEmpty ( ) ) outFile = inFile + " .anon " ;
m . readFile ( inFile , outFile ) ;
exit ( 0 ) ;
}
# endif // _GNCFILEANON
void MyMoneyGncReader : : setFileHideFactor ( ) {
# define MINFILEHIDEF 0.01
# define MAXFILEHIDEF 99.99
srand ( TQTime : : currentTime ( ) . second ( ) ) ; // seed randomizer for anonymize
m_fileHideFactor = 0.0 ;
while ( m_fileHideFactor = = 0.0 ) {
m_fileHideFactor = TQInputDialog : : getDouble (
i18n ( " Disguise your wealth " ) ,
i18n ( " Each monetary value on your file will be multiplied by a random number between 0.01 and 1.99 \n "
" with a different value used for each transaction. In addition, to further disguise the true \n "
" values, you may enter a number between %1 and %2 which will be applied to all values. \n "
" These numbers will not be stored in the file. " ) . arg ( MINFILEHIDEF ) . arg ( MAXFILEHIDEF ) ,
( 1.0 + ( int ) ( 1000.0 * rand ( ) / ( RAND_MAX + 1.0 ) ) ) / 100.0 ,
MINFILEHIDEF , MAXFILEHIDEF , 2 ) ;
}
}
# ifndef _GNCFILEANON
//********************************* convertCommodity *******************************************
void MyMoneyGncReader : : convertCommodity ( const GncCommodity * gcm ) {
TQ_CHECK_PTR ( gcm ) ;
MyMoneySecurity equ ;
if ( m_commodityCount = = 0 ) signalProgress ( 0 , m_gncCommodityCount , i18n ( " Loading commodities... " ) ) ;
if ( ! gcm - > isCurrency ( ) ) { // currencies should not be present here but...
equ . setName ( gcm - > name ( ) ) ;
equ . setTradingSymbol ( gcm - > id ( ) ) ;
equ . setTradingMarket ( gcm - > space ( ) ) ; // the 'space' may be market or quote source, dep on what the user did
// don't set the source here since he may not want quotes
//equ.setValue ("kmm-online-source", gcm->space()); // we don't know, so use it as both
equ . setTradingCurrency ( " " ) ; // not available here, will set from pricedb or transaction
equ . setSecurityType ( MyMoneySecurity : : SECURITY_STOCK ) ; // default to it being a stock
//tell the storage objects we have a new equity object.
equ . setSmallestAccountFraction ( gcm - > fraction ( ) . toInt ( ) ) ;
m_storage - > addSecurity ( equ ) ;
//assign the gnucash id as the key into the map to find our id
if ( gncdebug ) tqDebug ( " mapping, key = %s, id = %s " , gcm - > id ( ) . latin1 ( ) , equ . id ( ) . data ( ) ) ;
m_mapEquities [ gcm - > id ( ) . utf8 ( ) ] = equ . id ( ) ;
}
signalProgress ( + + m_commodityCount , 0 ) ;
return ;
}
//******************************* convertPrice ************************************************
void MyMoneyGncReader : : convertPrice ( const GncPrice * gpr ) {
TQ_CHECK_PTR ( gpr ) ;
// add this to our price history
if ( m_priceCount = = 0 ) signalProgress ( 0 , 1 , i18n ( " Loading prices... " ) ) ;
MyMoneyMoney rate = convBadValue ( gpr - > value ( ) ) ;
if ( gpr - > commodity ( ) - > isCurrency ( ) ) {
MyMoneyPrice exchangeRate ( gpr - > commodity ( ) - > id ( ) . utf8 ( ) , gpr - > currency ( ) - > id ( ) . utf8 ( ) ,
gpr - > priceDate ( ) , rate , i18n ( " Imported History " ) ) ;
m_storage - > addPrice ( exchangeRate ) ;
} else {
MyMoneySecurity e = m_storage - > security ( m_mapEquities [ gpr - > commodity ( ) - > id ( ) . utf8 ( ) ] ) ;
if ( gncdebug ) tqDebug ( " Searching map, key = %s, found id = %s " ,
gpr - > commodity ( ) - > id ( ) . latin1 ( ) , e . id ( ) . data ( ) ) ;
e . setTradingCurrency ( gpr - > currency ( ) - > id ( ) . utf8 ( ) ) ;
MyMoneyPrice stockPrice ( e . id ( ) , gpr - > currency ( ) - > id ( ) . utf8 ( ) , gpr - > priceDate ( ) , rate , i18n ( " Imported History " ) ) ;
m_storage - > addPrice ( stockPrice ) ;
m_storage - > modifySecurity ( e ) ;
}
signalProgress ( + + m_priceCount , 0 ) ;
return ;
}
//*********************************convertAccount ****************************************
void MyMoneyGncReader : : convertAccount ( const GncAccount * gac ) {
TQ_CHECK_PTR ( gac ) ;
TRY
// we don't care about the GNC root account
if ( " ROOT " = = gac - > type ( ) ) {
m_rootId = gac - > id ( ) . utf8 ( ) ;
return ;
}
MyMoneyAccount acc ;
if ( m_accountCount = = 0 ) signalProgress ( 0 , m_gncAccountCount , i18n ( " Loading accounts... " ) ) ;
acc . setName ( gac - > name ( ) ) ;
acc . setDescription ( gac - > desc ( ) ) ;
TQDate currentDate = TQDate : : currentDate ( ) ;
acc . setOpeningDate ( currentDate ) ;
acc . setLastModified ( currentDate ) ;
acc . setLastReconciliationDate ( currentDate ) ;
if ( gac - > commodity ( ) - > isCurrency ( ) ) {
acc . setCurrencyId ( gac - > commodity ( ) - > id ( ) . utf8 ( ) ) ;
m_currencyCount [ gac - > commodity ( ) - > id ( ) ] + + ;
}
acc . setParentAccountId ( gac - > parent ( ) . utf8 ( ) ) ;
// now determine the account type and its parent id
/* This list taken from
# Feb 2006: A RELAX NG Compact schema for gnucash "v2" XML files.
# Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org>
" NO_TYPE " " BANK " " CASH " " CREDIT " " ASSET " " LIABILITY " " STOCK " " MUTUAL " " CURRENCY "
" INCOME " " EXPENSE " " EQUITY " " RECEIVABLE " " PAYABLE " " CHECKING " " SAVINGS " " MONEYMRKT " " CREDITLINE "
Some don ' t seem to be used in practice . Not sure what CREDITLINE s / be converted as .
*/
if ( " BANK " = = gac - > type ( ) | | " CHECKING " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Checkings ) ;
} else if ( " SAVINGS " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Savings ) ;
} else if ( " ASSET " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Asset ) ;
} else if ( " CASH " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Cash ) ;
} else if ( " CURRENCY " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Cash ) ;
} else if ( " STOCK " = = gac - > type ( ) | | " MUTUAL " = = gac - > type ( ) ) {
// gnucash allows a 'broker' account to be denominated as type STOCK, but with
// a currency balance. We do not need to create a stock account for this
// actually, the latest version of gnc (1.8.8) doesn't seem to allow you to do
// this any more, though I do have one in my own account...
if ( gac - > commodity ( ) - > isCurrency ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Investment ) ;
} else {
acc . setAccountType ( MyMoneyAccount : : Stock ) ;
}
} else if ( " EQUITY " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Equity ) ;
} else if ( " LIABILITY " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Liability ) ;
} else if ( " CREDIT " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : CreditCard ) ;
} else if ( " INCOME " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Income ) ;
} else if ( " EXPENSE " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Expense ) ;
} else if ( " RECEIVABLE " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Asset ) ;
} else if ( " PAYABLE " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : Liability ) ;
} else if ( " MONEYMRKT " = = gac - > type ( ) ) {
acc . setAccountType ( MyMoneyAccount : : MoneyMarket ) ;
} else { // we have here an account type we can't currently handle
TQString em =
i18n ( " Current importer does not recognize GnuCash account type %1 " ) . arg ( gac - > type ( ) ) ;
throw new MYMONEYEXCEPTION ( em ) ;
}
// if no parent account is present, assign to one of our standard accounts
if ( ( acc . parentAccountId ( ) . isEmpty ( ) ) | | ( acc . parentAccountId ( ) = = m_rootId ) ) {
switch ( acc . accountGroup ( ) ) {
case MyMoneyAccount : : Asset : acc . setParentAccountId ( m_storage - > asset ( ) . id ( ) ) ; break ;
case MyMoneyAccount : : Liability : acc . setParentAccountId ( m_storage - > liability ( ) . id ( ) ) ; break ;
case MyMoneyAccount : : Income : acc . setParentAccountId ( m_storage - > income ( ) . id ( ) ) ; break ;
case MyMoneyAccount : : Expense : acc . setParentAccountId ( m_storage - > expense ( ) . id ( ) ) ; break ;
case MyMoneyAccount : : Equity : acc . setParentAccountId ( m_storage - > equity ( ) . id ( ) ) ; break ;
default : break ; // not necessary but avoids compiler warnings
}
}
// extra processing for a stock account
if ( acc . accountType ( ) = = MyMoneyAccount : : Stock ) {
// save the id for later linking to investment account
m_stockList . append ( gac - > id ( ) ) ;
// set the equity type
MyMoneySecurity e = m_storage - > security ( m_mapEquities [ gac - > commodity ( ) - > id ( ) . utf8 ( ) ] ) ;
if ( gncdebug ) tqDebug ( " Acct equity search, key = %s, found id = %s " ,
gac - > commodity ( ) - > id ( ) . latin1 ( ) , e . id ( ) . data ( ) ) ;
acc . setCurrencyId ( e . id ( ) ) ; // actually, the security id
if ( " MUTUAL " = = gac - > type ( ) ) {
e . setSecurityType ( MyMoneySecurity : : SECURITY_MUTUALFUND ) ;
if ( gncdebug ) tqDebug ( " Setting %s to mutual " , e . name ( ) . latin1 ( ) ) ;
m_storage - > modifySecurity ( e ) ;
}
// See if he wants online quotes for this account
// NB: In gnc, this selection is per account, in KMM, per security
// This is unlikely to cause problems in practice. If it does,
// we probably need to introduce a 'pricing basis' in the account class
TQPtrListIterator < GncObject > kvpi ( gac - > m_kvpList ) ;
GncKvp * k ;
while ( ( k = static_cast < GncKvp * > ( kvpi . current ( ) ) ) ! = 0 ) {
if ( k - > key ( ) . contains ( " price-source " ) & & k - > type ( ) = = " string " ) {
getPriceSource ( e , k - > value ( ) ) ;
break ;
} else {
+ + kvpi ;
}
}
}
// check for tax-related status
TQPtrListIterator < GncObject > kvpi ( gac - > m_kvpList ) ;
GncKvp * k ;
while ( ( k = static_cast < GncKvp * > ( kvpi . current ( ) ) ) ! = 0 ) {
if ( k - > key ( ) . contains ( " tax-related " ) & & k - > type ( ) = = " integer " & & k - > value ( ) = = " 1 " ) {
acc . setValue ( " Tax " , " Yes " ) ;
break ;
} else {
+ + kvpi ;
}
}
// all the details from the file about the account should be known by now.
// calling addAccount will automatically fill in the account ID.
m_storage - > addAccount ( acc ) ;
m_mapIds [ gac - > id ( ) . utf8 ( ) ] = acc . id ( ) ; // to link gnucash id to ours for tx posting
if ( gncdebug ) tqDebug ( " Gnucash account %s has id of %s, type of %s, parent is %s " ,
gac - > id ( ) . latin1 ( ) , acc . id ( ) . data ( ) ,
KMyMoneyUtils : : accountTypeToString ( acc . accountType ( ) ) . latin1 ( ) , acc . parentAccountId ( ) . data ( ) ) ;
signalProgress ( + + m_accountCount , 0 ) ;
return ;
PASS
}
//********************************************** convertTransaction *****************************
void MyMoneyGncReader : : convertTransaction ( const GncTransaction * gtx ) {
TQ_CHECK_PTR ( gtx ) ;
MyMoneyTransaction tx ;
MyMoneySplit split ;
unsigned int i ;
if ( m_transactionCount = = 0 ) signalProgress ( 0 , m_gncTransactionCount , i18n ( " Loading transactions... " ) ) ;
// initialize class variables related to transactions
m_txCommodity = " " ;
m_txPayeeId = " " ;
m_potentialTransfer = true ;
m_splitList . clear ( ) ; m_liabilitySplitList . clear ( ) ; m_otherSplitList . clear ( ) ;
// payee, dates, commodity
if ( ! gtx - > desc ( ) . isEmpty ( ) ) m_txPayeeId = createPayee ( gtx - > desc ( ) ) ;
tx . setEntryDate ( gtx - > dateEntered ( ) ) ;
tx . setPostDate ( gtx - > datePosted ( ) ) ;
m_txDatePosted = tx . postDate ( ) ; // save for use in splits
m_txChequeNo = gtx - > no ( ) ; // ditto
tx . setCommodity ( gtx - > currency ( ) . utf8 ( ) ) ;
m_txCommodity = tx . commodity ( ) ; // save in storage, maybe needed for Orphan accounts
// process splits
for ( i = 0 ; i < gtx - > splitCount ( ) ; i + + ) {
convertSplit ( static_cast < const GncSplit * > ( gtx - > getSplit ( i ) ) ) ;
}
// handle the odd case of just one split, which gnc allows,
// by just duplicating the split
// of course, we should change the sign but this case has only ever been seen
// when the balance is zero, and can cause kmm to crash, so...
if ( gtx - > splitCount ( ) = = 1 ) {
convertSplit ( static_cast < const GncSplit * > ( gtx - > getSplit ( 0 ) ) ) ;
}
m_splitList + = m_liabilitySplitList + = m_otherSplitList ;
// the splits are in order in splitList. Link them to the tx. also, determine the
// action type, and fill in some fields which gnc holds at transaction level
// first off, is it a transfer (can only have 2 splits?)
// also, a tx with just 2 splits is shown by GnuCash as non-split
bool nonSplitTx = true ;
if ( m_splitList . count ( ) ! = 2 ) {
m_potentialTransfer = false ;
nonSplitTx = false ;
}
for ( i = 0 ; i < gtx - > kvpCount ( ) ; i + + ) {
const GncKvp * slot = gtx - > getKvp ( i ) ;
if ( slot - > key ( ) = = " notes " ) tx . setMemo ( slot - > value ( ) ) ;
}
TQValueList < MyMoneySplit > : : iterator it = m_splitList . begin ( ) ;
while ( ! m_splitList . isEmpty ( ) ) {
split = * it ;
// at this point, if m_potentialTransfer is still true, it is actually one!
if ( m_potentialTransfer ) split . setAction ( MyMoneySplit : : ActionTransfer ) ;
if ( ( m_useTxNotes ) // if use txnotes option is set
& & ( nonSplitTx ) // and it's a (GnuCash) non-split transaction
& & ( ! tx . memo ( ) . isEmpty ( ) ) ) // and tx notes are present
split . setMemo ( tx . memo ( ) ) ; // use the tx notes as memo
tx . addSplit ( split ) ;
it = m_splitList . remove ( it ) ;
}
// memo - set from split - not any more
//tx.setMemo(txMemo);
m_storage - > addTransaction ( tx , true ) ; // all done, add the transaction to storage
signalProgress ( + + m_transactionCount , 0 ) ;
return ;
}
//******************************************convertSplit********************************
void MyMoneyGncReader : : convertSplit ( const GncSplit * gsp ) {
TQ_CHECK_PTR ( gsp ) ;
MyMoneySplit split ;
MyMoneyAccount splitAccount ;
// find the kmm account id coresponding to the gnc id
TQString kmmAccountId ;
map_accountIds : : Iterator id = m_mapIds . find ( gsp - > acct ( ) . utf8 ( ) ) ;
if ( id ! = m_mapIds . end ( ) ) {
kmmAccountId = id . data ( ) ;
} else { // for the case where the acs not found (which shouldn't happen?), create an account with gnc name
kmmAccountId = createOrphanAccount ( gsp - > acct ( ) ) ;
}
// find the account pointer and save for later
splitAccount = m_storage - > account ( kmmAccountId ) ;
// print some data so we can maybe identify this split later
// TODO : prints personal data
//if (gncdebug) tqDebug ("Split data - gncid %s, kmmid %s, memo %s, value %s, recon state %s",
// gsp->acct().latin1(), kmmAccountId.data(), gsp->memo().latin1(), gsp->value().latin1(),
// gsp->recon().latin1());
// payee id
split . setPayeeId ( m_txPayeeId . utf8 ( ) ) ;
// reconciled state and date
switch ( gsp - > recon ( ) . at ( 0 ) . latin1 ( ) ) {
case ' n ' :
split . setReconcileFlag ( MyMoneySplit : : NotReconciled ) ; break ;
case ' c ' :
split . setReconcileFlag ( MyMoneySplit : : Cleared ) ; break ;
case ' y ' :
split . setReconcileFlag ( MyMoneySplit : : Reconciled ) ; break ;
}
split . setReconcileDate ( gsp - > reconDate ( ) ) ;
// memo
split . setMemo ( gsp - > memo ( ) ) ;
// accountId
split . setAccountId ( kmmAccountId ) ;
// cheque no
split . setNumber ( m_txChequeNo ) ;
// value and quantity
MyMoneyMoney splitValue ( convBadValue ( gsp - > value ( ) ) ) ;
if ( gsp - > value ( ) = = " -1/0 " ) { // treat gnc invalid value as zero
// it's not quite a consistency check, but easier to treat it as such
postMessage ( " CC " , 4 , splitAccount . name ( ) . latin1 ( ) , TQString ( m_txDatePosted . toString ( Qt : : ISODate ) ) . latin1 ( ) ) ;
}
MyMoneyMoney splitQuantity ( convBadValue ( gsp - > qty ( ) ) ) ;
split . setValue ( splitValue ) ;
// if split currency = tx currency, set shares = value (14/10/05)
if ( splitAccount . currencyId ( ) = = m_txCommodity ) {
split . setShares ( splitValue ) ;
} else {
split . setShares ( splitQuantity ) ;
}
// in kmm, the first split is important. in this routine we will
// save the splits in our split list with the priority:
// 1. assets
// 2. liabilities
// 3. others (categories)
// but keeping each in same order as gnucash
MyMoneySecurity e ;
MyMoneyMoney price , newPrice ( 0 ) ;
switch ( splitAccount . accountGroup ( ) ) {
case MyMoneyAccount : : Asset :
if ( splitAccount . accountType ( ) = = MyMoneyAccount : : Stock ) {
split . value ( ) = = MyMoneyMoney ( 0 ) ?
split . setAction ( MyMoneySplit : : ActionAddShares ) : // free shares?
split . setAction ( MyMoneySplit : : ActionBuyShares ) ;
m_potentialTransfer = false ; // ?
// add a price history entry
e = m_storage - > security ( splitAccount . currencyId ( ) ) ;
// newPrice fix supplied by Phil Longstaff
price = split . value ( ) / split . shares ( ) ;
# define NEW_DENOM 10000
if ( ! split . shares ( ) . isZero ( ) ) // patch to fix divide by zero?
newPrice = MyMoneyMoney ( price . toDouble ( ) , ( signed64 ) NEW_DENOM ) ;
if ( ! newPrice . isZero ( ) ) {
TRY
// we can't use m_storage->security coz security list is not built yet
m_storage - > currency ( m_txCommodity ) ; // will throw exception if not currency
e . setTradingCurrency ( m_txCommodity ) ;
if ( gncdebug ) tqDebug ( " added price for %s, %s date %s " ,
e . name ( ) . latin1 ( ) , newPrice . toString ( ) . latin1 ( ) ,
TQString ( m_txDatePosted . toString ( Qt : : ISODate ) ) . latin1 ( ) ) ;
m_storage - > modifySecurity ( e ) ;
MyMoneyPrice dealPrice ( e . id ( ) , m_txCommodity , m_txDatePosted , newPrice , i18n ( " Imported Transaction " ) ) ;
m_storage - > addPrice ( dealPrice ) ;
CATCH // stock transfer; treat like free shares?
split . setAction ( MyMoneySplit : : ActionAddShares ) ;
delete e ;
}
}
} else { // not stock
if ( split . value ( ) . isNegative ( ) ) {
bool isNumeric = false ;
if ( ! split . number ( ) . isEmpty ( ) ) {
split . number ( ) . toLong ( & isNumeric ) ; // No TQString.isNumeric()??
}
if ( isNumeric ) {
split . setAction ( MyMoneySplit : : ActionCheck ) ;
} else {
split . setAction ( MyMoneySplit : : ActionWithdrawal ) ;
}
} else {
split . setAction ( MyMoneySplit : : ActionDeposit ) ;
}
}
m_splitList . append ( split ) ;
break ;
case MyMoneyAccount : : Liability :
split . value ( ) . isNegative ( ) ?
split . setAction ( MyMoneySplit : : ActionWithdrawal ) :
split . setAction ( MyMoneySplit : : ActionDeposit ) ;
m_liabilitySplitList . append ( split ) ;
break ;
default :
m_potentialTransfer = false ;
m_otherSplitList . append ( split ) ;
}
// backdate the account opening date if necessary
if ( m_txDatePosted < splitAccount . openingDate ( ) ) {
splitAccount . setOpeningDate ( m_txDatePosted ) ;
m_storage - > modifyAccount ( splitAccount ) ;
}
return ;
}
//********************************* convertTemplateTransaction **********************************************
MyMoneyTransaction MyMoneyGncReader : : convertTemplateTransaction ( const TQString & schedName , const GncTransaction * gtx ) {
TQ_CHECK_PTR ( gtx ) ;
MyMoneyTransaction tx ;
MyMoneySplit split ;
unsigned int i ;
if ( m_templateCount = = 0 ) signalProgress ( 0 , 1 , i18n ( " Loading templates... " ) ) ;
// initialize class variables related to transactions
m_txCommodity = " " ;
m_txPayeeId = " " ;
m_potentialTransfer = true ;
m_splitList . clear ( ) ; m_liabilitySplitList . clear ( ) ; m_otherSplitList . clear ( ) ;
// payee, dates, commodity
if ( ! gtx - > desc ( ) . isEmpty ( ) ) {
m_txPayeeId = createPayee ( gtx - > desc ( ) ) ;
} else {
m_txPayeeId = createPayee ( i18n ( " Unknown payee " ) ) ; // schedules require a payee tho normal tx's don't. not sure why...
}
tx . setEntryDate ( gtx - > dateEntered ( ) ) ;
tx . setPostDate ( gtx - > datePosted ( ) ) ;
m_txDatePosted = tx . postDate ( ) ;
tx . setCommodity ( gtx - > currency ( ) . utf8 ( ) ) ;
m_txCommodity = tx . commodity ( ) ; // save for possible use in orphan account
// process splits
for ( i = 0 ; i < gtx - > splitCount ( ) ; i + + ) {
convertTemplateSplit ( schedName , static_cast < const GncTemplateSplit * > ( gtx - > getSplit ( i ) ) ) ;
}
// determine the action type for the splits and link them to the template tx
/*TQString negativeActionType, positiveActionType;
if ( ! m_splitList . isEmpty ( ) ) { // if there are asset splits
positiveActionType = MyMoneySplit : : ActionDeposit ;
negativeActionType = MyMoneySplit : : ActionWithdrawal ;
} else { // if there are liability splits
positiveActionType = MyMoneySplit : : ActionWithdrawal ;
negativeActionType = MyMoneySplit : : ActionDeposit ;
} */
if ( ! m_otherSplitList . isEmpty ( ) ) m_potentialTransfer = false ; // tfrs can occur only between assets and asset/liabilities
m_splitList + = m_liabilitySplitList + = m_otherSplitList ;
// the splits are in order in splitList. Transfer them to the tx
// also, determine the action type. first off, is it a transfer (can only have 2 splits?)
if ( m_splitList . count ( ) ! = 2 ) m_potentialTransfer = false ;
// at this point, if m_potentialTransfer is still true, it is actually one!
TQString txMemo = " " ;
TQValueList < MyMoneySplit > : : iterator it = m_splitList . begin ( ) ;
while ( ! m_splitList . isEmpty ( ) ) {
split = * it ;
if ( m_potentialTransfer ) {
split . setAction ( MyMoneySplit : : ActionTransfer ) ;
} else {
if ( split . value ( ) . isNegative ( ) ) {
//split.setAction (negativeActionType);
split . setAction ( MyMoneySplit : : ActionWithdrawal ) ;
} else {
//split.setAction (positiveActionType);
split . setAction ( MyMoneySplit : : ActionDeposit ) ;
}
}
split . setNumber ( gtx - > no ( ) ) ; // set cheque no (or equivalent description)
// Arbitrarily, save the first non-null split memo as the memo for the whole tx
// I think this is necessary because txs with just 2 splits (the majority)
// are not viewable as split transactions in kmm so the split memo is not seen
if ( ( txMemo . isEmpty ( ) ) & & ( ! split . memo ( ) . isEmpty ( ) ) ) txMemo = split . memo ( ) ;
tx . addSplit ( split ) ;
it = m_splitList . remove ( it ) ;
}
// memo - set from split
tx . setMemo ( txMemo ) ;
signalProgress ( + + m_templateCount , 0 ) ;
return ( tx ) ;
}
//********************************* convertTemplateSplit ****************************************************
void MyMoneyGncReader : : convertTemplateSplit ( const TQString & schedName , const GncTemplateSplit * gsp ) {
TQ_CHECK_PTR ( gsp ) ;
// convertTemplateSplit
MyMoneySplit split ;
MyMoneyAccount splitAccount ;
unsigned int i , j ;
bool nonNumericFormula = false ;
// action, value and account will be set from slots
// reconcile state, always Not since it hasn't even been posted yet (?)
split . setReconcileFlag ( MyMoneySplit : : NotReconciled ) ;
// memo
split . setMemo ( gsp - > memo ( ) ) ;
// payee id
split . setPayeeId ( m_txPayeeId . utf8 ( ) ) ;
// read split slots (KVPs)
int xactionCount = 0 ;
int validSlotCount = 0 ;
TQString gncAccountId ;
for ( i = 0 ; i < gsp - > kvpCount ( ) ; i + + ) {
const GncKvp * slot = gsp - > getKvp ( i ) ;
if ( ( slot - > key ( ) = = " sched-xaction " ) & & ( slot - > type ( ) = = " frame " ) ) {
bool bFoundStringCreditFormula = false ;
bool bFoundStringDebitFormula = false ;
bool bFoundGuidAccountId = false ;
TQString gncCreditFormula , gncDebitFormula ;
for ( j = 0 ; j < slot - > kvpCount ( ) ; j + + ) {
const GncKvp * subSlot = slot - > getKvp ( j ) ;
// again, see comments above. when we have a full specification
// of all the options available to us, we can no doubt improve on this
if ( ( subSlot - > key ( ) = = " credit-formula " ) & & ( subSlot - > type ( ) = = " string " ) ) {
gncCreditFormula = subSlot - > value ( ) ;
bFoundStringCreditFormula = true ;
}
if ( ( subSlot - > key ( ) = = " debit-formula " ) & & ( subSlot - > type ( ) = = " string " ) ) {
gncDebitFormula = subSlot - > value ( ) ;
bFoundStringDebitFormula = true ;
}
if ( ( subSlot - > key ( ) = = " account " ) & & ( subSlot - > type ( ) = = " guid " ) ) {
gncAccountId = subSlot - > value ( ) ;
bFoundGuidAccountId = true ;
}
}
// all data read, now check we have everything
if ( ( bFoundStringCreditFormula ) & & ( bFoundStringDebitFormula ) & & ( bFoundGuidAccountId ) ) {
if ( gncdebug ) tqDebug ( " Found valid slot; credit %s, debit %s, acct %s " ,
gncCreditFormula . latin1 ( ) , gncDebitFormula . latin1 ( ) , gncAccountId . latin1 ( ) ) ;
validSlotCount + + ;
}
// validate numeric, work out sign
MyMoneyMoney exFormula ( 0 ) ;
exFormula . setNegativeMonetarySignPosition ( MyMoneyMoney : : BeforeQuantityMoney ) ;
TQString numericTest ;
char crdr = 0 ;
if ( ! gncCreditFormula . isEmpty ( ) ) {
crdr = ' C ' ;
numericTest = gncCreditFormula ;
} else if ( ! gncDebitFormula . isEmpty ( ) ) {
crdr = ' D ' ;
numericTest = gncDebitFormula ;
}
kMyMoneyMoneyValidator v ( 0 ) ;
int pos ; // useless, but required for validator
if ( v . validate ( numericTest , pos ) = = TQValidator : : Acceptable ) {
switch ( crdr ) {
case ' C ' :
exFormula = TQString ( " - " + numericTest ) ; break ;
case ' D ' :
exFormula = numericTest ;
}
} else {
if ( gncdebug ) tqDebug ( " %s is not numeric " , numericTest . latin1 ( ) ) ;
nonNumericFormula = true ;
}
split . setValue ( exFormula ) ;
xactionCount + + ;
} else {
postMessage ( " SC " , 3 , schedName . latin1 ( ) , slot - > key ( ) . latin1 ( ) , slot - > type ( ) . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
}
// report this as untranslatable tx
if ( xactionCount > 1 ) {
postMessage ( " SC " , 4 , schedName . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
if ( validSlotCount = = 0 ) {
postMessage ( " SC " , 5 , schedName . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
if ( nonNumericFormula ) {
postMessage ( " SC " , 6 , schedName . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
// find the kmm account id coresponding to the gnc id
TQString kmmAccountId ;
map_accountIds : : Iterator id = m_mapIds . find ( gncAccountId . utf8 ( ) ) ;
if ( id ! = m_mapIds . end ( ) ) {
kmmAccountId = id . data ( ) ;
} else { // for the case where the acs not found (which shouldn't happen?), create an account with gnc name
kmmAccountId = createOrphanAccount ( gncAccountId ) ;
}
splitAccount = m_storage - > account ( kmmAccountId ) ;
split . setAccountId ( kmmAccountId ) ;
// if split currency = tx currency, set shares = value (14/10/05)
if ( splitAccount . currencyId ( ) = = m_txCommodity ) {
split . setShares ( split . value ( ) ) ;
} /* else { //FIXME: scheduled currency or investment tx needs to be investigated
split . setShares ( splitQuantity ) ;
} */
// add the split to one of the lists
switch ( splitAccount . accountGroup ( ) ) {
case MyMoneyAccount : : Asset :
m_splitList . append ( split ) ; break ;
case MyMoneyAccount : : Liability :
m_liabilitySplitList . append ( split ) ; break ;
default :
m_otherSplitList . append ( split ) ;
}
// backdate the account opening date if necessary
if ( m_txDatePosted < splitAccount . openingDate ( ) ) {
splitAccount . setOpeningDate ( m_txDatePosted ) ;
m_storage - > modifyAccount ( splitAccount ) ;
}
return ;
}
//********************************* convertSchedule ********************************************************
void MyMoneyGncReader : : convertSchedule ( const GncSchedule * gsc ) {
TRY
TQ_CHECK_PTR ( gsc ) ;
MyMoneySchedule sc ;
MyMoneyTransaction tx ;
m_suspectSchedule = false ;
TQDate startDate , nextDate , lastDate , endDate ; // for date calculations
TQDate today = TQDate : : currentDate ( ) ;
int numOccurs , remOccurs ;
if ( m_scheduleCount = = 0 ) signalProgress ( 0 , m_gncScheduleCount , i18n ( " Loading schedules... " ) ) ;
// schedule name
sc . setName ( gsc - > name ( ) ) ;
// find the transaction template as stored earlier
TQPtrListIterator < GncTransaction > itt ( m_templateList ) ;
GncTransaction * ttx ;
while ( ( ttx = itt . current ( ) ) ! = 0 ) {
// the id to match against is the split:account value in the splits
if ( static_cast < const GncTemplateSplit * > ( ttx - > getSplit ( 0 ) ) - > acct ( ) = = gsc - > templId ( ) ) break ;
+ + itt ;
}
if ( itt = = 0 ) {
throw new MYMONEYEXCEPTION ( i18n ( " Can't find template transaction for schedule %1 " ) . arg ( sc . name ( ) ) ) ;
} else {
tx = convertTemplateTransaction ( sc . name ( ) , * itt ) ;
}
tx . clearId ( ) ;
// define the conversion table for intervals
struct convIntvl {
TQString gncType ; // the gnucash name
unsigned char interval ; // for date calculation
unsigned int intervalCount ;
MyMoneySchedule : : occurenceE occ ; // equivalent occurence code
MyMoneySchedule : : weekendOptionE wo ;
} ;
/* other intervals supported by gnc according to Josh Sled's schema (see above)
" none " " semi_monthly "
*/
/* some of these type names do not appear in gnucash and are difficult to generate for
pre 2.2 files . They can be generated for 2.2 however , by GncRecurrence : : getFrequency ( ) */
static convIntvl vi [ ] = {
{ " once " , ' o ' , 1 , MyMoneySchedule : : OCCUR_ONCE , MyMoneySchedule : : MoveNothing } ,
{ " daily " , ' d ' , 1 , MyMoneySchedule : : OCCUR_DAILY , MyMoneySchedule : : MoveNothing } ,
//{"daily_mf", 'd', 1, MyMoneySchedule::OCCUR_DAILY, MyMoneySchedule::MoveMonday }, doesn't work, need new freq in kmm
{ " 30-days " , ' d ' , 30 , MyMoneySchedule : : OCCUR_EVERYTHIRTYDAYS , MyMoneySchedule : : MoveNothing } ,
{ " weekly " , ' w ' , 1 , MyMoneySchedule : : OCCUR_WEEKLY , MyMoneySchedule : : MoveNothing } ,
{ " bi_weekly " , ' w ' , 2 , MyMoneySchedule : : OCCUR_EVERYOTHERWEEK , MyMoneySchedule : : MoveNothing } ,
{ " three-weekly " , ' w ' , 3 , MyMoneySchedule : : OCCUR_EVERYTHREEWEEKS , MyMoneySchedule : : MoveNothing } ,
{ " four-weekly " , ' w ' , 4 , MyMoneySchedule : : OCCUR_EVERYFOURWEEKS ,
MyMoneySchedule : : MoveNothing } ,
{ " eight-weekly " , ' w ' , 8 , MyMoneySchedule : : OCCUR_EVERYEIGHTWEEKS , MyMoneySchedule : : MoveNothing } ,
{ " monthly " , ' m ' , 1 , MyMoneySchedule : : OCCUR_MONTHLY , MyMoneySchedule : : MoveNothing } ,
{ " two-monthly " , ' m ' , 2 , MyMoneySchedule : : OCCUR_EVERYOTHERMONTH ,
MyMoneySchedule : : MoveNothing } ,
{ " quarterly " , ' m ' , 3 , MyMoneySchedule : : OCCUR_QUARTERLY , MyMoneySchedule : : MoveNothing } ,
{ " tri_annually " , ' m ' , 4 , MyMoneySchedule : : OCCUR_EVERYFOURMONTHS , MyMoneySchedule : : MoveNothing } ,
{ " semi_yearly " , ' m ' , 6 , MyMoneySchedule : : OCCUR_TWICEYEARLY , MyMoneySchedule : : MoveNothing } ,
{ " yearly " , ' y ' , 1 , MyMoneySchedule : : OCCUR_YEARLY , MyMoneySchedule : : MoveNothing } ,
{ " two-yearly " , ' y ' , 2 , MyMoneySchedule : : OCCUR_EVERYOTHERYEAR ,
MyMoneySchedule : : MoveNothing } ,
{ " zzz " , ' y ' , 1 , MyMoneySchedule : : OCCUR_YEARLY , MyMoneySchedule : : MoveNothing }
// zzz = stopper, may cause problems. what else can we do?
} ;
TQString frequency = " unknown " ; // set default to unknown frequency
bool unknownOccurs = false ; // may have zero, or more than one frequency/recurrence spec
TQString schedEnabled ;
if ( gsc - > version ( ) = = " 2.0.0 " ) {
if ( gsc - > m_vpRecurrence . count ( ) ! = 1 ) {
unknownOccurs = true ;
} else {
const GncRecurrence * gre = gsc - > m_vpRecurrence . first ( ) ;
//tqDebug (TQString("Sched %1, pt %2, mu %3, sd %4").arg(gsc->name()).arg(gre->periodType())
// .arg(gre->mult()).arg(gre->startDate().toString(Qt::ISODate)));
frequency = gre - > getFrequency ( ) ;
schedEnabled = gsc - > enabled ( ) ;
}
sc . setOccurence ( MyMoneySchedule : : OCCUR_ONCE ) ; // FIXME - how to convert
} else {
// find this interval
const GncFreqSpec * fs = gsc - > getFreqSpec ( ) ;
if ( fs = = NULL ) {
unknownOccurs = true ;
} else {
frequency = fs - > intervalType ( ) ;
if ( ! fs - > m_fsList . isEmpty ( ) ) unknownOccurs = true ; // nested freqspec
}
schedEnabled = " y " ; // earlier versions did not have an enable flag
}
int i ;
for ( i = 0 ; vi [ i ] . gncType ! = " zzz " ; i + + ) {
if ( frequency = = vi [ i ] . gncType ) break ;
}
if ( vi [ i ] . gncType = = " zzz " ) {
postMessage ( " SC " , 1 , sc . name ( ) . latin1 ( ) , frequency . latin1 ( ) ) ;
i = 0 ; // treat as single occurrence
m_suspectSchedule = true ;
}
if ( unknownOccurs ) {
postMessage ( " SC " , 7 , sc . name ( ) . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
// set the occurrence interval, weekend option, start date
sc . setOccurence ( vi [ i ] . occ ) ;
sc . setWeekendOption ( vi [ i ] . wo ) ;
sc . setStartDate ( gsc - > startDate ( ) ) ;
// if a last date was specified, use it, otherwise try to work out the last date
sc . setLastPayment ( gsc - > lastDate ( ) ) ;
numOccurs = gsc - > numOccurs ( ) . toInt ( ) ;
if ( sc . lastPayment ( ) = = TQDate ( ) ) {
nextDate = lastDate = gsc - > startDate ( ) ;
while ( ( nextDate < today ) & & ( numOccurs - - ! = 0 ) ) {
lastDate = nextDate ;
nextDate = incrDate ( lastDate , vi [ i ] . interval , vi [ i ] . intervalCount ) ;
}
sc . setLastPayment ( lastDate ) ;
}
// under Tom's new regime, the tx dates are the next due date (I think)
tx . setPostDate ( incrDate ( sc . lastPayment ( ) , vi [ i ] . interval , vi [ i ] . intervalCount ) ) ;
tx . setEntryDate ( incrDate ( sc . lastPayment ( ) , vi [ i ] . interval , vi [ i ] . intervalCount ) ) ;
// if an end date was specified, use it, otherwise if the input file had a number
// of occurs remaining, work out the end date
sc . setEndDate ( gsc - > endDate ( ) ) ;
numOccurs = gsc - > numOccurs ( ) . toInt ( ) ;
remOccurs = gsc - > remOccurs ( ) . toInt ( ) ;
if ( ( sc . endDate ( ) = = TQDate ( ) ) & & ( remOccurs > 0 ) ) {
endDate = sc . lastPayment ( ) ;
while ( remOccurs - - > 0 ) {
endDate = incrDate ( endDate , vi [ i ] . interval , vi [ i ] . intervalCount ) ;
}
sc . setEndDate ( endDate ) ;
}
// Check for sched deferred interval. Don't know how/if we can handle it, or even what it means...
if ( gsc - > getSchedDef ( ) ! = NULL ) {
postMessage ( " SC " , 8 , sc . name ( ) . latin1 ( ) ) ;
m_suspectSchedule = true ;
}
// payment type, options
sc . setPaymentType ( ( MyMoneySchedule : : paymentTypeE ) MyMoneySchedule : : STYPE_OTHER ) ;
sc . setFixed ( ! m_suspectSchedule ) ; // if any probs were found, set it as variable so user will always be prompted
// we don't currently have a 'disable' option, but just make sure auto-enter is off if not enabled
//tqDebug(TQString("%1 and %2").arg(gsc->autoCreate()).arg(schedEnabled));
sc . setAutoEnter ( ( gsc - > autoCreate ( ) = = " y " ) & & ( schedEnabled = = " y " ) ) ;
//tqDebug(TQString("autoEnter set to %1").arg(sc.autoEnter()));
// type
TQString actionType = tx . splits ( ) . first ( ) . action ( ) ;
if ( actionType = = MyMoneySplit : : ActionDeposit ) {
sc . setType ( ( MyMoneySchedule : : typeE ) MyMoneySchedule : : TYPE_DEPOSIT ) ;
} else if ( actionType = = MyMoneySplit : : ActionTransfer ) {
sc . setType ( ( MyMoneySchedule : : typeE ) MyMoneySchedule : : TYPE_TRANSFER ) ;
} else {
sc . setType ( ( MyMoneySchedule : : typeE ) MyMoneySchedule : : TYPE_BILL ) ;
}
// finally, set the transaction pointer
sc . setTransaction ( tx ) ;
//tell the storage objects we have a new schedule object.
if ( m_suspectSchedule & & m_dropSuspectSchedules ) {
postMessage ( " SC " , 2 , sc . name ( ) . latin1 ( ) ) ;
} else {
m_storage - > addSchedule ( sc ) ;
if ( m_suspectSchedule )
m_suspectList . append ( sc . id ( ) ) ;
}
signalProgress ( + + m_scheduleCount , 0 ) ;
return ;
PASS
}
//********************************* convertFreqSpec ********************************************************
void MyMoneyGncReader : : convertFreqSpec ( const GncFreqSpec * ) {
// Nowt to do here at the moment, convertSched only retrieves the interval type
// but we will probably need to look into the nested freqspec when we properly implement semi-monthly and stuff
return ;
}
//********************************* convertRecurrence ********************************************************
void MyMoneyGncReader : : convertRecurrence ( const GncRecurrence * ) {
return ;
}
//**********************************************************************************************************
//************************************* terminate **********************************************************
void MyMoneyGncReader : : terminate ( ) {
TRY
// All data has been converted and added to storage
// this code is just temporary to show us what is in the file.
if ( gncdebug ) tqDebug ( " %d accounts found in the GnuCash file " , ( unsigned int ) m_mapIds . count ( ) ) ;
for ( map_accountIds : : Iterator it = m_mapIds . begin ( ) ; it ! = m_mapIds . end ( ) ; + + it ) {
if ( gncdebug ) tqDebug ( " key = %s, value = %s " , it . key ( ) . data ( ) , it . data ( ) . data ( ) ) ;
}
// first step is to implement the users investment option, now we
// have all the accounts available
TQValueList < TQString > : : iterator stocks ;
for ( stocks = m_stockList . begin ( ) ; stocks ! = m_stockList . end ( ) ; + + stocks ) {
checkInvestmentOption ( * stocks ) ;
}
// Next step is to walk the list and assign the parent/child relationship between the objects.
unsigned int i = 0 ;
signalProgress ( 0 , m_accountCount , i18n ( " Reorganizing accounts... " ) ) ;
TQValueList < MyMoneyAccount > list ;
TQValueList < MyMoneyAccount > : : Iterator acc ;
m_storage - > accountList ( list ) ;
for ( acc = list . begin ( ) ; acc ! = list . end ( ) ; + + acc ) {
if ( ( * acc ) . parentAccountId ( ) = = m_storage - > asset ( ) . id ( ) ) {
MyMoneyAccount assets = m_storage - > asset ( ) ;
m_storage - > addAccount ( assets , ( * acc ) ) ;
if ( gncdebug ) tqDebug ( " Account id %s is a child of the main asset account " , ( * acc ) . id ( ) . data ( ) ) ;
} else if ( ( * acc ) . parentAccountId ( ) = = m_storage - > liability ( ) . id ( ) ) {
MyMoneyAccount liabilities = m_storage - > liability ( ) ;
m_storage - > addAccount ( liabilities , ( * acc ) ) ;
if ( gncdebug ) tqDebug ( " Account id %s is a child of the main liability account " , ( * acc ) . id ( ) . data ( ) ) ;
} else if ( ( * acc ) . parentAccountId ( ) = = m_storage - > income ( ) . id ( ) ) {
MyMoneyAccount incomes = m_storage - > income ( ) ;
m_storage - > addAccount ( incomes , ( * acc ) ) ;
if ( gncdebug ) tqDebug ( " Account id %s is a child of the main income account " , ( * acc ) . id ( ) . data ( ) ) ;
} else if ( ( * acc ) . parentAccountId ( ) = = m_storage - > expense ( ) . id ( ) ) {
MyMoneyAccount expenses = m_storage - > expense ( ) ;
m_storage - > addAccount ( expenses , ( * acc ) ) ;
if ( gncdebug ) tqDebug ( " Account id %s is a child of the main expense account " , ( * acc ) . id ( ) . data ( ) ) ;
} else if ( ( * acc ) . parentAccountId ( ) = = m_storage - > equity ( ) . id ( ) ) {
MyMoneyAccount equity = m_storage - > equity ( ) ;
m_storage - > addAccount ( equity , ( * acc ) ) ;
if ( gncdebug ) tqDebug ( " Account id %s is a child of the main equity account " , ( * acc ) . id ( ) . data ( ) ) ;
} else if ( ( * acc ) . parentAccountId ( ) = = m_rootId ) {
if ( gncdebug ) tqDebug ( " Account id %s is a child of root " , ( * acc ) . id ( ) . data ( ) ) ;
} else {
// it is not under one of the main accounts, so find gnucash parent
TQString parentKey = ( * acc ) . parentAccountId ( ) ;
if ( gncdebug ) tqDebug ( " acc %s, parent %s " , ( * acc ) . id ( ) . data ( ) ,
( * acc ) . parentAccountId ( ) . data ( ) ) ;
map_accountIds : : Iterator id = m_mapIds . find ( parentKey ) ;
if ( id ! = m_mapIds . end ( ) ) {
if ( gncdebug ) tqDebug ( " Setting account id %s's parent account id to %s " ,
( * acc ) . id ( ) . data ( ) , id . data ( ) . data ( ) ) ;
MyMoneyAccount parent = m_storage - > account ( id . data ( ) ) ;
parent = checkConsistency ( parent , ( * acc ) ) ;
m_storage - > addAccount ( parent , ( * acc ) ) ;
} else {
throw new MYMONEYEXCEPTION ( " terminate() could not find account id " ) ;
}
}
signalProgress ( + + i , 0 ) ;
} // end for account
signalProgress ( 0 , 1 , ( " . " ) ) ; // debug - get rid of reorg message
// offer the most common account currency as a default
TQString mainCurrency = " " ;
unsigned int maxCount = 0 ;
TQMap < TQString , unsigned int > : : ConstIterator it ;
for ( it = m_currencyCount . begin ( ) ; it ! = m_currencyCount . end ( ) ; + + it ) {
if ( it . data ( ) > maxCount ) {
maxCount = it . data ( ) ;
mainCurrency = it . key ( ) ;
}
}
if ( mainCurrency ! = " " ) {
/* fix for qt3.3.4?. According to TQt docs, this should return the enum id of the button pressed, and
indeed it used to do so . However now it seems to return the index of the button . In this case it doesn ' t matter ,
since for Yes , the id is 3 and the index is 0 , whereas the No button will return 4 or 1. So we test for either Yes case */
/* and now it seems to have changed again, returning 259 for a Yes??? so use KMessagebox */
TQString question = i18n ( " Your main currency seems to be %1 (%2); do you want to set this as your base currency? " )
. arg ( mainCurrency ) . arg ( m_storage - > currency ( mainCurrency . utf8 ( ) ) . name ( ) ) ;
if ( KMessageBox : : questionYesNo ( 0 , question , PACKAGE ) = = KMessageBox : : Yes ) {
m_storage - > setValue ( " kmm-baseCurrency " , mainCurrency ) ;
}
}
// now produce the end of job reports - first, work out which ones are required
m_ccCount = 0 , m_orCount = 0 , m_scCount = 0 ;
for ( i = 0 ; i < m_messageList . count ( ) ; i + + ) {
if ( ( * m_messageList . at ( i ) ) . source = = " CC " ) m_ccCount + + ;
if ( ( * m_messageList . at ( i ) ) . source = = " OR " ) m_orCount + + ;
if ( ( * m_messageList . at ( i ) ) . source = = " SC " ) m_scCount + + ;
}
TQValueList < TQString > sectionsToReport ; // list of sections needing report
sectionsToReport . append ( " MN " ) ; // always build the main section
if ( m_ccCount > 0 ) sectionsToReport . append ( " CC " ) ;
if ( m_orCount > 0 ) sectionsToReport . append ( " OR " ) ;
if ( m_scCount > 0 ) sectionsToReport . append ( " SC " ) ;
// produce the sections in message boxes
bool exit = false ;
for ( i = 0 ; ( i < sectionsToReport . count ( ) ) & & ! exit ; i + + ) {
TQString button0Text = i18n ( " More " ) ;
if ( i + 1 = = sectionsToReport . count ( ) )
button0Text = i18n ( " Done " ) ; // last section
KGuiItem yesItem ( button0Text , TQIconSet ( ) , " " , " " ) ;
KGuiItem noItem ( i18n ( " Save Report " ) , TQIconSet ( ) , " " , " " ) ;
switch ( KMessageBox : : questionYesNoCancel ( 0 ,
buildReportSection ( * sectionsToReport . at ( i ) ) ,
PACKAGE ,
yesItem , noItem ) ) {
case KMessageBox : : Yes :
break ;
case KMessageBox : : No :
exit = writeReportToFile ( sectionsToReport ) ;
break ;
default :
exit = true ;
break ;
}
}
for ( i = 0 ; i < m_suspectList . count ( ) ; i + + ) {
MyMoneySchedule sc = m_storage - > schedule ( m_suspectList [ i ] ) ;
KEditScheduleDlg * s ;
switch ( KMessageBox : : warningYesNo ( 0 , i18n ( " Problems were encountered in converting schedule '%1'. \n Do you want to review or edit it now? " ) . arg ( sc . name ( ) ) , PACKAGE ) ) {
case KMessageBox : : Yes :
s = new KEditScheduleDlg ( sc ) ;
// FIXME: connect newCategory to something useful, so that we
// can create categories from within the dialog
if ( s - > exec ( ) )
m_storage - > modifySchedule ( s - > schedule ( ) ) ;
delete s ;
break ;
default :
break ;
}
}
PASS
}
//************************************ buildReportSection************************************
TQString MyMoneyGncReader : : buildReportSection ( const TQString & source ) {
TRY
TQString s = " " ;
bool more = false ;
if ( source = = " MN " ) {
s . append ( i18n ( " Found: \n \n " ) ) ;
s . append ( TQString : : number ( m_commodityCount ) + i18n ( " commodities (equities) \n " ) ) ;
s . append ( TQString : : number ( m_priceCount ) + i18n ( " prices \n " ) ) ;
s . append ( TQString : : number ( m_accountCount ) + i18n ( " accounts \n " ) ) ;
s . append ( TQString : : number ( m_transactionCount ) + i18n ( " transactions \n " ) ) ;
s . append ( TQString : : number ( m_scheduleCount ) + i18n ( " schedules \n " ) ) ;
s . append ( " \n \n " ) ;
if ( m_ccCount = = 0 ) {
s . append ( i18n ( " No inconsistencies were detected " ) ) ;
} else {
s . append ( TQString : : number ( m_ccCount ) + i18n ( " inconsistencies were detected and corrected \n " ) ) ;
more = true ;
}
if ( m_orCount > 0 ) {
s . append ( " \n \n " ) ;
s . append ( TQString : : number ( m_orCount ) + i18n ( " orphan accounts were created \n " ) ) ;
more = true ;
}
if ( m_scCount > 0 ) {
s . append ( " \n \n " ) ;
s . append ( TQString : : number ( m_scCount ) + i18n ( " possible schedule problems were noted \n " ) ) ;
more = true ;
}
TQString unsupported ( " " ) ;
TQString lineSep ( " \n - " ) ;
if ( m_smallBusinessFound ) unsupported . append ( lineSep + i18n ( " Small Business Features (Customers, Invoices, etc.) " ) ) ;
if ( m_budgetsFound ) unsupported . append ( lineSep + i18n ( " Budgets " ) ) ;
if ( m_lotsFound ) unsupported . append ( lineSep + i18n ( " Lots " ) ) ;
if ( ! unsupported . isEmpty ( ) ) {
unsupported . prepend ( i18n ( " The following features found in your file are not currently supported: " ) ) ;
s . append ( unsupported ) ;
}
if ( more ) s . append ( i18n ( " \n \n Press More for further information " ) ) ;
} else { // we need to retrieve the posted messages for this source
if ( gncdebug ) tqDebug ( " Building messages for source %s " , source . latin1 ( ) ) ;
unsigned int i , j ;
for ( i = 0 ; i < m_messageList . count ( ) ; i + + ) {
GncMessageArgs * m = m_messageList . at ( i ) ;
if ( m - > source = = source ) {
if ( gncdebug ) tqDebug ( " %s " , TQString ( " build text source %1, code %2, argcount %3 " )
. arg ( m - > source ) . arg ( m - > code ) . arg ( m - > args . count ( ) ) . data ( ) ) ;
TQString ss = GncMessages : : text ( m - > source , m - > code ) ;
// add variable args. the .arg function seems always to replace the
// lowest numbered placeholder it finds, so translating messages
// with variables in a different order should still work okay (I think...)
for ( j = 0 ; j < m - > args . count ( ) ; j + + ) ss = ss . arg ( * m - > args . at ( j ) ) ;
s . append ( ss + " \n " ) ;
}
}
}
if ( gncdebug ) tqDebug ( " %s " , s . latin1 ( ) ) ;
return ( static_cast < const TQString > ( s ) ) ;
PASS
}
//************************ writeReportToFile*********************************
bool MyMoneyGncReader : : writeReportToFile ( const TQValueList < TQString > & sectionsToReport ) {
TRY
unsigned int i ;
TQFileDialog * fd = new TQFileDialog ( 0 , " Save report as " , TRUE ) ;
fd - > setMode ( TQFileDialog : : AnyFile ) ;
if ( fd - > exec ( ) ! = TQDialog : : Accepted ) {
delete fd ;
return ( false ) ;
}
TQFile reportFile ( fd - > selectedFile ( ) ) ;
TQFileInfo fi ( reportFile ) ;
if ( ! reportFile . open ( IO_WriteOnly ) ) {
delete fd ;
return ( false ) ;
}
TQTextStream stream ( & reportFile ) ;
for ( i = 0 ; i < sectionsToReport . count ( ) ; i + + ) {
stream < < buildReportSection ( * sectionsToReport . at ( i ) ) . latin1 ( ) < < endl ;
}
reportFile . close ( ) ;
delete fd ;
return ( true ) ;
PASS
}
/****************************************************************************
Utility routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//************************ createPayee ***************************
TQString MyMoneyGncReader : : createPayee ( const TQString & gncDescription ) {
MyMoneyPayee payee ;
try {
payee = m_storage - > payeeByName ( gncDescription ) ;
} catch ( MyMoneyException * e ) { // payee not found, create one
delete e ;
payee . setName ( gncDescription ) ;
m_storage - > addPayee ( payee ) ;
}
return ( payee . id ( ) ) ;
}
//************************************** createOrphanAccount *******************************
TQString MyMoneyGncReader : : createOrphanAccount ( const TQString & gncName ) {
MyMoneyAccount acc ;
acc . setName ( " orphan_ " + gncName ) ;
acc . setDescription ( i18n ( " Orphan created from unknown gnucash account " ) ) ;
TQDate today = TQDate : : currentDate ( ) ;
acc . setOpeningDate ( today ) ;
acc . setLastModified ( today ) ;
acc . setLastReconciliationDate ( today ) ;
acc . setCurrencyId ( m_txCommodity ) ;
acc . setAccountType ( MyMoneyAccount : : Asset ) ;
acc . setParentAccountId ( m_storage - > asset ( ) . id ( ) ) ;
m_storage - > addAccount ( acc ) ;
// assign the gnucash id as the key into the map to find our id
m_mapIds [ gncName . utf8 ( ) ] = acc . id ( ) ;
postMessage ( TQString ( " OR " ) , 1 , acc . name ( ) . ascii ( ) ) ;
return ( acc . id ( ) ) ;
}
//****************************** incrDate *********************************************
TQDate MyMoneyGncReader : : incrDate ( TQDate lastDate , unsigned char interval , unsigned int intervalCount ) {
TRY
switch ( interval ) {
case ' d ' :
return ( lastDate . addDays ( intervalCount ) ) ;
case ' w ' :
return ( lastDate . addDays ( intervalCount * 7 ) ) ;
case ' m ' :
return ( lastDate . addMonths ( intervalCount ) ) ;
case ' y ' :
return ( lastDate . addYears ( intervalCount ) ) ;
case ' o ' : // once-only
return ( lastDate ) ;
}
throw new MYMONEYEXCEPTION ( i18n ( " Internal error - invalid interval char in incrDate " ) ) ;
TQDate r = TQDate ( ) ; return ( r ) ; // to keep compiler happy
PASS
}
//********************************* checkConsistency **********************************
MyMoneyAccount MyMoneyGncReader : : checkConsistency ( MyMoneyAccount & parent , MyMoneyAccount & child ) {
TRY
// gnucash is flexible/weird enough to allow various inconsistencies
// these are a couple I found in my file, no doubt more will be discovered
if ( ( child . accountType ( ) = = MyMoneyAccount : : Investment ) & &
( parent . accountType ( ) ! = MyMoneyAccount : : Asset ) ) {
postMessage ( " CC " , 1 , child . name ( ) . latin1 ( ) ) ;
return m_storage - > asset ( ) ;
}
if ( ( child . accountType ( ) = = MyMoneyAccount : : Income ) & &
( parent . accountType ( ) ! = MyMoneyAccount : : Income ) ) {
postMessage ( " CC " , 2 , child . name ( ) . latin1 ( ) ) ;
return m_storage - > income ( ) ;
}
if ( ( child . accountType ( ) = = MyMoneyAccount : : Expense ) & &
( parent . accountType ( ) ! = MyMoneyAccount : : Expense ) ) {
postMessage ( " CC " , 3 , child . name ( ) . latin1 ( ) ) ;
return m_storage - > expense ( ) ;
}
return ( parent ) ;
PASS
}
//*********************************** checkInvestmentOption *************************
void MyMoneyGncReader : : checkInvestmentOption ( TQString stockId ) {
// implement the investment option for stock accounts
// first check whether the parent account (gnucash id) is actually an
// investment account. if it is, no further action is needed
MyMoneyAccount stockAcc = m_storage - > account ( m_mapIds [ stockId . utf8 ( ) ] ) ;
MyMoneyAccount parent ;
TQString parentKey = stockAcc . parentAccountId ( ) ;
map_accountIds : : Iterator id = m_mapIds . find ( parentKey ) ;
if ( id ! = m_mapIds . end ( ) ) {
parent = m_storage - > account ( id . data ( ) ) ;
if ( parent . accountType ( ) = = MyMoneyAccount : : Investment ) return ;
}
// so now, check the investment option requested by the user
// option 0 creates a separate investment account for each stock account
if ( m_investmentOption = = 0 ) {
MyMoneyAccount invAcc ( stockAcc ) ;
invAcc . setAccountType ( MyMoneyAccount : : Investment ) ;
invAcc . setCurrencyId ( TQString ( " " ) ) ; // we don't know what currency it is!!
invAcc . setParentAccountId ( parentKey ) ; // intersperse it between old parent and child stock acct
m_storage - > addAccount ( invAcc ) ;
m_mapIds [ invAcc . id ( ) ] = invAcc . id ( ) ; // so stock account gets parented (again) to investment account later
if ( gncdebug ) tqDebug ( " Created investment account %s as id %s, parent %s " , invAcc . name ( ) . data ( ) , invAcc . id ( ) . data ( ) ,
invAcc . parentAccountId ( ) . data ( ) ) ;
if ( gncdebug ) tqDebug ( " Setting stock %s, id %s, as child of %s " , stockAcc . name ( ) . data ( ) , stockAcc . id ( ) . data ( ) , invAcc . id ( ) . data ( ) ) ;
stockAcc . setParentAccountId ( invAcc . id ( ) ) ;
m_storage - > addAccount ( invAcc , stockAcc ) ;
// investment option 1 creates a single investment account for all stocks
} else if ( m_investmentOption = = 1 ) {
static TQString singleInvAccId = " " ;
MyMoneyAccount singleInvAcc ;
bool ok = false ;
if ( singleInvAccId . isEmpty ( ) ) { // if the account has not yet been created
TQString invAccName ;
while ( ! ok ) {
invAccName = TQInputDialog : : getText ( PACKAGE ,
i18n ( " Enter the investment account name " ) , TQLineEdit : : Normal ,
i18n ( " My Investments " ) , & ok ) ;
}
singleInvAcc . setName ( invAccName ) ;
singleInvAcc . setAccountType ( MyMoneyAccount : : Investment ) ;
singleInvAcc . setCurrencyId ( TQString ( " " ) ) ;
singleInvAcc . setParentAccountId ( m_storage - > asset ( ) . id ( ) ) ;
m_storage - > addAccount ( singleInvAcc ) ;
m_mapIds [ singleInvAcc . id ( ) ] = singleInvAcc . id ( ) ; // so stock account gets parented (again) to investment account later
if ( gncdebug ) tqDebug ( " Created investment account %s as id %s, parent %s, reparenting stock " ,
singleInvAcc . name ( ) . data ( ) , singleInvAcc . id ( ) . data ( ) , singleInvAcc . parentAccountId ( ) . data ( ) ) ;
singleInvAccId = singleInvAcc . id ( ) ;
} else { // the account has already been created
singleInvAcc = m_storage - > account ( singleInvAccId ) ;
}
m_storage - > addAccount ( singleInvAcc , stockAcc ) ; // add stock as child
// the original intention of option 2 was to allow any asset account to be converted to an investment (broker) account
// however, since we have already stored the accounts as asset, we have no way at present of changing their type
// the only alternative would be to hold all the gnucash data in memory, then implement this option, then convert all the data
// that would mean a major overhaul of the code. Perhaps I'll think of another way...
} else if ( m_investmentOption = = 2 ) {
static int lastSelected = 0 ;
MyMoneyAccount invAcc ( stockAcc ) ;
TQStringList accList ;
TQValueList < MyMoneyAccount > list ;
TQValueList < MyMoneyAccount > : : Iterator acc ;
m_storage - > accountList ( list ) ;
// build a list of candidates for the input box
for ( acc = list . begin ( ) ; acc ! = list . end ( ) ; + + acc ) {
// if (((*acc).accountGroup() == MyMoneyAccount::Asset) && ((*acc).accountType() != MyMoneyAccount::Stock)) accList.append ((*acc).name());
if ( ( * acc ) . accountType ( ) = = MyMoneyAccount : : Investment ) accList . append ( ( * acc ) . name ( ) ) ;
}
//if (accList.isEmpty()) tqFatal ("No available accounts");
bool ok = false ;
while ( ! ok ) { // keep going till we have a valid investment parent
TQString invAccName = TQInputDialog : : getItem (
PACKAGE , i18n ( " Select parent investment account or enter new name. Stock %1 " ) . arg ( stockAcc . name ( ) ) ,
accList , lastSelected , true , & ok ) ;
if ( ok ) {
lastSelected = accList . findIndex ( invAccName ) ; // preserve selection for next time
for ( acc = list . begin ( ) ; acc ! = list . end ( ) ; + + acc ) {
if ( ( * acc ) . name ( ) = = invAccName ) break ;
}
if ( acc ! = list . end ( ) ) { // an account was selected
invAcc = * acc ;
} else { // a new account name was entered
invAcc . setAccountType ( MyMoneyAccount : : Investment ) ;
invAcc . setName ( invAccName ) ;
invAcc . setCurrencyId ( TQString ( " " ) ) ;
invAcc . setParentAccountId ( m_storage - > asset ( ) . id ( ) ) ;
m_storage - > addAccount ( invAcc ) ;
ok = true ;
}
if ( invAcc . accountType ( ) = = MyMoneyAccount : : Investment ) {
ok = true ;
} else {
// this code is probably not going to be implemented coz we can't change account types (??)
#if 0
TQMessageBox mb ( PACKAGE ,
i18n ( " %1 is not an Investment Account. Do you wish to make it one? " ) . arg ( invAcc . name ( ) ) ,
TQMessageBox : : Question ,
TQMessageBox : : Yes | TQMessageBox : : Default ,
TQMessageBox : : No | TQMessageBox : : Escape ,
TQMessageBox : : NoButton ) ;
switch ( mb . exec ( ) ) {
case TQMessageBox : : No :
ok = false ; break ;
default :
// convert it - but what if it has splits???
tqFatal ( " Not yet implemented " ) ;
ok = true ;
break ;
}
# endif
switch ( KMessageBox : : questionYesNo ( 0 , i18n ( " %1 is not an Investment Account. Do you wish to make it one? " ) . arg ( invAcc . name ( ) , PACKAGE ) ) ) {
case KMessageBox : : Yes :
// convert it - but what if it has splits???
tqFatal ( " Not yet implemented " ) ;
ok = true ;
break ;
default :
ok = false ;
break ;
}
}
} // end if ok - user pressed Cancel
} // end while !ok
m_mapIds [ invAcc . id ( ) ] = invAcc . id ( ) ; // so stock account gets parented (again) to investment account later
m_storage - > addAccount ( invAcc , stockAcc ) ;
} else { // investment option != 0, 1, 2
tqFatal ( " Invalid investment option %d " , m_investmentOption ) ;
}
}
// get the price source for a stock (gnc account) where online quotes are requested
void MyMoneyGncReader : : getPriceSource ( MyMoneySecurity stock , TQString gncSource ) {
// if he wants to use Finance::Quote, no conversion of source name is needed
if ( m_useFinanceQuote ) {
stock . setValue ( " kmm-online-quote-system " , " Finance::Quote " ) ;
stock . setValue ( " kmm-online-source " , gncSource . lower ( ) ) ;
m_storage - > modifySecurity ( stock ) ;
return ;
}
// first check if we have already asked about this source
// (mapSources is initialy empty. We may be able to pre-fill it with some equivalent
// sources, if such things do exist. User feedback may help here.)
TQMap < TQString , TQString > : : Iterator it ;
for ( it = m_mapSources . begin ( ) ; it ! = m_mapSources . end ( ) ; it + + ) {
if ( it . key ( ) = = gncSource ) {
stock . setValue ( " kmm-online-source " , it . data ( ) ) ;
m_storage - > modifySecurity ( stock ) ;
return ;
}
}
// not found in map, so ask the user
KGncPriceSourceDlg * dlg = new KGncPriceSourceDlg ( stock . name ( ) , gncSource ) ;
dlg - > exec ( ) ;
TQString s = dlg - > selectedSource ( ) ;
if ( ! s . isEmpty ( ) ) {
stock . setValue ( " kmm-online-source " , s ) ;
m_storage - > modifySecurity ( stock ) ;
}
if ( dlg - > alwaysUse ( ) ) m_mapSources [ gncSource ] = s ;
delete dlg ;
return ;
}
// functions to control the progress bar
//*********************** setProgressCallback *****************************
void MyMoneyGncReader : : setProgressCallback ( void ( * callback ) ( int , int , const TQString & ) ) {
m_progressCallback = callback ; return ;
}
//************************** signalProgress *******************************
void MyMoneyGncReader : : signalProgress ( int current , int total , const TQString & msg ) {
if ( m_progressCallback ! = 0 )
( * m_progressCallback ) ( current , total , msg ) ;
return ;
}
// error and information reporting
//***************************** Information and error messages *********************
void MyMoneyGncReader : : postMessage ( const TQString & source , const unsigned int code , const char * arg1 ) {
postMessage ( source , code , TQStringList ( arg1 ) ) ;
}
void MyMoneyGncReader : : postMessage ( const TQString & source , const unsigned int code , const char * arg1 , const char * arg2 ) {
TQStringList argList ( arg1 ) ;
argList . append ( arg2 ) ;
postMessage ( source , code , argList ) ;
}
void MyMoneyGncReader : : postMessage ( const TQString & source , const unsigned int code , const char * arg1 , const char * arg2 , const char * arg3 ) {
TQStringList argList ( arg1 ) ;
argList . append ( arg2 ) ;
argList . append ( arg3 ) ;
postMessage ( source , code , argList ) ;
}
void MyMoneyGncReader : : postMessage ( const TQString & source , const unsigned int code , const TQStringList & argList ) {
unsigned int i ;
GncMessageArgs * m = new GncMessageArgs ;
m - > source = source ;
m - > code = code ;
// get the number of args this message requires
const unsigned int argCount = GncMessages : : argCount ( source , code ) ;
if ( ( gncdebug ) & & ( argCount ! = argList . count ( ) ) )
tqDebug ( " %s " , TQString ( " MyMoneyGncReader::postMessage debug: Message %1, code %2, requires %3 arguments, got %4 " )
. arg ( source ) . arg ( code ) . arg ( argCount ) . arg ( argList . count ( ) ) . data ( ) ) ;
// store the arguments
for ( i = 0 ; i < argCount ; i + + ) {
if ( i > argList . count ( ) ) m - > args . append ( TQString ( ) ) ;
else m - > args . append ( argList [ i ] ) ; //Adds the next argument to the list
}
m_messageList . append ( m ) ;
return ;
}
//********************************** Message texts **********************************************
GncMessages : : messText GncMessages : : texts [ ] = {
{ " CC " , 1 , i18n ( " An Investment account must be a child of an Asset account \n "
" Account %1 will be stored under the main Asset account " ) } ,
{ " CC " , 2 , i18n ( " An Income account must be a child of an Income account \n "
" Account %1 will be stored under the main Income account " ) } ,
{ " CC " , 3 , i18n ( " An Expense account must be a child of an Expense account \n "
" Account %1 will be stored under the main Expense account " ) } ,
{ " OR " , 1 , i18n ( " One or more transactions contain a reference to an otherwise unknown account \n "
" An asset account with the name %1 has been created to hold the data " ) } ,
{ " SC " , 1 , i18n ( " Schedule %1 has interval of %2 which is not currently available " ) } ,
{ " SC " , 2 , i18n ( " Schedule %1 dropped at user request " ) } ,
{ " SC " , 3 , i18n ( " Schedule %1 contains unknown action (key = %2, type = %3) " ) } ,
{ " SC " , 4 , i18n ( " Schedule %1 contains multiple actions; only one has been imported " ) } ,
{ " SC " , 5 , i18n ( " Schedule %1 contains no valid splits " ) } ,
{ " SC " , 6 , i18n ( " Schedule %1 appears to contain a formula. GnuCash formulae are not convertible " ) } ,
{ " SC " , 7 , i18n ( " Schedule %1 contains unknown interval specification; please check for correct operation " ) } ,
{ " SC " , 8 , i18n ( " Schedule %1 contains a deferred interval specification; please check for correct operation " ) } ,
{ " CC " , 4 , i18n ( " Account or Category %1, transaction date %2; split contains invalid value; please check " ) } ,
{ " ZZ " , 0 , " " } // stopper
} ;
//
TQString GncMessages : : text ( const TQString source , const unsigned int code ) {
TRY
unsigned int i ;
for ( i = 0 ; texts [ i ] . source ! = " ZZ " ; i + + ) {
if ( ( source = = texts [ i ] . source ) & & ( code = = texts [ i ] . code ) ) break ;
}
if ( texts [ i ] . source = = " ZZ " ) {
TQString mess = TQString ( ) . sprintf ( " Internal error - unknown message - source %s, code %d " , source . latin1 ( ) , code ) ;
throw new MYMONEYEXCEPTION ( mess ) ;
}
return ( texts [ i ] . text ) ;
PASS
}
//
unsigned int GncMessages : : argCount ( const TQString source , const unsigned int code ) {
TRY
unsigned int i ;
for ( i = 0 ; texts [ i ] . source ! = " ZZ " ; i + + ) {
if ( ( source = = texts [ i ] . source ) & & ( code = = texts [ i ] . code ) ) break ;
}
if ( texts [ i ] . source = = " ZZ " ) {
TQString mess = TQString ( ) . sprintf ( " Internal error - unknown message - source %s, code %d " , source . latin1 ( ) , code ) ;
throw new MYMONEYEXCEPTION ( mess ) ;
}
TQRegExp argConst ( " % \\ d " ) ;
int offset = 0 ;
unsigned int argCount = 0 ;
while ( ( offset = argConst . search ( texts [ i ] . text , offset ) ) ! = - 1 ) {
argCount + + ;
offset + = 2 ;
}
return ( argCount ) ;
PASS
}
# endif // _GNCFILEANON