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/mymoneytransaction.cpp

485 lines
14 KiB

/***************************************************************************
mymoneytransaction.cpp
-------------------
copyright : (C) 2000 by Michael Edwardes,
2002 by Thomas Baumgart
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. *
* *
***************************************************************************/
// ----------------------------------------------------------------------------
// QT Includes
// ----------------------------------------------------------------------------
// Project Includes
#include "mymoneytransaction.h"
MyMoneyTransaction::MyMoneyTransaction() :
MyMoneyObject()
{
m_nextSplitID = 1;
m_entryDate = TQDate();
m_postDate = TQDate();
}
MyMoneyTransaction::MyMoneyTransaction(const TQString id, const MyMoneyTransaction& transaction) :
MyMoneyObject(id)
{
*this = transaction;
m_id = id;
if(m_entryDate == TQDate())
m_entryDate = TQDate::currentDate();
TQValueList<MyMoneySplit>::Iterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
(*it).setTransactionId(id);
}
}
MyMoneyTransaction::MyMoneyTransaction(const TQDomElement& node, const bool forceId) :
MyMoneyObject(node, forceId)
{
if("TRANSACTION" != node.tagName())
throw new MYMONEYEXCEPTION("Node was not TRANSACTION");
m_nextSplitID = 1;
m_postDate = stringToDate(node.attribute("postdate"));
m_entryDate = stringToDate(node.attribute("entrydate"));
m_bankID = TQStringEmpty(node.attribute("bankid"));
m_memo = TQStringEmpty(node.attribute("memo"));
m_commodity = TQStringEmpty(node.attribute("commodity"));
TQDomNode child = node.firstChild();
while ( !child.isNull() && child.isElement() )
{
TQDomElement c = child.toElement();
if(c.tagName() == "SPLITS") {
// Process any split information found inside the transaction entry.
TQDomNodeList nodeList = c.elementsByTagName("SPLIT");
for(unsigned int i = 0; i < nodeList.count(); ++i) {
MyMoneySplit s(nodeList.item(i).toElement());
if(!m_bankID.isEmpty())
s.setBankID(m_bankID);
if(!s.accountId().isEmpty())
addSplit(s);
else
qDebug("Dropped split because it did not have an account id");
}
} else if(c.tagName() == "KEYVALUEPAIRS") {
MyMoneyKeyValueContainer kvp(c);
setPairs(kvp.pairs());
}
child = child.nextSibling();
}
m_bankID = TQString();
}
MyMoneyTransaction::~MyMoneyTransaction()
{
}
bool MyMoneyTransaction::operator == (const MyMoneyTransaction& right) const
{
return (MyMoneyObject::operator==(right) &&
MyMoneyKeyValueContainer::operator==(right) &&
(m_commodity == right.m_commodity) &&
((m_memo.length() == 0 && right.m_memo.length() == 0) || (m_memo == right.m_memo)) &&
(m_splits == right.m_splits) &&
(m_entryDate == right.m_entryDate) &&
(m_postDate == right.m_postDate) );
}
bool MyMoneyTransaction::accountReferenced(const TQString& id) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).accountId() == id)
return true;
}
return false;
}
void MyMoneyTransaction::addSplit(MyMoneySplit& split)
{
if(!split.id().isEmpty())
throw new MYMONEYEXCEPTION("Cannot add split with assigned id (" + split.id() + ")");
/*
TQValueList<MyMoneySplit>::Iterator it;
// if the account referenced in this split is already
// referenced in another split, we add the amount of
// this split to the other one. All other data contained
// in the new split will be discarded.
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).accountId() == split.accountId()) {
(*it).setValue((*it).value()+split.value());
split = (*it);
return;
}
}
*/
if(split.accountId().isEmpty())
throw new MYMONEYEXCEPTION("Cannot add split that does not contain an account reference");
MyMoneySplit newSplit(nextSplitID(), split);
split = newSplit;
split.setTransactionId(id());
m_splits.append(split);
}
void MyMoneyTransaction::modifySplit(MyMoneySplit& split)
{
// This version of the routine allows only a single
// split to reference one account. If a second split
// is modified to reference an account already referenced
// by another split, the values will be added and the
// duplicate removed.
/*
TQValueList<MyMoneySplit>::Iterator it;
TQValueList<MyMoneySplit>::Iterator self = m_splits.end();
TQValueList<MyMoneySplit>::Iterator dup = self;
bool duplicateAccount = false;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if(split.id() == (*it).id()) {
self = it;
} else if(split.accountId() == (*it).accountId()) {
(*it).setValue((*it).value() + split.value());
dup = it;
duplicateAccount = true;
}
}
if(self == m_splits.end())
throw new MYMONEYEXCEPTION("Invalid split id '" + split.id() + "'");
if(duplicateAccount) {
m_splits.remove(self);
split = *dup;
} else
*self = split;
*/
// This is the other version which allows having more splits referencing
// the same account.
if(split.accountId().isEmpty())
throw new MYMONEYEXCEPTION("Cannot modify split that does not contain an account reference");
TQValueList<MyMoneySplit>::Iterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if(split.id() == (*it).id()) {
*it = split;
return;
}
}
throw new MYMONEYEXCEPTION(TQString("Invalid split id '%1'").arg(split.id()));
}
void MyMoneyTransaction::removeSplit(const MyMoneySplit& split)
{
TQValueList<MyMoneySplit>::Iterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if(split.id() == (*it).id()) {
m_splits.remove(it);
break;
}
}
if(it == m_splits.end())
throw new MYMONEYEXCEPTION(TQString("Invalid split id '%1'").arg(split.id()));
}
void MyMoneyTransaction::removeSplits(void)
{
m_splits.clear();
m_nextSplitID = 1;
}
const MyMoneySplit& MyMoneyTransaction::splitByPayee(const TQString& payeeId) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).payeeId() == payeeId)
return *it;
}
throw new MYMONEYEXCEPTION(TQString("Split not found for payee '%1'").arg(TQString(payeeId)));
}
const MyMoneySplit& MyMoneyTransaction::splitByAccount(const TQString& accountId, const bool match) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if(match == true && (*it).accountId() == accountId)
return *it;
if(match == false && (*it).accountId() != accountId)
return *it;
}
throw new MYMONEYEXCEPTION(TQString("Split not found for account %1%2").arg(match?"":"!").arg(TQString(accountId)));
}
const MyMoneySplit& MyMoneyTransaction::splitByAccount(const TQStringList& accountIds, const bool match) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if(match == true && accountIds.contains((*it).accountId()) )
return *it;
if(match == false && !accountIds.contains((*it).accountId()))
return *it;
}
throw new MYMONEYEXCEPTION(TQString("Split not found for account %1%1...%2").arg(match?"":"!").arg(accountIds.front(),accountIds.back()));
}
const MyMoneySplit& MyMoneyTransaction::splitById(const TQString& splitId) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).id() == splitId)
return *it;
}
throw new MYMONEYEXCEPTION(TQString("Split not found for id '%1'").arg(TQString(splitId)));
}
const TQString MyMoneyTransaction::nextSplitID()
{
TQString id;
id = "S" + TQString(id.setNum(m_nextSplitID++)).rightJustify(SPLIT_ID_SIZE, '0');
return id;
}
const TQString MyMoneyTransaction::firstSplitID()
{
TQString id;
id = "S" + TQString(id.setNum(1)).rightJustify(SPLIT_ID_SIZE, '0');
return id;
}
const MyMoneyMoney MyMoneyTransaction::splitSum(void) const
{
MyMoneyMoney result(0);
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
result += (*it).value();
}
return result;
}
void MyMoneyTransaction::setPostDate(const TQDate& date) { m_postDate = date; }
void MyMoneyTransaction::setEntryDate(const TQDate& date) { m_entryDate = date; }
void MyMoneyTransaction::setMemo(const TQString& memo) { m_memo = memo; }
bool MyMoneyTransaction::isLoanPayment(void) const
{
try {
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).isAmortizationSplit())
return true;
}
} catch (MyMoneyException *e) {
delete e;
}
return false;
}
const MyMoneySplit& MyMoneyTransaction::amortizationSplit(void) const
{
static MyMoneySplit nullSplit;
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).isAmortizationSplit() && (*it).isAutoCalc())
return *it;
}
return nullSplit;
}
const MyMoneySplit& MyMoneyTransaction::interestSplit(void) const
{
static MyMoneySplit nullSplit;
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).isInterestSplit() && (*it).isAutoCalc())
return *it;
}
return nullSplit;
}
unsigned long MyMoneyTransaction::hash(const TQString& txt, unsigned long h)
{
unsigned long g;
for(unsigned i=0; i < txt.length(); ++i) {
unsigned short uc = txt[i].unicode();
for(unsigned j = 0; j < 2; ++j) {
unsigned char c = uc & 0xff;
// if either the cell or the row of the Unicode char is 0, stop processing
if(!c)
break;
h = (h << 4) + c;
if( (g = (h & 0xf0000000)) ) {
h = h ^ (g >> 24);
h = h ^ g;
}
uc >>= 8;
}
}
return h;
}
bool MyMoneyTransaction::isStockSplit(void) const
{
return (m_splits.count() == 1 && m_splits[0].action() == MyMoneySplit::ActionSplitShares);
}
bool MyMoneyTransaction::isImported(void) const
{
return value("Imported").lower() == TQString("true");
}
void MyMoneyTransaction::setImported(bool state)
{
if(state)
setValue("Imported", "true");
else
deletePair("Imported");
}
bool MyMoneyTransaction::isDuplicate(const MyMoneyTransaction& r) const
{
bool rc = true;
if(splitCount() != r.splitCount()) {
rc = false;
} else {
if(abs(m_postDate.daysTo(r.postDate())) > 3) {
rc = false;
} else {
unsigned long accHash[2];
unsigned long valHash[2];
unsigned long numHash[2];
for(int i = 0; i < 2; ++i)
accHash[i] = valHash[i] = numHash[i] = 0;
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = splits().begin(); it != splits().end(); ++it) {
accHash[0] += hash((*it).accountId());
valHash[0] += hash((*it).value().formatMoney("", 4));
numHash[0] += hash((*it).number());
}
for(it = r.splits().begin(); it != r.splits().end(); ++it) {
accHash[1] += hash((*it).accountId());
valHash[1] += hash((*it).value().formatMoney("", 4));
numHash[1] += hash((*it).number());
}
if(accHash[0] != accHash[1]
|| valHash[0] != valHash[1]
|| numHash[0] != numHash[1]
) {
rc = false;
}
}
}
return rc;
}
void MyMoneyTransaction::writeXML(TQDomDocument& document, TQDomElement& parent) const
{
TQDomElement el = document.createElement("TRANSACTION");
writeBaseXML(document, el);
el.setAttribute("postdate", dateToString(m_postDate));
el.setAttribute("memo", m_memo);
el.setAttribute("entrydate", dateToString(m_entryDate));
el.setAttribute("commodity", m_commodity);
TQDomElement splits = document.createElement("SPLITS");
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
(*it).writeXML(document, splits);
}
el.appendChild(splits);
MyMoneyKeyValueContainer::writeXML(document, el);
parent.appendChild(el);
}
bool MyMoneyTransaction::hasReferenceTo(const TQString& id) const
{
TQValueList<MyMoneySplit>::const_iterator it;
bool rc = (id == m_commodity);
for(it = m_splits.begin(); rc == false && it != m_splits.end(); ++it) {
rc = (*it).hasReferenceTo(id);
}
return rc;
}
bool MyMoneyTransaction::hasAutoCalcSplit(void) const
{
TQValueList<MyMoneySplit>::ConstIterator it;
for(it = m_splits.begin(); it != m_splits.end(); ++it) {
if((*it).isAutoCalc())
return true;
}
return false;
}
TQString MyMoneyTransaction::accountSignature(bool includeSplitCount) const
{
TQMap<TQString, int> accountList;
TQValueList<MyMoneySplit>::const_iterator it_s;
for(it_s = m_splits.begin(); it_s != m_splits.end(); ++it_s) {
accountList[(*it_s).accountId()] += 1;
}
TQMap<TQString, int>::const_iterator it_a;
TQString rc;
for(it_a = accountList.begin(); it_a != accountList.end(); ++it_a) {
if(it_a != accountList.begin())
rc += "-";
rc += it_a.key();
if(includeSplitCount)
rc += TQString("*%1").arg(*it_a);
}
return rc;
}
TQString MyMoneyTransaction::uniqueSortKey(void) const
{
TQString year, month, day, key;
const TQDate& postdate = postDate();
year = TQString(year.setNum(postdate.year())).rightJustify(YEAR_SIZE, '0');
month = TQString(month.setNum(postdate.month())).rightJustify(MONTH_SIZE, '0');
day = TQString(day.setNum(postdate.day())).rightJustify(DAY_SIZE, '0');
key = year + "-" + month + "-" + day + "-" + m_id;
return key;
}