|
|
|
/***************************************************************************
|
|
|
|
mymoneyofxconnector.cpp
|
|
|
|
-------------------
|
|
|
|
begin : Sat Nov 13 2004
|
|
|
|
copyright : (C) 2002 by Ace Jones
|
|
|
|
email : acejones@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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// System Includes
|
|
|
|
|
|
|
|
#include <libofx/libofx.h>
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// QT Includes
|
|
|
|
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// KDE Includes
|
|
|
|
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kcombobox.h>
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Project Includes
|
|
|
|
|
|
|
|
#include <kmymoney/mymoneyfile.h>
|
|
|
|
#include <kmymoney/mymoneyaccount.h>
|
|
|
|
#include <kmymoney/mymoneyinstitution.h>
|
|
|
|
#include <kmymoney/mymoneykeyvaluecontainer.h>
|
|
|
|
#include "mymoneyofxconnector.h"
|
|
|
|
|
|
|
|
OfxHeaderVersion::OfxHeaderVersion(KComboBox* combo, const TQString& headerVersion) :
|
|
|
|
m_combo(combo)
|
|
|
|
{
|
|
|
|
combo->clear();
|
|
|
|
combo->insertItem("102");
|
|
|
|
combo->insertItem("103");
|
|
|
|
|
|
|
|
if(!headerVersion.isEmpty()) {
|
|
|
|
combo->setCurrentItem(headerVersion);
|
|
|
|
} else {
|
|
|
|
combo->setCurrentItem("102");
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ! LIBOFX_IS_VERSION(0,9,0)
|
|
|
|
// This feature does not work with libOFX < 0.9 so
|
|
|
|
// we just make disable the button in this case
|
|
|
|
combo->setDisabled(true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString OfxHeaderVersion::headerVersion(void) const
|
|
|
|
{
|
|
|
|
return m_combo->currentText();
|
|
|
|
}
|
|
|
|
|
|
|
|
OfxAppVersion::OfxAppVersion(KComboBox* combo, const TQString& appId) :
|
|
|
|
m_combo(combo)
|
|
|
|
{
|
|
|
|
// http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-intuit-products/
|
|
|
|
// http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-microsoft-money/
|
|
|
|
|
|
|
|
// Quicken
|
|
|
|
m_appMap[i18n("Quicken Windows 2003")] = "QWIN:1200";
|
|
|
|
m_appMap[i18n("Quicken Windows 2004")] = "QWIN:1300";
|
|
|
|
m_appMap[i18n("Quicken Windows 2005")] = "QWIN:1400";
|
|
|
|
m_appMap[i18n("Quicken Windows 2006")] = "QWIN:1500";
|
|
|
|
m_appMap[i18n("Quicken Windows 2007")] = "QWIN:1600";
|
|
|
|
m_appMap[i18n("Quicken Windows 2008")] = "QWIN:1700";
|
|
|
|
|
|
|
|
// MS-Money
|
|
|
|
m_appMap[i18n("MS-Money 2003")] = "Money:1100";
|
|
|
|
m_appMap[i18n("MS-Money 2004")] = "Money:1200";
|
|
|
|
m_appMap[i18n("MS-Money 2005")] = "Money:1400";
|
|
|
|
m_appMap[i18n("MS-Money 2006")] = "Money:1500";
|
|
|
|
m_appMap[i18n("MS-Money 2007")] = "Money:1600";
|
|
|
|
m_appMap[i18n("MS-Money Plus")] = "Money:1700";
|
|
|
|
|
|
|
|
// KMyMoney
|
|
|
|
m_appMap["KMyMoney"] = "KMyMoney:1000";
|
|
|
|
|
|
|
|
combo->clear();
|
|
|
|
combo->insertStringList(m_appMap.keys());
|
|
|
|
|
|
|
|
TQMap<TQString, TQString>::const_iterator it_a;
|
|
|
|
for(it_a = m_appMap.begin(); it_a != m_appMap.end(); ++it_a) {
|
|
|
|
if(*it_a == appId)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(it_a != m_appMap.end()) {
|
|
|
|
combo->setCurrentItem(it_a.key());
|
|
|
|
} else {
|
|
|
|
combo->setCurrentItem(i18n("Quicken Windows 2008"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ! LIBOFX_IS_VERSION(0,9,0)
|
|
|
|
// This feature does not work with libOFX < 0.9 so
|
|
|
|
// we just make disable the button in this case
|
|
|
|
combo->setDisabled(true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString& OfxAppVersion::appId(void) const
|
|
|
|
{
|
|
|
|
static TQString defaultAppId("QWIN:1700");
|
|
|
|
|
|
|
|
TQString app = m_combo->currentText();
|
|
|
|
if(m_appMap[app] != defaultAppId)
|
|
|
|
return m_appMap[app];
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::MyMoneyOfxConnector(const MyMoneyAccount& _account):
|
|
|
|
m_account(_account)
|
|
|
|
{
|
|
|
|
m_fiSettings = m_account.onlineBankingSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyOfxConnector::iban(void) const { return m_fiSettings.value("bankid"); }
|
|
|
|
TQString MyMoneyOfxConnector::fiorg(void) const { return m_fiSettings.value("org"); }
|
|
|
|
TQString MyMoneyOfxConnector::fiid(void) const { return m_fiSettings.value("fid"); }
|
|
|
|
TQString MyMoneyOfxConnector::username(void) const { return m_fiSettings.value("username"); }
|
|
|
|
TQString MyMoneyOfxConnector::password(void) const { return m_fiSettings.value("password"); }
|
|
|
|
TQString MyMoneyOfxConnector::accountnum(void) const { return m_fiSettings.value("accountid"); }
|
|
|
|
TQString MyMoneyOfxConnector::url(void) const { return m_fiSettings.value("url"); }
|
|
|
|
|
|
|
|
TQDate MyMoneyOfxConnector::statementStartDate(void) const {
|
|
|
|
if ((m_fiSettings.value("kmmofx-todayMinus").toInt() != 0) && !m_fiSettings.value("kmmofx-numRequestDays").isEmpty())
|
|
|
|
{
|
|
|
|
return TQDate::currentDate().addDays(-m_fiSettings.value("kmmofx-numRequestDays").toInt());
|
|
|
|
}
|
|
|
|
else if ((m_fiSettings.value("kmmofx-lastUpdate").toInt() != 0) && !m_account.value("lastImportedTransactionDate").isEmpty())
|
|
|
|
{
|
|
|
|
return TQDate::fromString(m_account.value("lastImportedTransactionDate"), Qt::ISODate);
|
|
|
|
}
|
|
|
|
else if ((m_fiSettings.value("kmmofx-pickDate").toInt() != 0) && !m_fiSettings.value("kmmofx-specificDate").isEmpty())
|
|
|
|
{
|
|
|
|
return TQDate::fromString(m_fiSettings.value("kmmofx-specificDate"));
|
|
|
|
}
|
|
|
|
return TQDate::currentDate().addMonths(-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LIBOFX_IS_VERSION(0,9,0)
|
|
|
|
OfxAccountData::AccountType MyMoneyOfxConnector::accounttype(void) const
|
|
|
|
{
|
|
|
|
OfxAccountData::AccountType result = OfxAccountData::OFX_CHECKING;
|
|
|
|
|
|
|
|
TQString type = m_account.onlineBankingSettings()["type"];
|
|
|
|
if(type == "CHECKING")
|
|
|
|
result = OfxAccountData::OFX_CHECKING;
|
|
|
|
else if(type == "SAVINGS")
|
|
|
|
result = OfxAccountData::OFX_SAVINGS;
|
|
|
|
else if(type == "MONEY MARKET")
|
|
|
|
result = OfxAccountData::OFX_MONEYMRKT;
|
|
|
|
else if(type == "CREDIT LINE")
|
|
|
|
result = OfxAccountData::OFX_CREDITLINE;
|
|
|
|
else if(type == "CMA")
|
|
|
|
result = OfxAccountData::OFX_CMA;
|
|
|
|
else if(type == "CREDIT CARD")
|
|
|
|
result = OfxAccountData::OFX_CREDITCARD;
|
|
|
|
else if(type == "INVESTMENT")
|
|
|
|
result = OfxAccountData::OFX_INVESTMENT;
|
|
|
|
else {
|
|
|
|
switch( m_account.accountType()) {
|
|
|
|
case MyMoneyAccount::Investment:
|
|
|
|
result = OfxAccountData::OFX_INVESTMENT;
|
|
|
|
break;
|
|
|
|
case MyMoneyAccount::CreditCard:
|
|
|
|
result = OfxAccountData::OFX_CREDITCARD;
|
|
|
|
break;
|
|
|
|
case MyMoneyAccount::Savings:
|
|
|
|
result = OfxAccountData::OFX_SAVINGS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a bit of a personalized hack. Sometimes we may want to override the
|
|
|
|
// ofx type for an account. For now, I will stash it in the notes!
|
|
|
|
|
|
|
|
TQRegExp rexp("OFXTYPE:([A-Z]*)");
|
|
|
|
if ( rexp.search(m_account.description()) != -1 )
|
|
|
|
{
|
|
|
|
TQString override = rexp.cap(1);
|
|
|
|
kdDebug(2) << "MyMoneyOfxConnector::accounttype() overriding to " << result << endl;
|
|
|
|
|
|
|
|
if ( override == "BANK" )
|
|
|
|
result = OfxAccountData::OFX_CHECKING;
|
|
|
|
else if ( override == "CC" )
|
|
|
|
result = OfxAccountData::OFX_CREDITCARD;
|
|
|
|
else if ( override == "INV" )
|
|
|
|
result = OfxAccountData::OFX_INVESTMENT;
|
|
|
|
else if ( override == "MONEYMARKET")
|
|
|
|
result = OfxAccountData::OFX_MONEYMRKT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
AccountType MyMoneyOfxConnector::accounttype(void) const
|
|
|
|
{
|
|
|
|
AccountType result = OFX_BANK_ACCOUNT;
|
|
|
|
|
|
|
|
switch( m_account.accountType() )
|
|
|
|
{
|
|
|
|
case MyMoneyAccount::Investment:
|
|
|
|
result = OFX_INVEST_ACCOUNT;
|
|
|
|
break;
|
|
|
|
case MyMoneyAccount::CreditCard:
|
|
|
|
result = OFX_CREDITCARD_ACCOUNT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a bit of a personalized hack. Sometimes we may want to override the
|
|
|
|
// ofx type for an account. For now, I will stash it in the notes!
|
|
|
|
|
|
|
|
TQRegExp rexp("OFXTYPE:([A-Z]*)");
|
|
|
|
if ( rexp.search(m_account.description()) != -1 )
|
|
|
|
{
|
|
|
|
TQString override = rexp.cap(1);
|
|
|
|
kdDebug(2) << "MyMoneyOfxConnector::accounttype() overriding to " << result << endl;
|
|
|
|
|
|
|
|
if ( override == "BANK" )
|
|
|
|
result = OFX_BANK_ACCOUNT;
|
|
|
|
else if ( override == "CC" )
|
|
|
|
result = OFX_CREDITCARD_ACCOUNT;
|
|
|
|
else if ( override == "INV" )
|
|
|
|
result = OFX_INVEST_ACCOUNT;
|
|
|
|
#if 0 // money market is not supported by 0.8.x
|
|
|
|
else if ( override == "MONEYMARKET")
|
|
|
|
result = OFX_MONEYMRKT;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void MyMoneyOfxConnector::initRequest(OfxFiLogin* fi) const
|
|
|
|
{
|
|
|
|
memset(fi,0,sizeof(OfxFiLogin));
|
|
|
|
strncpy(fi->fid, fiid().latin1(), OFX_FID_LENGTH-1);
|
|
|
|
strncpy(fi->org, fiorg().latin1(), OFX_ORG_LENGTH-1);
|
|
|
|
strncpy(fi->userid, username().latin1(), OFX_USERID_LENGTH-1);
|
|
|
|
strncpy(fi->userpass, password().latin1(), OFX_USERPASS_LENGTH-1);
|
|
|
|
|
|
|
|
#if LIBOFX_IS_VERSION(0,9,0)
|
|
|
|
// If we don't know better, we pretend to be Quicken 2008
|
|
|
|
// http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-intuit-products/
|
|
|
|
// http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-microsoft-money/
|
|
|
|
TQString appId = m_account.onlineBankingSettings().value("appId");
|
|
|
|
TQRegExp exp("(.*):(.*)");
|
|
|
|
if(exp.search(appId) != -1) {
|
|
|
|
strncpy(fi->appid, exp.cap(1).latin1(), OFX_APPID_LENGTH-1);
|
|
|
|
strncpy(fi->appver, exp.cap(2).latin1(), OFX_APPVER_LENGTH-1);
|
|
|
|
} else {
|
|
|
|
strncpy(fi->appid, "QWIN", OFX_APPID_LENGTH-1);
|
|
|
|
strncpy(fi->appver, "1700", OFX_APPVER_LENGTH-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString headerVersion = m_account.onlineBankingSettings().value("kmmofx-headerVersion");
|
|
|
|
if(!headerVersion.isEmpty()) {
|
|
|
|
strncpy(fi->header_version, headerVersion.latin1(), OFX_HEADERVERSION_LENGTH-1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQByteArray MyMoneyOfxConnector::statementRequest(void) const
|
|
|
|
{
|
|
|
|
OfxFiLogin fi;
|
|
|
|
initRequest(&fi);
|
|
|
|
|
|
|
|
#if LIBOFX_IS_VERSION(0,9,0)
|
|
|
|
OfxAccountData account;
|
|
|
|
memset(&account,0,sizeof(OfxAccountData));
|
|
|
|
|
|
|
|
if(iban().latin1() != 0) {
|
|
|
|
strncpy(account.bank_id,iban().latin1(),OFX_BANKID_LENGTH-1);
|
|
|
|
strncpy(account.broker_id,iban().latin1(),OFX_BROKERID_LENGTH-1);
|
|
|
|
}
|
|
|
|
strncpy(account.account_number,accountnum().latin1(),OFX_ACCTID_LENGTH-1);
|
|
|
|
account.account_type = accounttype();
|
|
|
|
#else
|
|
|
|
OfxAccountInfo account;
|
|
|
|
memset(&account,0,sizeof(OfxAccountInfo));
|
|
|
|
|
|
|
|
if(iban().latin1() != 0) {
|
|
|
|
strncpy(account.bankid,iban().latin1(),OFX_BANKID_LENGTH-1);
|
|
|
|
strncpy(account.brokerid,iban().latin1(),OFX_BROKERID_LENGTH-1);
|
|
|
|
}
|
|
|
|
strncpy(account.accountid,accountnum().latin1(),OFX_ACCOUNT_ID_LENGTH-1);
|
|
|
|
account.type = accounttype();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char* szrequest = libofx_request_statement( &fi, &account, TQDateTime(statementStartDate()).toTime_t() );
|
|
|
|
TQString request = szrequest;
|
|
|
|
// remove the trailing zero
|
|
|
|
TQByteArray result = request.utf8();
|
|
|
|
result.truncate(result.size()-1);
|
|
|
|
free(szrequest);
|
|
|
|
|
|
|
|
TQString msg(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// this code is not used anymore. The logic is now
|
|
|
|
// contained in KOnlineBankingSetupWizard::finishLoginPage(void)
|
|
|
|
const TQByteArray MyMoneyOfxConnector::accountInfoRequest(void) const
|
|
|
|
{
|
|
|
|
OfxFiLogin fi;
|
|
|
|
initRequest(&fi);
|
|
|
|
|
|
|
|
char* szrequest = libofx_request_accountinfo( &fi );
|
|
|
|
TQString request = szrequest;
|
|
|
|
// remove the trailing zero
|
|
|
|
TQByteArray result = request.utf8();
|
|
|
|
result.truncate(result.size()-1);
|
|
|
|
free(szrequest);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::message(const TQString& _msgType, const TQString& _trnType, const Tag& _request)
|
|
|
|
{
|
|
|
|
return Tag(_msgType+"MSGSRQV1")
|
|
|
|
.subtag(Tag(_trnType+"TRNRQ")
|
|
|
|
.element("TRNUID",uuid())
|
|
|
|
.element("CLTCOOKIE","1")
|
|
|
|
.subtag(_request));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::investmentRequest(void) const
|
|
|
|
{
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
return message("INVSTMT","INVSTMT",Tag("INVSTMTRQ")
|
|
|
|
.subtag(Tag("INVACCTFROM").element("BROKERID", fiorg()).element("ACCTID", accountnum()))
|
|
|
|
.subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y"))
|
|
|
|
.element("INCOO","Y")
|
|
|
|
.subtag(Tag("INCPOS").element("DTASOF", dtnow_string).element("INCLUDE","Y"))
|
|
|
|
.element("INCBAL","Y"));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::bankStatementRequest(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
return message("BANK","STMT",Tag("STMTRQ")
|
|
|
|
.subtag(Tag("BANKACCTFROM").element("BANKID", iban()).element("ACCTID", accountnum()).element("ACCTTYPE", "CHECKING"))
|
|
|
|
.subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y")));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::creditCardRequest(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
return message("CREDITCARD","CCSTMT",Tag("CCSTMTRQ")
|
|
|
|
.subtag(Tag("CCACCTFROM").element("ACCTID",accountnum()))
|
|
|
|
.subtag(Tag("INCTRAN").element("DTSTART",dtstart_string).element("INCLUDE","Y")));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::signOn(void) const
|
|
|
|
{
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
Tag fi("FI");
|
|
|
|
fi.element("ORG",fiorg());
|
|
|
|
if ( !fiid().isEmpty() )
|
|
|
|
fi.element("FID",fiid());
|
|
|
|
|
|
|
|
return Tag("SIGNONMSGSRQV1")
|
|
|
|
.subtag(Tag("SONRQ")
|
|
|
|
.element("DTCLIENT",dtnow_string)
|
|
|
|
.element("USERID",username())
|
|
|
|
.element("USERPASS",password())
|
|
|
|
.element("LANGUAGE","ENG")
|
|
|
|
.subtag(fi)
|
|
|
|
.element("APPID","QWIN")
|
|
|
|
.element("APPVER","1100"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyOfxConnector::header(void)
|
|
|
|
{
|
|
|
|
return TQString("OFXHEADER:100\r\n"
|
|
|
|
"DATA:OFXSGML\r\n"
|
|
|
|
"VERSION:102\r\n"
|
|
|
|
"SECURITY:NONE\r\n"
|
|
|
|
"ENCODING:USASCII\r\n"
|
|
|
|
"CHARSET:1252\r\n"
|
|
|
|
"COMPRESSION:NONE\r\n"
|
|
|
|
"OLDFILEUID:NONE\r\n"
|
|
|
|
"NEWFILEUID:%1\r\n"
|
|
|
|
"\r\n").tqarg(uuid());
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyOfxConnector::uuid(void)
|
|
|
|
{
|
|
|
|
static int id = 1;
|
|
|
|
return TQDateTime::currentDateTime().toString("yyyyMMdd-hhmmsszzz-") + TQString::number(id++);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Methods to provide RESPONSES to OFX requests. This has no real use in
|
|
|
|
// KMyMoney, but it's included for the purposes of unit testing. This way, I
|
|
|
|
// can create a MyMoneyAccount, write it to an OFX file, import that OFX file,
|
|
|
|
// and check that everything made it through the importer.
|
|
|
|
//
|
|
|
|
// It's also a far-off dream to write an OFX server using KMyMoney as a
|
|
|
|
// backend. It really should not be that hard, and it would fill a void in
|
|
|
|
// the open source software community.
|
|
|
|
//
|
|
|
|
|
|
|
|
const TQByteArray MyMoneyOfxConnector::statementResponse(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
TQString request;
|
|
|
|
|
|
|
|
if ( accounttype()=="CC" )
|
|
|
|
request = header() + Tag("OFX").subtag(signOnResponse()).subtag(creditCardStatementResponse(_dtstart));
|
|
|
|
else if ( accounttype()=="INV" )
|
|
|
|
request = header() + Tag("OFX").subtag(signOnResponse()).data(investmentStatementResponse(_dtstart));
|
|
|
|
else
|
|
|
|
request = header() + Tag("OFX").subtag(signOnResponse()).subtag(bankStatementResponse(_dtstart));
|
|
|
|
|
|
|
|
// remove the trailing zero
|
|
|
|
TQByteArray result = request.utf8();
|
|
|
|
result.truncate(result.size()-1);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::signOnResponse(void) const
|
|
|
|
{
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
Tag sonrs("SONRS");
|
|
|
|
sonrs
|
|
|
|
.subtag(Tag("STATUS")
|
|
|
|
.element("CODE","0")
|
|
|
|
.element("SEVERITY","INFO")
|
|
|
|
.element("MESSAGE","The operation succeeded.")
|
|
|
|
)
|
|
|
|
.element("DTSERVER",dtnow_string)
|
|
|
|
.element("LANGUAGE","ENG");
|
|
|
|
|
|
|
|
Tag fi("FI");
|
|
|
|
if ( !fiorg().isEmpty() )
|
|
|
|
fi.element("ORG",fiorg());
|
|
|
|
if ( !fiid().isEmpty() )
|
|
|
|
fi.element("FID",fiid());
|
|
|
|
|
|
|
|
if ( !fi.isEmpty() )
|
|
|
|
sonrs.subtag(fi);
|
|
|
|
|
|
|
|
return Tag("SIGNONMSGSRSV1").subtag(sonrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::messageResponse(const TQString& _msgType, const TQString& _trnType, const Tag& _response)
|
|
|
|
{
|
|
|
|
return Tag(_msgType+"MSGSRSV1")
|
|
|
|
.subtag(Tag(_trnType+"TRNRS")
|
|
|
|
.element("TRNUID",uuid())
|
|
|
|
.subtag(Tag("STATUS").element("CODE","0").element("SEVERITY","INFO"))
|
|
|
|
.element("CLTCOOKIE","1")
|
|
|
|
.subtag(_response));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::bankStatementResponse(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
TQString transactionlist;
|
|
|
|
|
|
|
|
MyMoneyTransactionFilter filter;
|
|
|
|
filter.setDateFilter(_dtstart,TQDate::currentDate());
|
|
|
|
filter.addAccount(m_account.id());
|
|
|
|
TQValueList<MyMoneyTransaction> transactions = file->transactionList(filter);
|
|
|
|
TQValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin();
|
|
|
|
while ( it_transaction != transactions.end() )
|
|
|
|
{
|
|
|
|
transactionlist += transaction( *it_transaction );
|
|
|
|
++it_transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
return messageResponse("BANK","STMT",Tag("STMTRS")
|
|
|
|
.element("CURDEF","USD")
|
|
|
|
.subtag(Tag("BANKACCTFROM").element("BANKID", iban()).element("ACCTID", accountnum()).element("ACCTTYPE", "CHECKING"))
|
|
|
|
.subtag(Tag("BANKTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist))
|
|
|
|
.subtag(Tag("LEDGERBAL").element("BALAMT",file->balance(m_account.id()).formatMoney(TQString(),2)).element("DTASOF",dtnow_string )));
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::creditCardStatementResponse(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
TQString transactionlist;
|
|
|
|
|
|
|
|
MyMoneyTransactionFilter filter;
|
|
|
|
filter.setDateFilter(_dtstart,TQDate::currentDate());
|
|
|
|
filter.addAccount(m_account.id());
|
|
|
|
TQValueList<MyMoneyTransaction> transactions = file->transactionList(filter);
|
|
|
|
TQValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin();
|
|
|
|
while ( it_transaction != transactions.end() )
|
|
|
|
{
|
|
|
|
transactionlist += transaction( *it_transaction );
|
|
|
|
++it_transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
return messageResponse("CREDITCARD","CCSTMT",Tag("CCSTMTRS")
|
|
|
|
.element("CURDEF","USD")
|
|
|
|
.subtag(Tag("CCACCTFROM").element("ACCTID", accountnum()))
|
|
|
|
.subtag(Tag("BANKTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist))
|
|
|
|
.subtag(Tag("LEDGERBAL").element("BALAMT",file->balance(m_account.id()).formatMoney(TQString(),2)).element("DTASOF",dtnow_string )));
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyOfxConnector::investmentStatementResponse(const TQDate& _dtstart) const
|
|
|
|
{
|
|
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
|
|
|
|
TQString dtstart_string = _dtstart.toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
TQString dtnow_string = TQDateTime::currentDateTime().toString(Qt::ISODate).remove(TQRegExp("[^0-9]"));
|
|
|
|
|
|
|
|
TQString transactionlist;
|
|
|
|
|
|
|
|
MyMoneyTransactionFilter filter;
|
|
|
|
filter.setDateFilter(_dtstart,TQDate::currentDate());
|
|
|
|
filter.addAccount(m_account.id());
|
|
|
|
filter.addAccount(m_account.accountList());
|
|
|
|
TQValueList<MyMoneyTransaction> transactions = file->transactionList(filter);
|
|
|
|
TQValueList<MyMoneyTransaction>::const_iterator it_transaction = transactions.begin();
|
|
|
|
while ( it_transaction != transactions.end() )
|
|
|
|
{
|
|
|
|
transactionlist += investmentTransaction( *it_transaction );
|
|
|
|
++it_transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
Tag securitylist("SECLIST");
|
|
|
|
QCStringList accountids = m_account.accountList();
|
|
|
|
QCStringList::const_iterator it_accountid = accountids.begin();
|
|
|
|
while ( it_accountid != accountids.end() )
|
|
|
|
{
|
|
|
|
MyMoneySecurity equity = file->security(file->account(*it_accountid).currencyId());
|
|
|
|
|
|
|
|
securitylist.subtag(Tag("STOCKINFO")
|
|
|
|
.subtag(Tag("SECINFO")
|
|
|
|
.subtag(Tag("SECID")
|
|
|
|
.element("UNITQUEID",equity.id())
|
|
|
|
.element("UNITQUEIDTYPE","KMYMONEY"))
|
|
|
|
.element("SECNAME",equity.name())
|
|
|
|
.element("TICKER",equity.tradingSymbol())
|
|
|
|
.element("FIID",equity.id())));
|
|
|
|
|
|
|
|
++it_accountid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return messageResponse("INVSTMT","INVSTMT",Tag("INVSTMTRS")
|
|
|
|
.element("DTASOF", dtstart_string)
|
|
|
|
.element("CURDEF","USD")
|
|
|
|
.subtag(Tag("INVACCTFROM").element("BROKERID", fiorg()).element("ACCTID", accountnum()))
|
|
|
|
.subtag(Tag("INVTRANLIST").element("DTSTART",dtstart_string).element("DTEND",dtnow_string).data(transactionlist))
|
|
|
|
)
|
|
|
|
+ Tag("SECLISTMSGSRSV1").subtag(securitylist);
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::transaction(const MyMoneyTransaction& _t) const
|
|
|
|
{
|
|
|
|
// This method creates a transaction tag using ONLY the elements that importer uses
|
|
|
|
|
|
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
|
|
|
|
//Use this version for bank/cc transactions
|
|
|
|
MyMoneySplit s = _t.splitByAccount( m_account.id(), true );
|
|
|
|
|
|
|
|
//TODO (Ace) Write "investmentTransaction()"...
|
|
|
|
//Use this version for inv transactions
|
|
|
|
//MyMoneySplit s = _t.splitByAccount( m_account.accountList(), true );
|
|
|
|
|
|
|
|
Tag result ("STMTTRN");
|
|
|
|
|
|
|
|
result
|
|
|
|
// This is a temporary hack. I don't use the trntype field in importing at all,
|
|
|
|
// but libofx requires it to be there in order to import the file.
|
|
|
|
.element("TRNTYPE","DEBIT")
|
|
|
|
.element("DTPOSTED",_t.postDate().toString(Qt::ISODate).remove(TQRegExp("[^0-9]")))
|
|
|
|
.element("TRNAMT",s.value().formatMoney(TQString(),2));
|
|
|
|
|
|
|
|
if ( ! _t.bankID().isEmpty() )
|
|
|
|
result.element("FITID",_t.bankID());
|
|
|
|
else
|
|
|
|
result.element("FITID",_t.id());
|
|
|
|
|
|
|
|
if ( ! s.number().isEmpty() )
|
|
|
|
result.element("CHECKNUM",s.number());
|
|
|
|
|
|
|
|
if ( ! s.payeeId().isEmpty() )
|
|
|
|
result.element("NAME",file->payee(s.payeeId()).name());
|
|
|
|
|
|
|
|
if ( ! _t.memo().isEmpty() )
|
|
|
|
result.element("MEMO",_t.memo());
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyOfxConnector::Tag MyMoneyOfxConnector::investmentTransaction(const MyMoneyTransaction& _t) const
|
|
|
|
{
|
|
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
|
|
|
|
//Use this version for inv transactions
|
|
|
|
MyMoneySplit s = _t.splitByAccount( m_account.accountList(), true );
|
|
|
|
|
|
|
|
TQCString stockid = file->account(s.accountId()).currencyId();
|
|
|
|
|
|
|
|
Tag invtran("INVTRAN");
|
|
|
|
invtran.element("FITID",_t.id()).element("DTTRADE",_t.postDate().toString(Qt::ISODate).remove(TQRegExp("[^0-9]")));
|
|
|
|
if ( !_t.memo().isEmpty() )
|
|
|
|
invtran.element("MEMO",_t.memo());
|
|
|
|
|
|
|
|
if ( s.action() == MyMoneySplit::ActionBuyShares )
|
|
|
|
{
|
|
|
|
if ( s.shares().isNegative() )
|
|
|
|
{
|
|
|
|
return Tag("SELLSTOCK")
|
|
|
|
.subtag(Tag("INVSELL")
|
|
|
|
.subtag(invtran)
|
|
|
|
.subtag(Tag("SECID").element("UNITQUEID",stockid).element("UNITQUEIDTYPE","KMYMONEY"))
|
|
|
|
.element("UNITS",TQString(((s.shares())).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("UNITPRICE",TQString((s.value()/s.shares()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.]")))
|
|
|
|
.element("TOTAL",TQString((-s.value()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("SUBACCTSEC","CASH")
|
|
|
|
.element("SUBACCTFUND","CASH"))
|
|
|
|
.element("SELLTYPE","SELL");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return Tag("BUYSTOCK")
|
|
|
|
.subtag(Tag("INVBUY")
|
|
|
|
.subtag(invtran)
|
|
|
|
.subtag(Tag("SECID").element("UNITQUEID",stockid).element("UNITQUEIDTYPE","KMYMONEY"))
|
|
|
|
.element("UNITS",TQString((s.shares()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("UNITPRICE",TQString((s.value()/s.shares()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.]")))
|
|
|
|
.element("TOTAL",TQString((-(s.value())).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("SUBACCTSEC","CASH")
|
|
|
|
.element("SUBACCTFUND","CASH"))
|
|
|
|
.element("BUYTYPE","BUY");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( s.action() == MyMoneySplit::ActionReinvestDividend )
|
|
|
|
{
|
|
|
|
// Should the TOTAL tag really be negative for a REINVEST? That's very strange, but
|
|
|
|
// it's what they look like coming from my bank, and I can't find any information to refute it.
|
|
|
|
|
|
|
|
return Tag("REINVEST")
|
|
|
|
.subtag(invtran)
|
|
|
|
.subtag(Tag("SECID").element("UNITQUEID",stockid).element("UNITQUEIDTYPE","KMYMONEY"))
|
|
|
|
.element("INCOMETYPE","DIV")
|
|
|
|
.element("TOTAL",TQString((-s.value()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("SUBACCTSEC","CASH")
|
|
|
|
.element("UNITS",TQString((s.shares()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.\\-]")))
|
|
|
|
.element("UNITPRICE",TQString((s.value()/s.shares()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9.]")));
|
|
|
|
}
|
|
|
|
else if ( s.action() == MyMoneySplit::ActionDividend )
|
|
|
|
{
|
|
|
|
// find the split with the category, which has the actual amount of the dividend
|
|
|
|
TQValueList<MyMoneySplit> splits = _t.splits();
|
|
|
|
TQValueList<MyMoneySplit>::const_iterator it_split = splits.begin();
|
|
|
|
bool found = false;
|
|
|
|
while( it_split != splits.end() )
|
|
|
|
{
|
|
|
|
TQCString accid = (*it_split).accountId();
|
|
|
|
MyMoneyAccount acc = file->account(accid);
|
|
|
|
if ( acc.accountType() == MyMoneyAccount::Income || acc.accountType() == MyMoneyAccount::Expense )
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++it_split;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( found )
|
|
|
|
return Tag("INCOME")
|
|
|
|
.subtag(invtran)
|
|
|
|
.subtag(Tag("SECID").element("UNITQUEID",stockid).element("UNITQUEIDTYPE","KMYMONEY"))
|
|
|
|
.element("INCOMETYPE","DIV")
|
|
|
|
.element("TOTAL",TQString((-(*it_split).value()).formatMoney(TQString(),2)).remove(TQRegExp("[^0-9\\.\\-]")))
|
|
|
|
.element("SUBACCTSEC","CASH")
|
|
|
|
.element("SUBACCTFUND","CASH");
|
|
|
|
else
|
|
|
|
return Tag("ERROR").element("DETAILS","Unable to determine the amount of this income transaction.");
|
|
|
|
}
|
|
|
|
|
|
|
|
//FIXME: Do something useful with these errors
|
|
|
|
return Tag("ERROR").element("DETAILS","This transaction contains an unsupported action type");
|
|
|
|
}
|
|
|
|
#endif
|