File indexing completed on 2024-05-12 05:06:45
0001 /* 0002 SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2001-2019 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-FileCopyrightText: 2001 Felix Rodriguez <frodriguez@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2003 Kevin Tambascio <ktambascio@users.sourceforge.net> 0006 SPDX-FileCopyrightText: 2004-2005 Ace Jones <acejones@users.sourceforge.net> 0007 SPDX-FileCopyrightText: 2006 Darren Gould <darren_gould@gmx.de> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "mymoneytransaction.h" 0012 #include "mymoneytransaction_p.h" 0013 0014 // ---------------------------------------------------------------------------- 0015 // QT Includes 0016 0017 #include <QStringList> 0018 #include <QMap> 0019 0020 // ---------------------------------------------------------------------------- 0021 // Project Includes 0022 0023 #include "mymoneyutils.h" 0024 #include "mymoneymoney.h" 0025 #include "mymoneyexception.h" 0026 #include "mymoneyenums.h" 0027 0028 MyMoneyTransaction::MyMoneyTransaction() : 0029 MyMoneyObject(*new MyMoneyTransactionPrivate) 0030 { 0031 } 0032 0033 MyMoneyTransaction::MyMoneyTransaction(const QString &id) : 0034 MyMoneyObject(*new MyMoneyTransactionPrivate, id) 0035 { 0036 } 0037 0038 MyMoneyTransaction::MyMoneyTransaction(const MyMoneyTransaction& other) : 0039 MyMoneyObject(*new MyMoneyTransactionPrivate(*other.d_func()), other.id()), 0040 MyMoneyKeyValueContainer(other) 0041 { 0042 } 0043 0044 MyMoneyTransaction::MyMoneyTransaction(const QString& id, const MyMoneyTransaction& other) : 0045 MyMoneyObject(*new MyMoneyTransactionPrivate(*other.d_func()), id), 0046 MyMoneyKeyValueContainer(other) 0047 { 0048 Q_D(MyMoneyTransaction); 0049 if (d->m_entryDate == QDate()) 0050 d->m_entryDate = QDate::currentDate(); 0051 0052 for (auto& split : d->m_splits) 0053 split.setTransactionId(id); 0054 } 0055 0056 MyMoneyTransaction::~MyMoneyTransaction() 0057 { 0058 } 0059 0060 QDate MyMoneyTransaction::entryDate() const 0061 { 0062 Q_D(const MyMoneyTransaction); 0063 return d->m_entryDate; 0064 } 0065 0066 void MyMoneyTransaction::setEntryDate(const QDate& date) 0067 { 0068 Q_D(MyMoneyTransaction); 0069 d->m_entryDate = date; 0070 } 0071 0072 QDate MyMoneyTransaction::postDate() const 0073 { 0074 Q_D(const MyMoneyTransaction); 0075 return d->m_postDate; 0076 } 0077 0078 void MyMoneyTransaction::setPostDate(const QDate& date) 0079 { 0080 Q_D(MyMoneyTransaction); 0081 d->m_postDate = date; 0082 } 0083 0084 QString MyMoneyTransaction::memo() const 0085 { 0086 Q_D(const MyMoneyTransaction); 0087 return d->m_memo; 0088 } 0089 0090 void MyMoneyTransaction::setMemo(const QString& memo) 0091 { 0092 Q_D(MyMoneyTransaction); 0093 d->m_memo = memo; 0094 } 0095 0096 QList<MyMoneySplit> MyMoneyTransaction::splits() const 0097 { 0098 Q_D(const MyMoneyTransaction); 0099 return d->m_splits; 0100 } 0101 0102 QList<MyMoneySplit>& MyMoneyTransaction::splits() 0103 { 0104 Q_D(MyMoneyTransaction); 0105 return d->m_splits; 0106 } 0107 0108 MyMoneySplit MyMoneyTransaction::firstSplit() const 0109 { 0110 Q_D(const MyMoneyTransaction); 0111 return d->m_splits.first(); 0112 } 0113 0114 uint MyMoneyTransaction::splitCount() const 0115 { 0116 Q_D(const MyMoneyTransaction); 0117 return d->m_splits.count(); 0118 } 0119 0120 uint MyMoneyTransaction::splitCountWithValue() const 0121 { 0122 uint rc = 0; 0123 Q_D(const MyMoneyTransaction); 0124 const auto splitCount = d->m_splits.count(); 0125 for (int split = 0; split < splitCount; ++split) { 0126 rc += (d->m_splits[split].value().isZero() ? 0 : 1); 0127 } 0128 return rc; 0129 } 0130 0131 QString MyMoneyTransaction::commodity() const 0132 { 0133 Q_D(const MyMoneyTransaction); 0134 return d->m_commodity; 0135 } 0136 0137 void MyMoneyTransaction::setCommodity(const QString& commodityId) 0138 { 0139 Q_D(MyMoneyTransaction); 0140 d->m_commodity = commodityId; 0141 d->clearReferences(); 0142 } 0143 0144 QString MyMoneyTransaction::bankID() const 0145 { 0146 Q_D(const MyMoneyTransaction); 0147 return d->m_bankID; 0148 } 0149 0150 void MyMoneyTransaction::setBankID(const QString& bankID) 0151 { 0152 Q_D(MyMoneyTransaction); 0153 d->m_bankID = bankID; 0154 } 0155 0156 bool MyMoneyTransaction::operator == (const MyMoneyTransaction& right) const 0157 { 0158 Q_D(const MyMoneyTransaction); 0159 auto d2 = static_cast<const MyMoneyTransactionPrivate *>(right.d_func()); 0160 return (MyMoneyObject::operator==(right) 0161 && MyMoneyKeyValueContainer::operator==(right) // 0162 && (d->m_commodity == d2->m_commodity) // 0163 && ((d->m_memo.length() == 0 && d2->m_memo.length() == 0) || (d->m_memo == d2->m_memo)) // 0164 && (d->m_splits == d2->m_splits) // 0165 && (d->m_entryDate == d2->m_entryDate) // 0166 && (d->m_postDate == d2->m_postDate)); 0167 } 0168 0169 bool MyMoneyTransaction::operator != (const MyMoneyTransaction& r) const 0170 { 0171 return !(*this == r); 0172 } 0173 0174 bool MyMoneyTransaction::operator< (const MyMoneyTransaction& r) const 0175 { 0176 return postDate() < r.postDate(); 0177 } 0178 0179 bool MyMoneyTransaction::operator<= (const MyMoneyTransaction& r) const 0180 { 0181 return postDate() <= r.postDate(); 0182 } 0183 0184 bool MyMoneyTransaction::operator> (const MyMoneyTransaction& r) const 0185 { 0186 return postDate() > r.postDate(); 0187 } 0188 0189 bool MyMoneyTransaction::accountReferenced(const QString& id) const 0190 { 0191 Q_D(const MyMoneyTransaction); 0192 0193 for (const auto& split : d->m_splits) { 0194 if (split.accountId() == id) 0195 return true; 0196 } 0197 return false; 0198 } 0199 0200 void MyMoneyTransaction::addSplit(MyMoneySplit &split) 0201 { 0202 if (!split.id().isEmpty()) 0203 throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot add split with assigned id '%1' to transaction %2").arg(split.id(), id())); 0204 0205 if (split.accountId().isEmpty()) 0206 throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot add split that does not contain an account reference to transaction %1").arg(id())); 0207 0208 Q_D(MyMoneyTransaction); 0209 MyMoneySplit newSplit(d->nextSplitID(), split); 0210 split = newSplit; 0211 split.setTransactionId(id()); 0212 d->m_splits.append(split); 0213 d->clearReferences(); 0214 } 0215 0216 void MyMoneyTransaction::modifySplit(const MyMoneySplit& split) 0217 { 0218 // This is the other version which allows having more splits referencing 0219 // the same account. 0220 if (split.accountId().isEmpty()) 0221 throw MYMONEYEXCEPTION(QString::fromLatin1("Cannot modify split that does not contain an account reference in transaction %1").arg(id())); 0222 0223 Q_D(MyMoneyTransaction); 0224 for (auto& it_split : d->m_splits) { 0225 if (split.id() == it_split.id()) { 0226 it_split = split; 0227 d->clearReferences(); 0228 return; 0229 } 0230 } 0231 throw MYMONEYEXCEPTION(QString::fromLatin1("Invalid split id '%1' in transaction %2").arg(split.id(), id())); 0232 } 0233 0234 void MyMoneyTransaction::removeSplit(const MyMoneySplit& split) 0235 { 0236 Q_D(MyMoneyTransaction); 0237 for (int end = d->m_splits.size(), i = 0; i < end; ++i) { 0238 if (split.id() == d->m_splits.at(i).id()) { 0239 d->m_splits.removeAt(i); 0240 d->clearReferences(); 0241 return; 0242 } 0243 } 0244 0245 throw MYMONEYEXCEPTION(QString::fromLatin1("Invalid split id '%1' in transaction %2").arg(split.id(), id())); 0246 } 0247 0248 void MyMoneyTransaction::removeSplits() 0249 { 0250 Q_D(MyMoneyTransaction); 0251 d->m_splits.clear(); 0252 d->clearReferences(); 0253 } 0254 0255 MyMoneySplit MyMoneyTransaction::splitByPayee(const QString& payeeId) const 0256 { 0257 Q_D(const MyMoneyTransaction); 0258 for (const auto& split : d->m_splits) { 0259 if (split.payeeId() == payeeId) 0260 return split; 0261 } 0262 throw MYMONEYEXCEPTION(QString::fromLatin1("Split not found for payee '%1' in transaction %2").arg(payeeId, id())); 0263 } 0264 0265 MyMoneySplit MyMoneyTransaction::splitByAccount(const QString& accountId, const bool match) const 0266 { 0267 Q_D(const MyMoneyTransaction); 0268 for (const auto& split : d->m_splits) { 0269 if ((match == true && split.accountId() == accountId) || 0270 (match == false && split.accountId() != accountId)) 0271 return split; 0272 } 0273 throw MYMONEYEXCEPTION(QString::fromLatin1("Split not found for account %1%2 in transaction %3").arg(match ? "" : "!", accountId, id())); 0274 } 0275 0276 MyMoneySplit MyMoneyTransaction::splitByAccount(const QStringList& accountIds, const bool match) const 0277 { 0278 Q_D(const MyMoneyTransaction); 0279 for (const auto& split : d->m_splits) { 0280 if ((match == true && accountIds.contains(split.accountId())) || 0281 (match == false && !accountIds.contains(split.accountId()))) 0282 return split; 0283 } 0284 throw MYMONEYEXCEPTION( 0285 QString::fromLatin1("Split not found for account %1%2...%3 in transaction %4").arg(match ? "" : "!", accountIds.front(), accountIds.back(), id())); 0286 } 0287 0288 MyMoneySplit MyMoneyTransaction::splitById(const QString& splitId) const 0289 { 0290 Q_D(const MyMoneyTransaction); 0291 for (const auto& split : d->m_splits) { 0292 if (split.id() == splitId) 0293 return split; 0294 } 0295 throw MYMONEYEXCEPTION(QString::fromLatin1("Split not found for id '%1' in transaction %2").arg(splitId, id())); 0296 } 0297 0298 QString MyMoneyTransaction::firstSplitID() 0299 { 0300 QString id; 0301 id = 'S' + id.setNum(1).rightJustified(MyMoneyTransactionPrivate::SPLIT_ID_SIZE, '0'); 0302 return id; 0303 } 0304 0305 MyMoneyMoney MyMoneyTransaction::splitSum() const 0306 { 0307 MyMoneyMoney result; 0308 0309 Q_D(const MyMoneyTransaction); 0310 for (const auto& split : d->m_splits) 0311 result += split.value(); 0312 return result; 0313 } 0314 0315 void MyMoneyTransaction::reverse() 0316 { 0317 Q_D(MyMoneyTransaction); 0318 for (MyMoneySplit& split : d->m_splits) { 0319 split.negateValue(); 0320 split.negateShares(); 0321 } 0322 } 0323 0324 bool MyMoneyTransaction::isLoanPayment() const 0325 { 0326 try { 0327 0328 Q_D(const MyMoneyTransaction); 0329 for (const auto& split : d->m_splits) { 0330 if (split.isAmortizationSplit()) 0331 return true; 0332 } 0333 } catch (const MyMoneyException &) { 0334 } 0335 return false; 0336 } 0337 0338 MyMoneySplit MyMoneyTransaction::amortizationSplit() const 0339 { 0340 static MyMoneySplit nullSplit; 0341 0342 Q_D(const MyMoneyTransaction); 0343 for (const auto& split : d->m_splits) { 0344 if (split.isAmortizationSplit() && split.isAutoCalc()) 0345 return split; 0346 } 0347 return nullSplit; 0348 } 0349 0350 MyMoneySplit MyMoneyTransaction::interestSplit() const 0351 { 0352 static MyMoneySplit nullSplit; 0353 0354 Q_D(const MyMoneyTransaction); 0355 for (const auto& split : d->m_splits) { 0356 if (split.isInterestSplit() && split.isAutoCalc()) 0357 return split; 0358 } 0359 return nullSplit; 0360 } 0361 0362 unsigned long MyMoneyTransaction::hash(const QString& txt, unsigned long h) 0363 { 0364 unsigned long g; 0365 0366 for (int i = 0; i < txt.length(); ++i) { 0367 unsigned short uc = txt[i].unicode(); 0368 for (unsigned j = 0; j < 2; ++j) { 0369 unsigned char c = uc & 0xff; 0370 // if either the cell or the row of the Unicode char is 0, stop processing 0371 if (!c) 0372 break; 0373 h = (h << 4) + c; 0374 if ((g = (h & 0xf0000000))) { 0375 h = h ^(g >> 24); 0376 h = h ^ g; 0377 } 0378 uc >>= 8; 0379 } 0380 } 0381 return h; 0382 } 0383 0384 bool MyMoneyTransaction::isStockSplit() const 0385 { 0386 Q_D(const MyMoneyTransaction); 0387 return (d->m_splits.count() == 1 && d->m_splits.first().action() == MyMoneySplit::actionName(eMyMoney::Split::Action::SplitShares)); 0388 } 0389 0390 bool MyMoneyTransaction::isImported() const 0391 { 0392 return value("Imported").toLower() == QString("true"); 0393 } 0394 0395 void MyMoneyTransaction::setImported(bool state) 0396 { 0397 if (state) 0398 setValue("Imported", "true"); 0399 else 0400 deletePair("Imported"); 0401 } 0402 0403 bool MyMoneyTransaction::hasAutoCalcSplit() const 0404 { 0405 Q_D(const MyMoneyTransaction); 0406 0407 for (const auto& split : d->m_splits) 0408 if (split.isAutoCalc()) 0409 return true; 0410 return false; 0411 } 0412 0413 QString MyMoneyTransaction::accountSignature(bool includeSplitCount) const 0414 { 0415 Q_D(const MyMoneyTransaction); 0416 QMap<QString, int> accountList; 0417 for (const auto& split : d->m_splits) 0418 accountList[split.accountId()] += 1; 0419 0420 QMap<QString, int>::const_iterator it_a; 0421 QString rc; 0422 for (it_a = accountList.constBegin(); it_a != accountList.constEnd(); ++it_a) { 0423 if (it_a != accountList.constBegin()) 0424 rc += '-'; 0425 rc += it_a.key(); 0426 if (includeSplitCount) 0427 rc += QString("*%1").arg(*it_a); 0428 } 0429 return rc; 0430 } 0431 0432 QString MyMoneyTransaction::uniqueSortKey() const 0433 { 0434 Q_D(const MyMoneyTransaction); 0435 return uniqueSortKey(postDate(), d->m_id); 0436 } 0437 0438 QString MyMoneyTransaction::uniqueSortKey(const QDate& date, const QString& id) 0439 { 0440 QString year, month, day; 0441 year = year.setNum(date.year()).rightJustified(MyMoneyTransactionPrivate::YEAR_SIZE, '0'); 0442 month = month.setNum(date.month()).rightJustified(MyMoneyTransactionPrivate::MONTH_SIZE, '0'); 0443 day = day.setNum(date.day()).rightJustified(MyMoneyTransactionPrivate::DAY_SIZE, '0'); 0444 const auto key = QString::fromLatin1("%1-%2-%3-%4").arg(year, month, day, id); 0445 return key; 0446 } 0447 0448 bool MyMoneyTransaction::replaceId(const QString& newId, const QString& oldId) 0449 { 0450 auto changed = false; 0451 Q_D(MyMoneyTransaction); 0452 for (MyMoneySplit& split : d->m_splits) 0453 changed |= split.replaceId(newId, oldId); 0454 0455 return changed; 0456 }