|
|
|
/***************************************************************************
|
|
|
|
|
|
|
|
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::tqcurrentDate();
|
|
|
|
|
|
|
|
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'").tqarg(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'").tqarg(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'").tqarg(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").tqarg(match?"":"!").tqarg(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").tqarg(match?"":"!").tqarg(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'").tqarg(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].tqunicode();
|
|
|
|
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").tqarg(*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;
|
|
|
|
}
|