/*************************************************************************** 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. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include <config.h> #endif // ---------------------------------------------------------------------------- // 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 tqDebug("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; }