You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/mymoney/mymoneyaccount.cpp

749 lines
20 KiB

/***************************************************************************
mymoneyaccount.cpp
-------------------
copyright : (C) 2000 by Michael Edwardes
(C) 2002 by Thomas Baumagrt
email : mte@users.sourceforge.net
ipwizard@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
// ----------------------------------------------------------------------------
// TQt Includes
#include <tqregexp.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
// ----------------------------------------------------------------------------
// TDE Includes
#include <tdelocale.h>
// ----------------------------------------------------------------------------
// Project Includes
#include <kmymoney/mymoneyexception.h>
#include <kmymoney/mymoneyaccount.h>
#include <kmymoney/mymoneysplit.h>
MyMoneyAccount::MyMoneyAccount() :
m_fraction(-1)
{
m_accountType = UnknownAccountType;
}
MyMoneyAccount::~MyMoneyAccount()
{
}
MyMoneyAccount::MyMoneyAccount(const TQString& id, const MyMoneyAccount& right) :
MyMoneyObject(id)
{
*this = right;
setId(id);
}
MyMoneyAccount::MyMoneyAccount(const TQDomElement& node) :
MyMoneyObject(node),
MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement()),
m_fraction(-1)
{
if("ACCOUNT" != node.tagName())
throw new MYMONEYEXCEPTION("Node was not ACCOUNT");
setName(node.attribute("name"));
// tqDebug(TQString("Reading information for account %1").arg(acc.name()));
setParentAccountId(TQStringEmpty(node.attribute("parentaccount")));
setLastModified(stringToDate(TQStringEmpty(node.attribute("lastmodified"))));
setLastReconciliationDate(stringToDate(TQStringEmpty(node.attribute("lastreconciled"))));
if(!m_lastReconciliationDate.isValid()) {
// for some reason, I was unable to access our own kvp at this point through
// the value() method. It always returned empty strings. The workaround for
// this is to construct a local kvp the same way as we have done before and
// extract the value from it.
//
// Since we want to get rid of the lastStatementDate record anyway, this seems
// to be ok for now. (ipwizard - 2008-08-14)
TQString txt = MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement()).value("lastStatementDate");
if(!txt.isEmpty()) {
setLastReconciliationDate(TQDate::fromString(txt, TQt::ISODate));
}
}
setInstitutionId(TQStringEmpty(node.attribute("institution")));
setNumber(TQStringEmpty(node.attribute("number")));
setOpeningDate(stringToDate(TQStringEmpty(node.attribute("opened"))));
setCurrencyId(TQStringEmpty(node.attribute("currency")));
TQString tmp = TQStringEmpty(node.attribute("type"));
bool bOK = false;
int type = tmp.toInt(&bOK);
if(bOK) {
setAccountType(static_cast<MyMoneyAccount::accountTypeE>(type));
} else {
tqWarning(TQString("XMLREADER: Account %1 had invalid or no account type information.").arg(name()));
}
if(node.hasAttribute("openingbalance")) {
if(!MyMoneyMoney(node.attribute("openingbalance")).isZero()) {
TQString msg = i18n("Account %1 contains an opening balance. Please use a KMyMoney version >= 0.8 and < 0.9 to correct the problem.").arg(m_name);
throw new MYMONEYEXCEPTION(msg);
}
}
setDescription(node.attribute("description"));
m_id = TQStringEmpty(node.attribute("id"));
// tqDebug(TQString("Account %1 has id of %2, type of %3, parent is %4.").arg(acc.name()).arg(id).arg(type).arg(acc.parentAccountId()));
// Process any Sub-Account information found inside the account entry.
m_accountList.clear();
TQDomNodeList nodeList = node.elementsByTagName("SUBACCOUNTS");
if(nodeList.count() > 0) {
nodeList = nodeList.item(0).toElement().elementsByTagName("SUBACCOUNT");
for(unsigned int i = 0; i < nodeList.count(); ++i) {
addAccountId(TQString(nodeList.item(i).toElement().attribute("id")));
}
}
nodeList = node.elementsByTagName("ONLINEBANKING");
if(nodeList.count() > 0) {
TQDomNamedNodeMap attributes = nodeList.item(0).toElement().attributes();
for(unsigned int i = 0; i < attributes.count(); ++i) {
const TQDomAttr& it_attr = attributes.item(i).toAttr();
m_onlineBankingSettings.setValue(it_attr.name().utf8(), it_attr.value());
}
}
}
void MyMoneyAccount::setName(const TQString& name)
{
m_name = name;
}
void MyMoneyAccount::setNumber(const TQString& number)
{
m_number = number;
}
void MyMoneyAccount::setDescription(const TQString& desc)
{
m_description = desc;
}
void MyMoneyAccount::setInstitutionId(const TQString& id)
{
m_institution = id;
}
void MyMoneyAccount::setLastModified(const TQDate& date)
{
m_lastModified = date;
}
void MyMoneyAccount::setOpeningDate(const TQDate& date)
{
m_openingDate = date;
}
void MyMoneyAccount::setLastReconciliationDate(const TQDate& date)
{
// FIXME: for a limited time (maybe until we delivered 1.0) we
// keep the last reconciliation date also in the KVP for backward
// compatability. After that, the setValue() statemetn should be removed
// and the XML ctor should remove the value completely from the KVP
setValue("lastStatementDate", date.toString(TQt::ISODate));
m_lastReconciliationDate = date;
}
void MyMoneyAccount::setParentAccountId(const TQString& parent)
{
m_parentAccount = parent;
}
void MyMoneyAccount::setAccountType(const accountTypeE type)
{
m_accountType = type;
}
void MyMoneyAccount::addAccountId(const TQString& account)
{
if(!m_accountList.contains(account))
m_accountList += account;
}
void MyMoneyAccount::removeAccountIds(void)
{
m_accountList.clear();
}
void MyMoneyAccount::removeAccountId(const TQString& account)
{
TQStringList::Iterator it;
it = m_accountList.find(account);
if(it != m_accountList.end())
m_accountList.remove(it);
}
MyMoneyAccount::accountTypeE MyMoneyAccount::accountGroup(MyMoneyAccount::accountTypeE type)
{
switch(type) {
case MyMoneyAccount::Checkings:
case MyMoneyAccount::Savings:
case MyMoneyAccount::Cash:
case MyMoneyAccount::Currency:
case MyMoneyAccount::Investment:
case MyMoneyAccount::MoneyMarket:
case MyMoneyAccount::CertificateDep:
case MyMoneyAccount::AssetLoan:
case MyMoneyAccount::Stock:
return MyMoneyAccount::Asset;
case MyMoneyAccount::CreditCard:
case MyMoneyAccount::Loan:
return MyMoneyAccount::Liability;
default:
return type;
}
}
bool MyMoneyAccount::operator == (const MyMoneyAccount& right) const
{
return (MyMoneyKeyValueContainer::operator==(right) &&
MyMoneyObject::operator==(right) &&
(m_accountList == right.m_accountList) &&
(m_accountType == right.m_accountType) &&
(m_lastModified == right.m_lastModified) &&
(m_lastReconciliationDate == right.m_lastReconciliationDate) &&
((m_name.length() == 0 && right.m_name.length() == 0) || (m_name == right.m_name)) &&
((m_number.length() == 0 && right.m_number.length() == 0) || (m_number == right.m_number)) &&
((m_description.length() == 0 && right.m_description.length() == 0) || (m_description == right.m_description)) &&
(m_openingDate == right.m_openingDate) &&
(m_parentAccount == right.m_parentAccount) &&
(m_currencyId == right.m_currencyId) &&
(m_institution == right.m_institution) );
}
MyMoneyAccount::accountTypeE MyMoneyAccount::accountGroup(void) const
{
return accountGroup(m_accountType);
}
void MyMoneyAccount::setCurrencyId(const TQString& id)
{
m_currencyId = id;
}
bool MyMoneyAccount::isAssetLiability(void) const
{
return accountGroup() == Asset || accountGroup() == Liability;
}
bool MyMoneyAccount::isIncomeExpense(void) const
{
return accountGroup() == Income || accountGroup() == Expense;
}
bool MyMoneyAccount::isLoan(void) const
{
return accountType() == Loan || accountType() == AssetLoan;
}
bool MyMoneyAccount::isInvest(void) const
{
return accountType() == Stock;
}
MyMoneyAccountLoan::MyMoneyAccountLoan(const MyMoneyAccount& acc)
: MyMoneyAccount(acc)
{
}
const MyMoneyMoney MyMoneyAccountLoan::loanAmount(void) const
{
return MyMoneyMoney(value("loan-amount"));
}
void MyMoneyAccountLoan::setLoanAmount(const MyMoneyMoney& amount)
{
setValue("loan-amount", amount.toString());
}
const MyMoneyMoney MyMoneyAccountLoan::interestRate(const TQDate& date) const
{
MyMoneyMoney rate;
TQString key;
TQString val;
if(!date.isValid())
return rate;
key.sprintf("ir-%04d-%02d-%02d", date.year(), date.month(), date.day());
TQRegExp regExp("ir-(\\d{4})-(\\d{2})-(\\d{2})");
TQMap<TQString, TQString>::ConstIterator it;
for(it = pairs().begin(); it != pairs().end(); ++it) {
if(regExp.search(it.key()) > -1) {
if(it.key() <= key)
val = *it;
else
break;
} else if(!val.isEmpty())
break;
}
if(!val.isEmpty()) {
rate = MyMoneyMoney(val);
}
return rate;
}
void MyMoneyAccountLoan::setInterestRate(const TQDate& date, const MyMoneyMoney& value)
{
if(!date.isValid())
return;
TQString key;
key.sprintf("ir-%04d-%02d-%02d", date.year(), date.month(), date.day());
setValue(key, value.toString());
}
MyMoneyAccountLoan::interestDueE MyMoneyAccountLoan::interestCalculation(void) const
{
TQString payTime(value("interest-calculation"));
if(payTime == "paymentDue")
return paymentDue;
return paymentReceived;
}
void MyMoneyAccountLoan::setInterestCalculation(const MyMoneyAccountLoan::interestDueE onReception)
{
if(onReception == paymentDue)
setValue("interest-calculation", "paymentDue");
else
setValue("interest-calculation", "paymentReceived");
}
const TQDate MyMoneyAccountLoan::nextInterestChange(void) const
{
TQDate rc;
TQRegExp regExp("(\\d{4})-(\\d{2})-(\\d{2})");
if(regExp.search(value("interest-nextchange")) != -1) {
rc.setYMD(regExp.cap(1).toInt(), regExp.cap(2).toInt(), regExp.cap(3).toInt());
}
return rc;
}
void MyMoneyAccountLoan::setNextInterestChange(const TQDate& date)
{
setValue("interest-nextchange", date.toString(TQt::ISODate));
}
int MyMoneyAccountLoan::interestChangeFrequency(int* unit) const
{
int rc = -1;
if(unit)
*unit = 1;
TQRegExp regExp("(\\d+)/(\\d{1})");
if(regExp.search(value("interest-changefrequency")) != -1) {
rc = regExp.cap(1).toInt();
if(unit != 0) {
*unit = regExp.cap(2).toInt();
}
}
return rc;
}
void MyMoneyAccountLoan::setInterestChangeFrequency(const int amount, const int unit)
{
TQString val;
val.sprintf("%d/%d", amount, unit);
setValue("interest-changeFrequency", val);
}
const TQString MyMoneyAccountLoan::schedule(void) const
{
return TQString(value("schedule").latin1());
}
void MyMoneyAccountLoan::setSchedule(const TQString& sched)
{
setValue("schedule", sched);
}
bool MyMoneyAccountLoan::fixedInterestRate(void) const
{
// make sure, that an empty kvp element returns true
return !(value("fixed-interest") == "no");
}
void MyMoneyAccountLoan::setFixedInterestRate(const bool fixed)
{
setValue("fixed-interest", fixed ? "yes" : "no");
if(fixed) {
deletePair("interest-nextchange");
deletePair("interest-changeFrequency");
}
}
const MyMoneyMoney MyMoneyAccountLoan::finalPayment(void) const
{
return MyMoneyMoney(value("final-payment"));
}
void MyMoneyAccountLoan::setFinalPayment(const MyMoneyMoney& finalPayment)
{
setValue("final-payment", finalPayment.toString());
}
unsigned int MyMoneyAccountLoan::term(void) const
{
return value("term").toUInt();
}
void MyMoneyAccountLoan::setTerm(const unsigned int payments)
{
setValue("term", TQString::number(payments));
}
const MyMoneyMoney MyMoneyAccountLoan::periodicPayment(void) const
{
return MyMoneyMoney(value("periodic-payment"));
}
void MyMoneyAccountLoan::setPeriodicPayment(const MyMoneyMoney& payment)
{
setValue("periodic-payment", payment.toString());
}
const TQString MyMoneyAccountLoan::payee(void) const
{
return value("payee");
}
void MyMoneyAccountLoan::setPayee(const TQString& payee)
{
setValue("payee", payee);
}
const TQString MyMoneyAccountLoan::interestAccountId(void) const
{
return TQString::null;
}
void MyMoneyAccountLoan::setInterestAccountId(const TQString& /* id */)
{
}
bool MyMoneyAccountLoan::hasReferenceTo(const TQString& id) const
{
return MyMoneyAccount::hasReferenceTo(id)
|| (id == payee())
|| (id == schedule());
}
void MyMoneyAccountLoan::setInterestCompounding(int frequency)
{
setValue("compoundingFrequency", TQString("%1").arg(frequency));
}
int MyMoneyAccountLoan::interestCompounding(void) const
{
return value("compoundingFrequency").toInt();
}
void MyMoneyAccount::writeXML(TQDomDocument& document, TQDomElement& parent) const
{
TQDomElement el = document.createElement("ACCOUNT");
writeBaseXML(document, el);
el.setAttribute("parentaccount", parentAccountId());
el.setAttribute("lastreconciled", dateToString(lastReconciliationDate()));
el.setAttribute("lastmodified", dateToString(lastModified()));
el.setAttribute("institution", institutionId());
el.setAttribute("opened", dateToString(openingDate()));
el.setAttribute("number", number());
// el.setAttribute("openingbalance", openingBalance().toString());
el.setAttribute("type", accountType());
el.setAttribute("name", name());
el.setAttribute("description", description());
if(!currencyId().isEmpty())
el.setAttribute("currency", currencyId());
//Add in subaccount information, if this account has subaccounts.
if(accountCount())
{
TQDomElement subAccounts = document.createElement("SUBACCOUNTS");
TQStringList::ConstIterator it;
for(it = accountList().begin(); it != accountList().end(); ++it)
{
TQDomElement temp = document.createElement("SUBACCOUNT");
temp.setAttribute("id", (*it));
subAccounts.appendChild(temp);
}
el.appendChild(subAccounts);
}
// Write online banking settings
if(m_onlineBankingSettings.pairs().count()) {
TQDomElement onlinesettings = document.createElement("ONLINEBANKING");
TQMap<TQString,TQString>::const_iterator it_key = m_onlineBankingSettings.pairs().begin();
while ( it_key != m_onlineBankingSettings.pairs().end() ) {
onlinesettings.setAttribute(it_key.key(), it_key.data());
++it_key;
}
el.appendChild(onlinesettings);
}
// FIXME drop the lastStatementDate record from the KVP when it is
// not stored there after setLastReconciliationDate() has been changed
// See comment there when this will happen
// deletePair("lastStatementDate");
//Add in Key-Value Pairs for accounts.
MyMoneyKeyValueContainer::writeXML(document, el);
parent.appendChild(el);
}
bool MyMoneyAccount::hasReferenceTo(const TQString& id) const
{
return (id == m_institution) || (id == m_parentAccount) || (id == m_currencyId);
}
void MyMoneyAccount::setOnlineBankingSettings(const MyMoneyKeyValueContainer& values)
{
m_onlineBankingSettings = values;
}
const MyMoneyKeyValueContainer& MyMoneyAccount::onlineBankingSettings(void) const
{
return m_onlineBankingSettings;
}
void MyMoneyAccount::setClosed(bool closed)
{
if(closed)
setValue("mm-closed", "yes");
else
deletePair("mm-closed");
}
bool MyMoneyAccount::isClosed(void) const
{
return !(value("mm-closed").isEmpty());
}
int MyMoneyAccount::fraction(const MyMoneySecurity& sec) const
{
int fraction;
if(m_accountType == Cash)
fraction = sec.smallestCashFraction();
else
fraction = sec.smallestAccountFraction();
return fraction;
}
int MyMoneyAccount::fraction(const MyMoneySecurity& sec)
{
if(m_accountType == Cash)
m_fraction = sec.smallestCashFraction();
else
m_fraction = sec.smallestAccountFraction();
return m_fraction;
}
int MyMoneyAccount::fraction(void) const
{
Q_ASSERT(m_fraction != -1);
return m_fraction;
}
bool MyMoneyAccount::isCategory(void) const
{
return m_accountType == Income || m_accountType == Expense;
}
TQString MyMoneyAccount::brokerageName(void) const
{
if(m_accountType == Investment)
return TQString("%1 (%2)").arg(m_name, i18n("Brokerage (suffix for account names)", "Brokerage"));
return m_name;
}
void MyMoneyAccount::adjustBalance(const MyMoneySplit& s, bool reverse)
{
if(s.action() == MyMoneySplit::ActionSplitShares) {
if(reverse)
m_balance = m_balance / s.shares();
else
m_balance = m_balance * s.shares();
} else {
if(reverse)
m_balance -= s.shares();
else
m_balance += s.shares();
}
}
TQPixmap MyMoneyAccount::accountPixmap(bool reconcileFlag, int size) const
{
TQString icon;
switch(accountType()) {
default:
if(accountGroup() == MyMoneyAccount::Asset)
icon = "account-types_asset";
else
icon = "account-types_liability";
break;
case MyMoneyAccount::Investment:
case MyMoneyAccount::Stock:
case MyMoneyAccount::MoneyMarket:
case MyMoneyAccount::CertificateDep:
icon = "account-types_investments";
break;
case MyMoneyAccount::Checkings:
icon = "account-types_checking";
break;
case MyMoneyAccount::Savings:
icon = "account-types_savings";
break;
case MyMoneyAccount::AssetLoan:
case MyMoneyAccount::Loan:
icon = "account-types_loan";
break;
case MyMoneyAccount::CreditCard:
icon = "account-types_credit-card";
break;
case MyMoneyAccount::Asset:
icon = "account-types_asset";
break;
case MyMoneyAccount::Cash:
icon = "account-types_cash";
break;
case MyMoneyAccount::Income:
icon = "account-types_income";
break;
case MyMoneyAccount::Expense:
icon = "account-types_expense";
break;
case MyMoneyAccount::Equity:
icon = "account";
break;
}
TQPixmap result = DesktopIcon(icon, size);
if(isClosed()) {
TQPixmap ovly = DesktopIcon("account-types_closed", size);
bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
} else if(reconcileFlag) {
TQPixmap ovly = DesktopIcon("account-types_reconcile.png", size);
bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
} else if(!onlineBankingSettings().value("provider").isEmpty()) {
TQPixmap ovly = DesktopIcon("account-types_online.png", size);
bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
}
return result;
}
TQString MyMoneyAccount::accountTypeToString(const MyMoneyAccount::accountTypeE accountType)
{
TQString returnString;
switch (accountType) {
case MyMoneyAccount::Checkings:
returnString = i18n("Checking");
break;
case MyMoneyAccount::Savings:
returnString = i18n("Savings");
break;
case MyMoneyAccount::CreditCard:
returnString = i18n("Credit Card");
break;
case MyMoneyAccount::Cash:
returnString = i18n("Cash");
break;
case MyMoneyAccount::Loan:
returnString = i18n("Loan");
break;
case MyMoneyAccount::CertificateDep:
returnString = i18n("Certificate of Deposit");
break;
case MyMoneyAccount::Investment:
returnString = i18n("Investment");
break;
case MyMoneyAccount::MoneyMarket:
returnString = i18n("Money Market");
break;
case MyMoneyAccount::Asset:
returnString = i18n("Asset");
break;
case MyMoneyAccount::Liability:
returnString = i18n("Liability");
break;
case MyMoneyAccount::Currency:
returnString = i18n("Currency");
break;
case MyMoneyAccount::Income:
returnString = i18n("Income");
break;
case MyMoneyAccount::Expense:
returnString = i18n("Expense");
break;
case MyMoneyAccount::AssetLoan:
returnString = i18n("Investment Loan");
break;
case MyMoneyAccount::Stock:
returnString = i18n("Stock");
break;
case MyMoneyAccount::Equity:
returnString = i18n("Equity");
break;
default:
returnString = i18n("Unknown");
}
return returnString;
}