File indexing completed on 2024-05-12 05:06:46
0001 /* 0002 SPDX-FileCopyrightText: 2003-2019 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2004 Ace Jones <acejones@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2008-2010 Alvaro Soliverez <asoliverez@gmail.com> 0005 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "mymoneytransactionfilter.h" 0010 0011 // ---------------------------------------------------------------------------- 0012 // QT Includes 0013 0014 #include <QDate> 0015 #include <QDebug> 0016 #include <QFlags> 0017 #include <QRegularExpression> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Includes 0021 0022 // ---------------------------------------------------------------------------- 0023 // Project Includes 0024 0025 #include "mymoneymoney.h" 0026 #include "mymoneyfile.h" 0027 #include "mymoneyaccount.h" 0028 #include "mymoneysecurity.h" 0029 #include "mymoneypayee.h" 0030 #include "mymoneytag.h" 0031 #include "mymoneytransaction.h" 0032 #include "mymoneysplit.h" 0033 #include "mymoneyenums.h" 0034 0035 class MyMoneyTransactionFilterPrivate { 0036 0037 public: 0038 MyMoneyTransactionFilterPrivate() 0039 : m_reportAllSplits(false) 0040 , m_considerCategory(false) 0041 , m_considerCategorySplits(false) 0042 , m_matchOnly(false) 0043 , m_treatTransfersAsIncomeExpense(false) 0044 , m_matchingSplitsCount(0) 0045 , m_invertText(false) 0046 , m_filterIsRegExp(false) 0047 { 0048 } 0049 0050 MyMoneyTransactionFilter::FilterSet m_filterSet; 0051 bool m_reportAllSplits; 0052 bool m_considerCategory; 0053 bool m_considerCategorySplits; 0054 bool m_matchOnly; 0055 bool m_treatTransfersAsIncomeExpense; 0056 0057 uint m_matchingSplitsCount; 0058 0059 QRegularExpression m_text; 0060 bool m_invertText; 0061 bool m_filterIsRegExp; 0062 QHash<QString, QString> m_accounts; 0063 QHash<QString, QString> m_payees; 0064 QHash<QString, QString> m_tags; 0065 QHash<QString, QString> m_categories; 0066 QHash<int, QString> m_states; 0067 QHash<int, QString> m_types; 0068 QHash<int, QString> m_validity; 0069 QString m_fromNr; 0070 QString m_toNr; 0071 QDate m_fromDate; 0072 QDate m_toDate; 0073 MyMoneyMoney m_fromAmount; 0074 MyMoneyMoney m_toAmount; 0075 }; 0076 0077 MyMoneyTransactionFilter::MyMoneyTransactionFilter() : 0078 d_ptr(new MyMoneyTransactionFilterPrivate) 0079 { 0080 Q_D(MyMoneyTransactionFilter); 0081 d->m_reportAllSplits = true; 0082 d->m_considerCategory = true; 0083 } 0084 0085 MyMoneyTransactionFilter::MyMoneyTransactionFilter(const QString& id) : 0086 d_ptr(new MyMoneyTransactionFilterPrivate) 0087 { 0088 addAccount(id); 0089 } 0090 0091 MyMoneyTransactionFilter::MyMoneyTransactionFilter(const MyMoneyTransactionFilter& other) : 0092 d_ptr(new MyMoneyTransactionFilterPrivate(*other.d_func())) 0093 { 0094 } 0095 0096 MyMoneyTransactionFilter::~MyMoneyTransactionFilter() 0097 { 0098 Q_D(MyMoneyTransactionFilter); 0099 delete d; 0100 } 0101 0102 void MyMoneyTransactionFilter::clear() 0103 { 0104 Q_D(MyMoneyTransactionFilter); 0105 d->m_filterSet = {}; 0106 d->m_invertText = false; 0107 d->m_filterIsRegExp = false; 0108 d->m_accounts.clear(); 0109 d->m_categories.clear(); 0110 d->m_payees.clear(); 0111 d->m_tags.clear(); 0112 d->m_types.clear(); 0113 d->m_states.clear(); 0114 d->m_validity.clear(); 0115 d->m_fromDate = QDate(); 0116 d->m_toDate = QDate(); 0117 } 0118 0119 void MyMoneyTransactionFilter::clearAccountFilter() 0120 { 0121 Q_D(MyMoneyTransactionFilter); 0122 d->m_filterSet.setFlag(accountFilterActive); 0123 d->m_accounts.clear(); 0124 } 0125 0126 void MyMoneyTransactionFilter::setTextFilter(const QRegularExpression& text, bool isRegExp, bool invert) 0127 { 0128 Q_D(MyMoneyTransactionFilter); 0129 d->m_filterSet.setFlag(textFilterActive); 0130 d->m_filterIsRegExp = isRegExp; 0131 d->m_invertText = invert; 0132 d->m_text = text; 0133 } 0134 0135 void MyMoneyTransactionFilter::addAccount(const QStringList& ids) 0136 { 0137 Q_D(MyMoneyTransactionFilter); 0138 0139 d->m_filterSet.setFlag(accountFilterActive); 0140 for (const auto& id : ids) 0141 addAccount(id); 0142 } 0143 0144 void MyMoneyTransactionFilter::addAccount(const QString& id) 0145 { 0146 Q_D(MyMoneyTransactionFilter); 0147 if (!d->m_accounts.isEmpty() && !id.isEmpty() && 0148 d->m_accounts.contains(id)) 0149 return; 0150 0151 d->m_filterSet.setFlag(accountFilterActive); 0152 if (!id.isEmpty()) 0153 d->m_accounts.insert(id, QString()); 0154 } 0155 0156 void MyMoneyTransactionFilter::addCategory(const QStringList& ids) 0157 { 0158 Q_D(MyMoneyTransactionFilter); 0159 0160 d->m_filterSet.setFlag(categoryFilterActive); 0161 for (const auto& id : ids) 0162 addCategory(id); 0163 } 0164 0165 void MyMoneyTransactionFilter::addCategory(const QString& id) 0166 { 0167 Q_D(MyMoneyTransactionFilter); 0168 if (!d->m_categories.isEmpty() && !id.isEmpty() && 0169 d->m_categories.contains(id)) 0170 return; 0171 0172 d->m_filterSet.setFlag(categoryFilterActive); 0173 if (!id.isEmpty()) 0174 d->m_categories.insert(id, QString()); 0175 } 0176 0177 void MyMoneyTransactionFilter::setDateFilter(const QDate& from, const QDate& to) 0178 { 0179 Q_D(MyMoneyTransactionFilter); 0180 d->m_filterSet.setFlag(dateFilterActive, from.isValid() || to.isValid()); 0181 d->m_fromDate = from; 0182 d->m_toDate = to; 0183 if (from.isValid() && to.isValid()) { 0184 if (from > to) { 0185 d->m_fromDate = to; 0186 d->m_toDate = from; 0187 } 0188 } 0189 } 0190 0191 void MyMoneyTransactionFilter::setAmountFilter(const MyMoneyMoney& from, const MyMoneyMoney& to) 0192 { 0193 Q_D(MyMoneyTransactionFilter); 0194 d->m_filterSet.setFlag(amountFilterActive); 0195 d->m_fromAmount = from.abs(); 0196 d->m_toAmount = to.abs(); 0197 0198 // make sure that the user does not try to fool us ;-) 0199 if (from > to) 0200 std::swap(d->m_fromAmount, d->m_toAmount); 0201 } 0202 0203 void MyMoneyTransactionFilter::addPayee(const QString& id) 0204 { 0205 Q_D(MyMoneyTransactionFilter); 0206 if (!d->m_payees.isEmpty() && !id.isEmpty() && 0207 d->m_payees.contains(id)) 0208 return; 0209 0210 d->m_filterSet.setFlag(payeeFilterActive); 0211 if (!id.isEmpty()) 0212 d->m_payees.insert(id, QString()); 0213 } 0214 0215 void MyMoneyTransactionFilter::addTag(const QString& id) 0216 { 0217 Q_D(MyMoneyTransactionFilter); 0218 if (!d->m_tags.isEmpty() && !id.isEmpty() && 0219 d->m_tags.contains(id)) 0220 return; 0221 0222 d->m_filterSet.setFlag(tagFilterActive); 0223 if (!id.isEmpty()) 0224 d->m_tags.insert(id, QString()); 0225 } 0226 0227 void MyMoneyTransactionFilter::addType(const int type) 0228 { 0229 Q_D(MyMoneyTransactionFilter); 0230 if (!d->m_types.isEmpty() && 0231 d->m_types.contains(type)) 0232 return; 0233 0234 d->m_filterSet.setFlag(typeFilterActive); 0235 d->m_types.insert(type, QString()); 0236 } 0237 0238 void MyMoneyTransactionFilter::addState(const int state) 0239 { 0240 Q_D(MyMoneyTransactionFilter); 0241 if (!d->m_states.isEmpty() && 0242 d->m_states.contains(state)) 0243 return; 0244 0245 d->m_filterSet.setFlag(stateFilterActive); 0246 d->m_states.insert(state, QString()); 0247 } 0248 0249 void MyMoneyTransactionFilter::addValidity(const int type) 0250 { 0251 Q_D(MyMoneyTransactionFilter); 0252 if (!d->m_validity.isEmpty() && 0253 d->m_validity.contains(type)) 0254 return; 0255 0256 d->m_filterSet.setFlag(validityFilterActive); 0257 d->m_validity.insert(type, QString()); 0258 } 0259 0260 void MyMoneyTransactionFilter::setNumberFilter(const QString& from, const QString& to) 0261 { 0262 Q_D(MyMoneyTransactionFilter); 0263 d->m_filterSet.setFlag(nrFilterActive); 0264 d->m_fromNr = from; 0265 d->m_toNr = to; 0266 } 0267 0268 void MyMoneyTransactionFilter::setReportAllSplits(const bool report) 0269 { 0270 Q_D(MyMoneyTransactionFilter); 0271 d->m_reportAllSplits = report; 0272 } 0273 0274 void MyMoneyTransactionFilter::setConsiderCategorySplits(const bool check) 0275 { 0276 Q_D(MyMoneyTransactionFilter); 0277 d->m_considerCategorySplits = check; 0278 } 0279 0280 void MyMoneyTransactionFilter::setConsiderCategory(const bool check) 0281 { 0282 Q_D(MyMoneyTransactionFilter); 0283 d->m_considerCategory = check; 0284 } 0285 0286 void MyMoneyTransactionFilter::setTreatTransfersAsIncomeExpense(const bool check) 0287 { 0288 Q_D(MyMoneyTransactionFilter); 0289 d->m_treatTransfersAsIncomeExpense = check; 0290 } 0291 0292 bool MyMoneyTransactionFilter::treatTransfersAsIncomeExpense() const 0293 { 0294 Q_D(const MyMoneyTransactionFilter); 0295 return d->m_treatTransfersAsIncomeExpense; 0296 } 0297 0298 uint MyMoneyTransactionFilter::matchingSplitsCount(const MyMoneyTransaction& transaction) 0299 { 0300 Q_D(MyMoneyTransactionFilter); 0301 d->m_matchOnly = true; 0302 matchingSplits(transaction); 0303 d->m_matchOnly = false; 0304 return d->m_matchingSplitsCount; 0305 } 0306 0307 QVector<MyMoneySplit> MyMoneyTransactionFilter::matchingSplits(const MyMoneyTransaction& transaction) 0308 { 0309 Q_D(MyMoneyTransactionFilter); 0310 0311 QVector<MyMoneySplit> matchingSplits; 0312 const auto file = MyMoneyFile::instance(); 0313 0314 // qDebug("T: %s", transaction.id().data()); 0315 // if no filter is set, we can safely return a match 0316 // if we should report all splits, then we collect them 0317 if (!d->m_filterSet && d->m_reportAllSplits) { 0318 d->m_matchingSplitsCount = transaction.splitCount(); 0319 if (!d->m_matchOnly) 0320 matchingSplits = QVector<MyMoneySplit>::fromList(transaction.splits()); 0321 return matchingSplits; 0322 } 0323 0324 d->m_matchingSplitsCount = 0; 0325 const auto filter = d->m_filterSet; 0326 0327 // perform checks on the MyMoneyTransaction object first 0328 0329 // check the date range 0330 if (filter & dateFilterActive) { 0331 if ((d->m_fromDate != QDate() && 0332 transaction.postDate() < d->m_fromDate) || 0333 (d->m_toDate != QDate() && 0334 transaction.postDate() > d->m_toDate)) { 0335 return matchingSplits; 0336 } 0337 } 0338 0339 auto categoryMatched = !(filter.testFlag(categoryFilterActive)); 0340 auto accountMatched = !(filter.testFlag(accountFilterActive)); 0341 auto isTransfer = true; 0342 0343 // check the transaction's validity 0344 if (filter & validityFilterActive) { 0345 if (!d->m_validity.isEmpty() && 0346 !d->m_validity.contains((int)validTransaction(transaction))) 0347 return matchingSplits; 0348 } 0349 0350 // if d->m_reportAllSplits == false.. 0351 // ...then we don't need splits... 0352 // ...but we need to know if there were any found 0353 auto isMatchingSplitsEmpty = true; 0354 0355 auto extendedFilter = d->m_filterSet; 0356 extendedFilter.setFlag(dateFilterActive, false); 0357 extendedFilter.setFlag(accountFilterActive, false); 0358 extendedFilter.setFlag(categoryFilterActive, false); 0359 0360 const auto needAccountMatch = filter.testFlag(accountFilterActive); 0361 const auto needCategoryMatch = filter.testFlag(categoryFilterActive); 0362 if (needAccountMatch || needCategoryMatch || 0363 extendedFilter != 0) { 0364 const auto& splits = transaction.splits(); 0365 for (const auto& s : splits) { 0366 if (needAccountMatch || needCategoryMatch) { 0367 auto removeSplit = true; 0368 if (d->m_considerCategory) { 0369 switch (file->account(s.accountId()).accountGroup()) { 0370 case eMyMoney::Account::Type::Income: 0371 case eMyMoney::Account::Type::Expense: 0372 isTransfer = false; 0373 // check if the split references one of the categories in the list 0374 if (needCategoryMatch) { 0375 if (d->m_categories.isEmpty()) { 0376 // we're looking for transactions with 'no' categories 0377 d->m_matchingSplitsCount = 0; 0378 matchingSplits.clear(); 0379 return matchingSplits; 0380 } else if (d->m_categories.contains(s.accountId())) { 0381 categoryMatched = true; 0382 removeSplit = false; 0383 } 0384 } 0385 break; 0386 0387 default: 0388 // check if the split references one of the accounts in the list 0389 if (!filter.testFlag(accountFilterActive)) { 0390 removeSplit = false; 0391 } else if (!d->m_accounts.isEmpty() && 0392 d->m_accounts.contains(s.accountId())) { 0393 accountMatched = true; 0394 removeSplit = false; 0395 } 0396 0397 break; 0398 } 0399 0400 } else { 0401 if (!filter.testFlag(accountFilterActive)) { 0402 removeSplit = false; 0403 } else if (!d->m_accounts.isEmpty() && 0404 d->m_accounts.contains(s.accountId())) { 0405 accountMatched = true; 0406 removeSplit = false; 0407 } 0408 } 0409 0410 if (removeSplit) 0411 continue; 0412 } 0413 0414 // check if less frequent filters are active 0415 if (extendedFilter != 0) { 0416 const auto acc = file->account(s.accountId()); 0417 if (!(matchAmount(s) && matchText(s, acc))) 0418 continue; 0419 0420 // Determine if this account is a category or an account 0421 auto isCategory = false; 0422 switch (acc.accountGroup()) { 0423 case eMyMoney::Account::Type::Income: 0424 case eMyMoney::Account::Type::Expense: 0425 isCategory = true; 0426 default: 0427 break; 0428 } 0429 0430 bool includeSplit = d->m_considerCategorySplits || (!d->m_considerCategorySplits && !isCategory); 0431 if (includeSplit) { 0432 // check the payee list 0433 if (filter.testFlag(payeeFilterActive)) { 0434 if (!d->m_payees.isEmpty()) { 0435 if (s.payeeId().isEmpty() || !d->m_payees.contains(s.payeeId())) 0436 continue; 0437 } else if (!s.payeeId().isEmpty()) 0438 continue; 0439 } 0440 0441 // check the tag list 0442 if (filter.testFlag(tagFilterActive)) { 0443 const auto tags = s.tagIdList(); 0444 if (!d->m_tags.isEmpty()) { 0445 if (tags.isEmpty()) { 0446 continue; 0447 } else { 0448 auto found = false; 0449 for (const auto& tag : tags) { 0450 if (d->m_tags.contains(tag)) { 0451 found = true; 0452 break; 0453 } 0454 } 0455 0456 if (!found) 0457 continue; 0458 } 0459 } else if (!tags.isEmpty()) 0460 continue; 0461 } 0462 0463 // check the type list 0464 if (filter.testFlag(typeFilterActive) && 0465 !d->m_types.isEmpty() && 0466 !d->m_types.contains(splitType(transaction, s, acc))) 0467 continue; 0468 0469 // check the state list 0470 if (filter.testFlag(stateFilterActive) && 0471 !d->m_states.isEmpty() && 0472 !d->m_states.contains(splitState(s))) 0473 continue; 0474 0475 if (filter.testFlag(nrFilterActive) && 0476 ((!d->m_fromNr.isEmpty() && s.number() < d->m_fromNr) || 0477 (!d->m_toNr.isEmpty() && s.number() > d->m_toNr))) 0478 continue; 0479 0480 } else if (filter & (payeeFilterActive | tagFilterActive | typeFilterActive | stateFilterActive | nrFilterActive)) { 0481 continue; 0482 } 0483 } 0484 if (d->m_reportAllSplits) 0485 matchingSplits.append(s); 0486 0487 isMatchingSplitsEmpty = false; 0488 } 0489 0490 // check if we're looking for transactions without assigned category 0491 if (!categoryMatched && transaction.splitCount() == 1 && d->m_categories.isEmpty()) 0492 categoryMatched = true; 0493 0494 if ((needAccountMatch && !accountMatched) || 0495 (needCategoryMatch && !categoryMatched)) { 0496 matchingSplits.clear(); 0497 return matchingSplits; 0498 } 0499 } else if (d->m_reportAllSplits) { 0500 const auto& splits = transaction.splits(); 0501 for (const auto& s : splits) 0502 matchingSplits.append(s); 0503 d->m_matchingSplitsCount = matchingSplits.count(); 0504 return matchingSplits; 0505 } else if (transaction.splitCount() > 0) { 0506 isMatchingSplitsEmpty = false; 0507 } 0508 0509 // check if we're looking for transactions without assigned category 0510 if (!categoryMatched && transaction.splitCount() == 1 && d->m_categories.isEmpty()) 0511 categoryMatched = true; 0512 0513 // if there's no category filter and the category did not 0514 // match, then we still want to see this transaction if it's 0515 // a transfer 0516 if (!categoryMatched && !filter.testFlag(categoryFilterActive)) 0517 categoryMatched = isTransfer; 0518 0519 if (isMatchingSplitsEmpty || !(accountMatched && categoryMatched)) { 0520 d->m_matchingSplitsCount = 0; 0521 return matchingSplits; 0522 } 0523 0524 if (!d->m_reportAllSplits && !isMatchingSplitsEmpty) { 0525 d->m_matchingSplitsCount = 1; 0526 if (!d->m_matchOnly) 0527 matchingSplits.append(transaction.firstSplit()); 0528 } else { 0529 d->m_matchingSplitsCount = matchingSplits.count(); 0530 } 0531 0532 // all filters passed, I guess we have a match 0533 // qDebug(" C: %d", m_matchingSplits.count()); 0534 return matchingSplits; 0535 } 0536 0537 QDate MyMoneyTransactionFilter::fromDate() const 0538 { 0539 Q_D(const MyMoneyTransactionFilter); 0540 return d->m_fromDate; 0541 } 0542 0543 QDate MyMoneyTransactionFilter::toDate() const 0544 { 0545 Q_D(const MyMoneyTransactionFilter); 0546 return d->m_toDate; 0547 } 0548 0549 bool MyMoneyTransactionFilter::matchText(const MyMoneySplit& s, const MyMoneyAccount& acc) const 0550 { 0551 Q_D(const MyMoneyTransactionFilter); 0552 // check if the text is contained in one of the fields 0553 // memo, value, number, payee, tag, account 0554 if (d->m_filterSet & textFilterActive) { 0555 const auto file = MyMoneyFile::instance(); 0556 const auto sec = file->security(acc.currencyId()); 0557 if (s.memo().contains(d->m_text) || 0558 s.shares().formatMoney(acc.fraction(sec)).contains(d->m_text) || 0559 s.value().formatMoney(acc.fraction(sec)).contains(d->m_text) || 0560 s.number().contains(d->m_text) || 0561 (d->m_text.pattern().compare(s.transactionId())) == 0) 0562 return !d->m_invertText; 0563 0564 if (acc.name().contains(d->m_text)) 0565 return !d->m_invertText; 0566 0567 if (!s.payeeId().isEmpty() && file->payee(s.payeeId()).name().contains(d->m_text)) 0568 return !d->m_invertText; 0569 0570 const auto& tagIdList = s.tagIdList(); 0571 for (const auto& tag : tagIdList) 0572 if (file->tag(tag).name().contains(d->m_text)) 0573 return !d->m_invertText; 0574 0575 return d->m_invertText; 0576 } 0577 return true; 0578 } 0579 0580 bool MyMoneyTransactionFilter::matchAmount(const MyMoneySplit& s) const 0581 { 0582 Q_D(const MyMoneyTransactionFilter); 0583 if (d->m_filterSet & amountFilterActive) { 0584 const auto value = s.value().abs(); 0585 const auto shares = s.shares().abs(); 0586 if ((value < d->m_fromAmount || value > d->m_toAmount) && 0587 (shares < d->m_fromAmount || shares > d->m_toAmount)) 0588 return false; 0589 } 0590 0591 return true; 0592 } 0593 0594 bool MyMoneyTransactionFilter::match(const MyMoneySplit& s) const 0595 { 0596 const auto& acc = MyMoneyFile::instance()->account(s.accountId()); 0597 return matchText(s, acc) && matchAmount(s); 0598 } 0599 0600 bool MyMoneyTransactionFilter::match(const MyMoneyTransaction& transaction) 0601 { 0602 Q_D(MyMoneyTransactionFilter); 0603 d->m_matchOnly = true; 0604 matchingSplits(transaction); 0605 d->m_matchOnly = false; 0606 return d->m_matchingSplitsCount > 0; 0607 } 0608 0609 int MyMoneyTransactionFilter::splitState(const MyMoneySplit& split) const 0610 { 0611 switch (split.reconcileFlag()) { 0612 default: 0613 case eMyMoney::Split::State::NotReconciled: 0614 return (int)eMyMoney::TransactionFilter::State::NotReconciled; 0615 case eMyMoney::Split::State::Cleared: 0616 return (int)eMyMoney::TransactionFilter::State::Cleared; 0617 case eMyMoney::Split::State::Reconciled: 0618 return (int)eMyMoney::TransactionFilter::State::Reconciled; 0619 case eMyMoney::Split::State::Frozen: 0620 return (int)eMyMoney::TransactionFilter::State::Frozen; 0621 } 0622 } 0623 0624 int MyMoneyTransactionFilter::splitType(const MyMoneyTransaction& t, const MyMoneySplit& split, const MyMoneyAccount& acc) const 0625 { 0626 Q_D(const MyMoneyTransactionFilter); 0627 if (acc.isIncomeExpense()) 0628 return (int)eMyMoney::TransactionFilter::Type::All; 0629 0630 if (t.splitCount() == 2 && !d->m_treatTransfersAsIncomeExpense) { 0631 const auto& splits = t.splits(); 0632 const auto file = MyMoneyFile::instance(); 0633 const auto& a = splits.at(0).id().compare(split.id()) == 0 ? acc : file->account(splits.at(0).accountId()); 0634 const auto& b = splits.at(1).id().compare(split.id()) == 0 ? acc : file->account(splits.at(1).accountId()); 0635 0636 if (!a.isIncomeExpense() && !b.isIncomeExpense()) 0637 return (int)eMyMoney::TransactionFilter::Type::Transfers; 0638 } 0639 0640 if (split.value().isPositive()) 0641 return (int)eMyMoney::TransactionFilter::Type::Deposits; 0642 0643 return (int)eMyMoney::TransactionFilter::Type::Payments; 0644 } 0645 0646 eMyMoney::TransactionFilter::Validity MyMoneyTransactionFilter::validTransaction(const MyMoneyTransaction& t) const 0647 { 0648 MyMoneyMoney val; 0649 const auto& splits = t.splits(); 0650 for (const auto& split : splits) 0651 val += split.value(); 0652 0653 return (val == MyMoneyMoney()) ? eMyMoney::TransactionFilter::Validity::Valid : eMyMoney::TransactionFilter::Validity::Invalid; 0654 } 0655 0656 bool MyMoneyTransactionFilter::includesCategory(const QString& cat) const 0657 { 0658 Q_D(const MyMoneyTransactionFilter); 0659 return !d->m_filterSet.testFlag(categoryFilterActive) || d->m_categories.contains(cat); 0660 } 0661 0662 bool MyMoneyTransactionFilter::includesAccount(const QString& acc) const 0663 { 0664 Q_D(const MyMoneyTransactionFilter); 0665 return !d->m_filterSet.testFlag(accountFilterActive) || d->m_accounts.contains(acc); 0666 } 0667 0668 bool MyMoneyTransactionFilter::includesPayee(const QString& pye) const 0669 { 0670 Q_D(const MyMoneyTransactionFilter); 0671 return !d->m_filterSet.testFlag(payeeFilterActive) || d->m_payees.contains(pye); 0672 } 0673 0674 bool MyMoneyTransactionFilter::includesTag(const QString& tag) const 0675 { 0676 Q_D(const MyMoneyTransactionFilter); 0677 return !d->m_filterSet.testFlag(tagFilterActive) || d->m_tags.contains(tag); 0678 } 0679 0680 bool MyMoneyTransactionFilter::dateFilter(QDate& from, QDate& to) const 0681 { 0682 Q_D(const MyMoneyTransactionFilter); 0683 from = d->m_fromDate; 0684 to = d->m_toDate; 0685 return d->m_filterSet.testFlag(dateFilterActive); 0686 } 0687 0688 bool MyMoneyTransactionFilter::amountFilter(MyMoneyMoney& from, MyMoneyMoney& to) const 0689 { 0690 Q_D(const MyMoneyTransactionFilter); 0691 from = d->m_fromAmount; 0692 to = d->m_toAmount; 0693 return d->m_filterSet.testFlag(amountFilterActive); 0694 } 0695 0696 bool MyMoneyTransactionFilter::numberFilter(QString& from, QString& to) const 0697 { 0698 Q_D(const MyMoneyTransactionFilter); 0699 from = d->m_fromNr; 0700 to = d->m_toNr; 0701 return d->m_filterSet.testFlag(nrFilterActive); 0702 } 0703 0704 bool MyMoneyTransactionFilter::payees(QStringList& list) const 0705 { 0706 Q_D(const MyMoneyTransactionFilter); 0707 auto result = d->m_filterSet.testFlag(payeeFilterActive); 0708 0709 if (result) { 0710 QHashIterator<QString, QString> it_payee(d->m_payees); 0711 while (it_payee.hasNext()) { 0712 it_payee.next(); 0713 list += it_payee.key(); 0714 } 0715 } 0716 return result; 0717 } 0718 0719 QStringList MyMoneyTransactionFilter::payees() const 0720 { 0721 Q_D(const MyMoneyTransactionFilter); 0722 QStringList list; 0723 0724 if (d->m_filterSet.testFlag(payeeFilterActive)) { 0725 QHashIterator<QString, QString> it_payee(d->m_payees); 0726 while (it_payee.hasNext()) { 0727 it_payee.next(); 0728 list += it_payee.key(); 0729 } 0730 } 0731 return list; 0732 } 0733 0734 bool MyMoneyTransactionFilter::tags(QStringList& list) const 0735 { 0736 Q_D(const MyMoneyTransactionFilter); 0737 auto result = d->m_filterSet.testFlag(tagFilterActive); 0738 0739 if (result) { 0740 QHashIterator<QString, QString> it_tag(d->m_tags); 0741 while (it_tag.hasNext()) { 0742 it_tag.next(); 0743 list += it_tag.key(); 0744 } 0745 } 0746 return result; 0747 } 0748 0749 QStringList MyMoneyTransactionFilter::tags() const 0750 { 0751 QStringList tagIds; 0752 tags(tagIds); 0753 return tagIds; 0754 } 0755 0756 bool MyMoneyTransactionFilter::accounts(QStringList& list) const 0757 { 0758 Q_D(const MyMoneyTransactionFilter); 0759 auto result = d->m_filterSet.testFlag(accountFilterActive); 0760 0761 if (result) { 0762 QHashIterator<QString, QString> it_account(d->m_accounts); 0763 while (it_account.hasNext()) { 0764 it_account.next(); 0765 QString account = it_account.key(); 0766 list += account; 0767 } 0768 } 0769 return result; 0770 } 0771 0772 QStringList MyMoneyTransactionFilter::accounts() const 0773 { 0774 Q_D(const MyMoneyTransactionFilter); 0775 QStringList list; 0776 0777 if (d->m_filterSet.testFlag(accountFilterActive)) { 0778 QHashIterator<QString, QString> it_account(d->m_accounts); 0779 while (it_account.hasNext()) { 0780 it_account.next(); 0781 QString account = it_account.key(); 0782 list += account; 0783 } 0784 } 0785 return list; 0786 } 0787 0788 bool MyMoneyTransactionFilter::categories(QStringList& list) const 0789 { 0790 Q_D(const MyMoneyTransactionFilter); 0791 auto result = d->m_filterSet.testFlag(categoryFilterActive); 0792 0793 if (result) { 0794 QHashIterator<QString, QString> it_category(d->m_categories); 0795 while (it_category.hasNext()) { 0796 it_category.next(); 0797 list += it_category.key(); 0798 } 0799 } 0800 return result; 0801 } 0802 0803 bool MyMoneyTransactionFilter::types(QList<int>& list) const 0804 { 0805 Q_D(const MyMoneyTransactionFilter); 0806 auto result = d->m_filterSet.testFlag(typeFilterActive); 0807 0808 if (result) { 0809 QHashIterator<int, QString> it_type(d->m_types); 0810 while (it_type.hasNext()) { 0811 it_type.next(); 0812 list += it_type.key(); 0813 } 0814 } 0815 return result; 0816 } 0817 0818 bool MyMoneyTransactionFilter::states(QList<int>& list) const 0819 { 0820 Q_D(const MyMoneyTransactionFilter); 0821 auto result = d->m_filterSet.testFlag(stateFilterActive); 0822 0823 if (result) { 0824 QHashIterator<int, QString> it_state(d->m_states); 0825 while (it_state.hasNext()) { 0826 it_state.next(); 0827 list += it_state.key(); 0828 } 0829 } 0830 return result; 0831 } 0832 0833 bool MyMoneyTransactionFilter::validities(QList<int>& list) const 0834 { 0835 Q_D(const MyMoneyTransactionFilter); 0836 auto result = d->m_filterSet.testFlag(validityFilterActive); 0837 0838 if (result) { 0839 QHashIterator<int, QString> it_validity(d->m_validity); 0840 while (it_validity.hasNext()) { 0841 it_validity.next(); 0842 list += it_validity.key(); 0843 } 0844 } 0845 return result; 0846 } 0847 0848 bool MyMoneyTransactionFilter::firstType(int&i) const 0849 { 0850 Q_D(const MyMoneyTransactionFilter); 0851 auto result = d->m_filterSet.testFlag(typeFilterActive); 0852 0853 if (result) { 0854 QHashIterator<int, QString> it_type(d->m_types); 0855 if (it_type.hasNext()) { 0856 it_type.next(); 0857 i = it_type.key(); 0858 } 0859 } 0860 return result; 0861 } 0862 0863 bool MyMoneyTransactionFilter::firstState(int&i) const 0864 { 0865 Q_D(const MyMoneyTransactionFilter); 0866 auto result = d->m_filterSet.testFlag(stateFilterActive); 0867 0868 if (result) { 0869 QHashIterator<int, QString> it_state(d->m_states); 0870 if (it_state.hasNext()) { 0871 it_state.next(); 0872 i = it_state.key(); 0873 } 0874 } 0875 return result; 0876 } 0877 0878 bool MyMoneyTransactionFilter::firstValidity(int&i) const 0879 { 0880 Q_D(const MyMoneyTransactionFilter); 0881 auto result = d->m_filterSet.testFlag(validityFilterActive); 0882 0883 if (result) { 0884 QHashIterator<int, QString> it_validity(d->m_validity); 0885 if (it_validity.hasNext()) { 0886 it_validity.next(); 0887 i = it_validity.key(); 0888 } 0889 } 0890 return result; 0891 } 0892 0893 bool MyMoneyTransactionFilter::textFilter(QRegularExpression& text, bool& isRegExp) const 0894 { 0895 Q_D(const MyMoneyTransactionFilter); 0896 text = d->m_text; 0897 isRegExp = d->m_filterIsRegExp; 0898 return d->m_filterSet.testFlag(textFilterActive); 0899 } 0900 0901 bool MyMoneyTransactionFilter::isInvertingText() const 0902 { 0903 Q_D(const MyMoneyTransactionFilter); 0904 return d->m_invertText; 0905 } 0906 0907 void MyMoneyTransactionFilter::setDateFilter(eMyMoney::TransactionFilter::Date range) 0908 { 0909 QDate from, to; 0910 if (translateDateRange(range, from, to)) 0911 setDateFilter(from, to); 0912 } 0913 0914 static int fiscalYearStartMonth = 1; 0915 static int fiscalYearStartDay = 1; 0916 0917 void MyMoneyTransactionFilter::setFiscalYearStart(int firstMonth, int firstDay) 0918 { 0919 fiscalYearStartMonth = firstMonth; 0920 fiscalYearStartDay = firstDay; 0921 } 0922 0923 bool MyMoneyTransactionFilter::translateDateRange(eMyMoney::TransactionFilter::Date id, QDate& start, QDate& end) 0924 { 0925 bool rc = true; 0926 int yr = QDate::currentDate().year(); 0927 int mon = QDate::currentDate().month(); 0928 0929 switch (id) { 0930 case eMyMoney::TransactionFilter::Date::All: 0931 start = QDate(); 0932 end = QDate(); 0933 break; 0934 case eMyMoney::TransactionFilter::Date::AsOfToday: 0935 start = QDate(); 0936 end = QDate::currentDate(); 0937 break; 0938 case eMyMoney::TransactionFilter::Date::CurrentMonth: 0939 start = QDate(yr, mon, 1); 0940 end = QDate(yr, mon, 1).addMonths(1).addDays(-1); 0941 break; 0942 case eMyMoney::TransactionFilter::Date::CurrentYear: 0943 start = QDate(yr, 1, 1); 0944 end = QDate(yr, 12, 31); 0945 break; 0946 case eMyMoney::TransactionFilter::Date::MonthToDate: 0947 start = QDate(yr, mon, 1); 0948 end = QDate::currentDate(); 0949 break; 0950 case eMyMoney::TransactionFilter::Date::YearToDate: 0951 start = QDate(yr, 1, 1); 0952 end = QDate::currentDate(); 0953 break; 0954 case eMyMoney::TransactionFilter::Date::YearToMonth: 0955 start = QDate(yr, 1, 1); 0956 end = QDate(yr, mon, 1).addDays(-1); 0957 break; 0958 case eMyMoney::TransactionFilter::Date::LastMonth: 0959 start = QDate(yr, mon, 1).addMonths(-1); 0960 end = QDate(yr, mon, 1).addDays(-1); 0961 break; 0962 case eMyMoney::TransactionFilter::Date::LastYear: 0963 start = QDate(yr, 1, 1).addYears(-1); 0964 end = QDate(yr, 12, 31).addYears(-1); 0965 break; 0966 case eMyMoney::TransactionFilter::Date::Last7Days: 0967 start = QDate::currentDate().addDays(-7); 0968 end = QDate::currentDate(); 0969 break; 0970 case eMyMoney::TransactionFilter::Date::Last30Days: 0971 start = QDate::currentDate().addDays(-30); 0972 end = QDate::currentDate(); 0973 break; 0974 case eMyMoney::TransactionFilter::Date::Last3Months: 0975 start = QDate::currentDate().addMonths(-3); 0976 end = QDate::currentDate(); 0977 break; 0978 case eMyMoney::TransactionFilter::Date::Last6Months: 0979 start = QDate::currentDate().addMonths(-6); 0980 end = QDate::currentDate(); 0981 break; 0982 case eMyMoney::TransactionFilter::Date::Last11Months: 0983 start = QDate(yr, mon, 1).addMonths(-12); 0984 end = QDate(yr, mon, 1).addDays(-1); 0985 break; 0986 case eMyMoney::TransactionFilter::Date::Last12Months: 0987 start = QDate::currentDate().addMonths(-12); 0988 end = QDate::currentDate(); 0989 break; 0990 case eMyMoney::TransactionFilter::Date::Next7Days: 0991 start = QDate::currentDate(); 0992 end = QDate::currentDate().addDays(7); 0993 break; 0994 case eMyMoney::TransactionFilter::Date::Next30Days: 0995 start = QDate::currentDate(); 0996 end = QDate::currentDate().addDays(30); 0997 break; 0998 case eMyMoney::TransactionFilter::Date::Next3Months: 0999 start = QDate::currentDate(); 1000 end = QDate::currentDate().addMonths(3); 1001 break; 1002 case eMyMoney::TransactionFilter::Date::Next6Months: 1003 start = QDate::currentDate(); 1004 end = QDate::currentDate().addMonths(6); 1005 break; 1006 case eMyMoney::TransactionFilter::Date::Next12Months: 1007 start = QDate::currentDate(); 1008 end = QDate::currentDate().addMonths(12); 1009 break; 1010 case eMyMoney::TransactionFilter::Date::Next18Months: 1011 start = QDate::currentDate(); 1012 end = QDate::currentDate().addMonths(18); 1013 break; 1014 case eMyMoney::TransactionFilter::Date::UserDefined: 1015 start = QDate(); 1016 end = QDate(); 1017 break; 1018 case eMyMoney::TransactionFilter::Date::Last3ToNext3Months: 1019 start = QDate::currentDate().addMonths(-3); 1020 end = QDate::currentDate().addMonths(3); 1021 break; 1022 case eMyMoney::TransactionFilter::Date::CurrentQuarter: 1023 start = QDate(yr, mon - ((mon - 1) % 3), 1); 1024 end = start.addMonths(3).addDays(-1); 1025 break; 1026 case eMyMoney::TransactionFilter::Date::LastQuarter: 1027 start = QDate(yr, mon - ((mon - 1) % 3), 1).addMonths(-3); 1028 end = start.addMonths(3).addDays(-1); 1029 break; 1030 case eMyMoney::TransactionFilter::Date::NextQuarter: 1031 start = QDate(yr, mon - ((mon - 1) % 3), 1).addMonths(3); 1032 end = start.addMonths(3).addDays(-1); 1033 break; 1034 case eMyMoney::TransactionFilter::Date::CurrentFiscalYear: 1035 start = QDate(QDate::currentDate().year(), fiscalYearStartMonth, fiscalYearStartDay); 1036 if (QDate::currentDate() < start) 1037 start = start.addYears(-1); 1038 end = start.addYears(1).addDays(-1); 1039 break; 1040 case eMyMoney::TransactionFilter::Date::LastFiscalYear: 1041 start = QDate(QDate::currentDate().year(), fiscalYearStartMonth, fiscalYearStartDay); 1042 if (QDate::currentDate() < start) 1043 start = start.addYears(-1); 1044 start = start.addYears(-1); 1045 end = start.addYears(1).addDays(-1); 1046 break; 1047 case eMyMoney::TransactionFilter::Date::Today: 1048 start = QDate::currentDate(); 1049 end = QDate::currentDate(); 1050 break; 1051 default: 1052 qWarning("Unknown date identifier %d in MyMoneyTransactionFilter::translateDateRange()", (int)id); 1053 rc = false; 1054 break; 1055 } 1056 return rc; 1057 } 1058 1059 MyMoneyTransactionFilter::FilterSet MyMoneyTransactionFilter::filterSet() const 1060 { 1061 Q_D(const MyMoneyTransactionFilter); 1062 return d->m_filterSet; 1063 } 1064 1065 void MyMoneyTransactionFilter::removeReference(const QString& id) 1066 { 1067 Q_D(MyMoneyTransactionFilter); 1068 if (d->m_accounts.end() != d->m_accounts.find(id)) { 1069 qDebug("%s", qPrintable(QString("Remove account '%1' from report").arg(id))); 1070 d->m_accounts.take(id); 1071 } else if (d->m_categories.end() != d->m_categories.find(id)) { 1072 qDebug("%s", qPrintable(QString("Remove category '%1' from report").arg(id))); 1073 d->m_categories.remove(id); 1074 } else if (d->m_payees.end() != d->m_payees.find(id)) { 1075 qDebug("%s", qPrintable(QString("Remove payee '%1' from report").arg(id))); 1076 d->m_payees.remove(id); 1077 } else if (d->m_tags.end() != d->m_tags.find(id)) { 1078 qDebug("%s", qPrintable(QString("Remove tag '%1' from report").arg(id))); 1079 d->m_tags.remove(id); 1080 } 1081 }