File indexing completed on 2024-05-19 05:08:36
0001 /* 0002 SPDX-FileCopyrightText: 2002-2019 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ktransactionfilter.h" 0008 #include "ktransactionfilter_p.h" 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 // ---------------------------------------------------------------------------- 0014 // KDE Includes 0015 0016 #include <KHelpClient> 0017 #include <KLocalizedString> 0018 0019 // ---------------------------------------------------------------------------- 0020 // Project Includes 0021 0022 #include "daterangedlg.h" 0023 #include "kmymoneysettings.h" 0024 #include "mymoneyexception.h" 0025 #include "mymoneyfile.h" 0026 #include "mymoneyreport.h" 0027 #include "mymoneysplit.h" 0028 #include "mymoneytransaction.h" 0029 #include "mymoneytransactionfilter.h" 0030 #include "mymoneyutils.h" 0031 0032 #include "ui_ktransactionfilter.h" 0033 0034 KTransactionFilter::KTransactionFilter(QWidget *parent, bool withEquityAccounts, bool withInvestments, bool withDataTab) : 0035 QWidget(parent), 0036 d_ptr(new KTransactionFilterPrivate(this)) 0037 { 0038 Q_D(KTransactionFilter); 0039 d->init(withEquityAccounts, withInvestments, withDataTab); 0040 QMetaObject::invokeMethod(d->ui->m_textEdit, "setFocus", Qt::QueuedConnection); 0041 } 0042 0043 KTransactionFilter::~KTransactionFilter() 0044 { 0045 Q_D(KTransactionFilter); 0046 delete d; 0047 } 0048 0049 void KTransactionFilter::slotReset() 0050 { 0051 Q_D(KTransactionFilter); 0052 d->ui->m_textEdit->setText(QString()); 0053 d->ui->m_regExp->setChecked(false); 0054 d->ui->m_caseSensitive->setChecked(false); 0055 d->ui->m_textNegate->setCurrentItem(0); 0056 0057 d->ui->m_amountEdit->setEnabled(true); 0058 d->ui->m_amountFromEdit->setEnabled(false); 0059 d->ui->m_amountToEdit->setEnabled(false); 0060 d->ui->m_amountEdit->setText(QString()); 0061 d->ui->m_amountFromEdit->setText(QString()); 0062 d->ui->m_amountToEdit->setText(QString()); 0063 d->ui->m_amountButton->setChecked(true); 0064 d->ui->m_amountRangeButton->setChecked(false); 0065 0066 d->ui->m_emptyPayeesButton->setChecked(false); 0067 d->selectAllItems(d->ui->m_payeesView, true); 0068 0069 d->ui->m_emptyTagsButton->setChecked(false); 0070 d->selectAllItems(d->ui->m_tagsView, true); 0071 0072 d->ui->m_typeBox->setCurrentIndex((int)eMyMoney::TransactionFilter::Type::All); 0073 d->ui->m_stateBox->setCurrentIndex((int)eMyMoney::TransactionFilter::State::All); 0074 d->ui->m_validityBox->setCurrentIndex((int)eMyMoney::TransactionFilter::Validity::Any); 0075 0076 d->ui->m_nrEdit->setEnabled(true); 0077 d->ui->m_nrFromEdit->setEnabled(false); 0078 d->ui->m_nrToEdit->setEnabled(false); 0079 d->ui->m_nrEdit->setText(QString()); 0080 d->ui->m_nrFromEdit->setText(QString()); 0081 d->ui->m_nrToEdit->setText(QString()); 0082 d->ui->m_nrButton->setChecked(true); 0083 d->ui->m_nrRangeButton->setChecked(false); 0084 0085 // the following call implies a call to slotUpdateSelections, 0086 // that's why we call it last 0087 if (d->m_dateRange) 0088 d->m_dateRange->slotReset(); 0089 slotUpdateSelections(); 0090 0091 d->ui->m_accountsView->slotSelectAllAccounts(); 0092 d->ui->m_categoriesView->slotSelectAllAccounts(); 0093 } 0094 0095 void KTransactionFilter::slotUpdateSelections() 0096 { 0097 Q_D(KTransactionFilter); 0098 QString txt; 0099 const QString separator(", "); 0100 // Text tab 0101 if (!d->ui->m_textEdit->text().isEmpty()) { 0102 if (!txt.isEmpty()) 0103 txt += separator; 0104 txt += i18n("Text"); 0105 d->ui->m_regExp->setEnabled(QRegularExpression(d->ui->m_textEdit->text()).isValid()); 0106 } else 0107 d->ui->m_regExp->setEnabled(false); 0108 0109 d->ui->m_caseSensitive->setEnabled(!d->ui->m_textEdit->text().isEmpty()); 0110 d->ui->m_textNegate->setEnabled(!d->ui->m_textEdit->text().isEmpty()); 0111 0112 // Account tab 0113 if (!d->ui->m_accountsView->allItemsSelected()) { 0114 if (!txt.isEmpty()) 0115 txt += separator; 0116 txt += i18n("Account"); 0117 } 0118 0119 if (d->m_dateRange && d->m_dateRange->dateRange() != eMyMoney::TransactionFilter::Date::All) { 0120 if (!txt.isEmpty()) 0121 txt += separator; 0122 txt += i18n("Date"); 0123 } 0124 0125 // Amount tab 0126 if ((d->ui->m_amountButton->isChecked() && d->ui->m_amountEdit->isValid()) 0127 || (d->ui->m_amountRangeButton->isChecked() 0128 && (d->ui->m_amountFromEdit->isValid() || d->ui->m_amountToEdit->isValid()))) { 0129 if (!txt.isEmpty()) 0130 txt += separator; 0131 txt += i18n("Amount"); 0132 } 0133 0134 // Categories tab 0135 if (!d->ui->m_categoriesView->allItemsSelected()) { 0136 if (!txt.isEmpty()) 0137 txt += separator; 0138 txt += i18n("Category"); 0139 } 0140 0141 // Tags tab 0142 if (!d->allItemsSelected(d->ui->m_tagsView) 0143 || d->ui->m_emptyTagsButton->isChecked()) { 0144 if (!txt.isEmpty()) 0145 txt += separator; 0146 txt += i18n("Tags"); 0147 } 0148 d->ui->m_tagsView->setEnabled(!d->ui->m_emptyTagsButton->isChecked()); 0149 0150 // Payees tab 0151 if (!d->allItemsSelected(d->ui->m_payeesView) 0152 || d->ui->m_emptyPayeesButton->isChecked()) { 0153 if (!txt.isEmpty()) 0154 txt += separator; 0155 txt += i18n("Payees"); 0156 } 0157 d->ui->m_payeesView->setEnabled(!d->ui->m_emptyPayeesButton->isChecked()); 0158 0159 // Details tab 0160 if (d->ui->m_typeBox->currentIndex() != 0 // 0161 || d->ui->m_stateBox->currentIndex() != 0 // 0162 || d->ui->m_validityBox->currentIndex() != 0 // 0163 || (d->ui->m_nrButton->isChecked() && d->ui->m_nrEdit->text().length() != 0) // 0164 || (d->ui->m_nrRangeButton->isChecked() // 0165 && (d->ui->m_nrFromEdit->text().length() != 0 || d->ui->m_nrToEdit->text().length() != 0))) { 0166 if (!txt.isEmpty()) 0167 txt += separator; 0168 txt += i18n("Details"); 0169 } 0170 0171 //Show a warning about transfers if Categories are filtered - bug #1523508 0172 if (!d->ui->m_categoriesView->allItemsSelected()) { 0173 d->ui->m_transferWarning->setText(i18n("Warning: Filtering by Category will exclude all transfers from the results.")); 0174 } else { 0175 d->ui->m_transferWarning->setText(QString()); 0176 } 0177 0178 // disable the search button if no selection is made 0179 Q_EMIT selectionNotEmpty(!txt.isEmpty()); 0180 0181 if (txt.isEmpty()) { 0182 txt = i18nc("No selection", "(None)"); 0183 } 0184 d->ui->m_selectedCriteria->setText(i18n("Current selections: %1", txt)); 0185 } 0186 0187 void KTransactionFilter::slotAmountSelected() 0188 { 0189 Q_D(KTransactionFilter); 0190 d->ui->m_amountEdit->setEnabled(true); 0191 d->ui->m_amountFromEdit->setEnabled(false); 0192 d->ui->m_amountToEdit->setEnabled(false); 0193 slotUpdateSelections(); 0194 } 0195 0196 void KTransactionFilter::slotAmountRangeSelected() 0197 { 0198 Q_D(KTransactionFilter); 0199 d->ui->m_amountEdit->setEnabled(false); 0200 d->ui->m_amountFromEdit->setEnabled(true); 0201 d->ui->m_amountToEdit->setEnabled(true); 0202 slotUpdateSelections(); 0203 } 0204 0205 void KTransactionFilter::slotSelectAllPayees() 0206 { 0207 Q_D(KTransactionFilter); 0208 d->selectAllItems(d->ui->m_payeesView, true); 0209 } 0210 0211 void KTransactionFilter::slotDeselectAllPayees() 0212 { 0213 Q_D(KTransactionFilter); 0214 d->selectAllItems(d->ui->m_payeesView, false); 0215 } 0216 0217 void KTransactionFilter::slotSelectAllTags() 0218 { 0219 Q_D(KTransactionFilter); 0220 d->selectAllItems(d->ui->m_tagsView, true); 0221 } 0222 0223 void KTransactionFilter::slotDeselectAllTags() 0224 { 0225 Q_D(KTransactionFilter); 0226 d->selectAllItems(d->ui->m_tagsView, false); 0227 } 0228 0229 void KTransactionFilter::slotNrSelected() 0230 { 0231 Q_D(KTransactionFilter); 0232 d->ui->m_nrEdit->setEnabled(true); 0233 d->ui->m_nrFromEdit->setEnabled(false); 0234 d->ui->m_nrToEdit->setEnabled(false); 0235 slotUpdateSelections(); 0236 } 0237 0238 void KTransactionFilter::slotNrRangeSelected() 0239 { 0240 Q_D(KTransactionFilter); 0241 d->ui->m_nrEdit->setEnabled(false); 0242 d->ui->m_nrFromEdit->setEnabled(true); 0243 d->ui->m_nrToEdit->setEnabled(true); 0244 slotUpdateSelections(); 0245 } 0246 0247 void KTransactionFilter::slotShowHelp() 0248 { 0249 Q_D(KTransactionFilter); 0250 auto anchor = d->m_helpAnchor[d->ui->m_criteriaTab->currentWidget()]; 0251 if (anchor.isEmpty()) 0252 anchor = QString("details.search"); 0253 0254 KHelpClient::invokeHelp(anchor); 0255 } 0256 0257 MyMoneyTransactionFilter KTransactionFilter::setupFilter() 0258 { 0259 Q_D(KTransactionFilter); 0260 d->m_filter.clear(); 0261 0262 // Text tab 0263 if (!d->ui->m_textEdit->text().isEmpty()) { 0264 QRegularExpression exp; 0265 auto pattern(d->ui->m_textEdit->text()); 0266 if (!d->ui->m_regExp->isChecked()) { 0267 pattern = MyMoneyUtils::convertWildcardToRegularExpression(pattern); 0268 } 0269 exp.setPattern(pattern); 0270 exp.setPatternOptions(d->ui->m_caseSensitive->isChecked() ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); 0271 d->m_filter.setTextFilter(exp, d->ui->m_regExp->isChecked(), d->ui->m_textNegate->currentIndex() != 0); 0272 } 0273 0274 // Account tab 0275 if (!d->ui->m_accountsView->allItemsSelected()) { 0276 // retrieve a list of selected accounts 0277 QStringList list; 0278 d->ui->m_accountsView->selectedItems(list); 0279 0280 // if we're not in expert mode, we need to make sure 0281 // that all stock accounts for the selected investment 0282 // account are also selected 0283 if (!KMyMoneySettings::expertMode()) { 0284 QStringList missing; 0285 for (const auto& selection : qAsConst(list)) { 0286 const auto acc = MyMoneyFile::instance()->account(selection); 0287 if (acc.accountType() == eMyMoney::Account::Type::Investment) { 0288 for (const auto& sAccount : acc.accountList()) { 0289 if (!list.contains(sAccount)) { 0290 missing.append(sAccount); 0291 } 0292 } 0293 } 0294 } 0295 list += missing; 0296 } 0297 0298 d->m_filter.addAccount(list); 0299 } 0300 0301 // Date tab 0302 if (d->m_dateRange && (int)d->m_dateRange->dateRange() != 0) { 0303 d->m_filter.setDateFilter(d->m_dateRange->fromDate(), d->m_dateRange->toDate()); 0304 } 0305 0306 // Amount tab 0307 if ((d->ui->m_amountButton->isChecked() && d->ui->m_amountEdit->isValid())) { 0308 d->m_filter.setAmountFilter(d->ui->m_amountEdit->value(), d->ui->m_amountEdit->value()); 0309 0310 } else if ((d->ui->m_amountRangeButton->isChecked() // 0311 && (d->ui->m_amountFromEdit->isValid() || d->ui->m_amountToEdit->isValid()))) { 0312 0313 MyMoneyMoney from(MyMoneyMoney::minValue), to(MyMoneyMoney::maxValue); 0314 if (d->ui->m_amountFromEdit->isValid()) 0315 from = d->ui->m_amountFromEdit->value(); 0316 if (d->ui->m_amountToEdit->isValid()) 0317 to = d->ui->m_amountToEdit->value(); 0318 0319 d->m_filter.setAmountFilter(from, to); 0320 } 0321 0322 // Categories tab 0323 if (!d->ui->m_categoriesView->allItemsSelected()) { 0324 d->m_filter.addCategory(d->ui->m_categoriesView->selectedItems()); 0325 } 0326 0327 // Tags tab 0328 if (d->ui->m_emptyTagsButton->isChecked()) { 0329 d->m_filter.addTag(QString()); 0330 0331 } else if (!d->allItemsSelected(d->ui->m_tagsView)) { 0332 d->scanCheckListItems(d->ui->m_tagsView, KTransactionFilterPrivate::addTagToFilter); 0333 } 0334 0335 // Payees tab 0336 if (d->ui->m_emptyPayeesButton->isChecked()) { 0337 d->m_filter.addPayee(QString()); 0338 0339 } else if (!d->allItemsSelected(d->ui->m_payeesView)) { 0340 d->scanCheckListItems(d->ui->m_payeesView, KTransactionFilterPrivate::addPayeeToFilter); 0341 } 0342 0343 // Details tab 0344 if (d->ui->m_typeBox->currentIndex() != 0) 0345 d->m_filter.addType(d->ui->m_typeBox->currentIndex()); 0346 0347 if (d->ui->m_stateBox->currentIndex() != 0) 0348 d->m_filter.addState(d->ui->m_stateBox->currentIndex()); 0349 0350 if (d->ui->m_validityBox->currentIndex() != 0) 0351 d->m_filter.addValidity(d->ui->m_validityBox->currentIndex()); 0352 0353 if (d->ui->m_nrButton->isChecked() && !d->ui->m_nrEdit->text().isEmpty()) 0354 d->m_filter.setNumberFilter(d->ui->m_nrEdit->text(), d->ui->m_nrEdit->text()); 0355 0356 if (d->ui->m_nrRangeButton->isChecked() // 0357 && (!d->ui->m_nrFromEdit->text().isEmpty() || !d->ui->m_nrToEdit->text().isEmpty())) { 0358 d->m_filter.setNumberFilter(d->ui->m_nrFromEdit->text(), d->ui->m_nrToEdit->text()); 0359 } 0360 return d->m_filter; 0361 } 0362 0363 void KTransactionFilter::resetFilter(MyMoneyReport& rep) 0364 { 0365 Q_D(KTransactionFilter); 0366 // 0367 // Text Filter 0368 // 0369 0370 QRegularExpression textfilter; 0371 bool isRegExp(false); 0372 if (rep.textFilter(textfilter, isRegExp)) { 0373 auto pattern(textfilter.pattern()); 0374 if (!isRegExp) { 0375 pattern = MyMoneyUtils::convertRegularExpressionToWildcard(pattern); 0376 } 0377 d->ui->m_textEdit->setText(pattern); 0378 d->ui->m_caseSensitive->setChecked(!(textfilter.patternOptions() & QRegularExpression::CaseInsensitiveOption)); 0379 d->ui->m_regExp->setChecked(isRegExp); 0380 d->ui->m_textNegate->setCurrentIndex(rep.isInvertingText()); 0381 } 0382 0383 // 0384 // Type & State Filters 0385 // 0386 0387 int type; 0388 if (rep.firstType(type)) 0389 d->ui->m_typeBox->setCurrentIndex(type); 0390 0391 int state; 0392 if (rep.firstState(state)) 0393 d->ui->m_stateBox->setCurrentIndex(state); 0394 0395 int validity; 0396 if (rep.firstValidity(validity)) 0397 d->ui->m_validityBox->setCurrentIndex(validity); 0398 0399 // 0400 // Number Filter 0401 // 0402 0403 QString nrFrom, nrTo; 0404 if (rep.numberFilter(nrFrom, nrTo)) { 0405 if (nrFrom == nrTo) { 0406 d->ui->m_nrEdit->setEnabled(true); 0407 d->ui->m_nrFromEdit->setEnabled(false); 0408 d->ui->m_nrToEdit->setEnabled(false); 0409 d->ui->m_nrEdit->setText(nrFrom); 0410 d->ui->m_nrFromEdit->setText(QString()); 0411 d->ui->m_nrToEdit->setText(QString()); 0412 d->ui->m_nrButton->setChecked(true); 0413 d->ui->m_nrRangeButton->setChecked(false); 0414 } else { 0415 d->ui->m_nrEdit->setEnabled(false); 0416 d->ui->m_nrFromEdit->setEnabled(true); 0417 d->ui->m_nrToEdit->setEnabled(false); 0418 d->ui->m_nrEdit->setText(QString()); 0419 d->ui->m_nrFromEdit->setText(nrFrom); 0420 d->ui->m_nrToEdit->setText(nrTo); 0421 d->ui->m_nrButton->setChecked(false); 0422 d->ui->m_nrRangeButton->setChecked(true); 0423 } 0424 } else { 0425 d->ui->m_nrEdit->setEnabled(true); 0426 d->ui->m_nrFromEdit->setEnabled(false); 0427 d->ui->m_nrToEdit->setEnabled(false); 0428 d->ui->m_nrEdit->setText(QString()); 0429 d->ui->m_nrFromEdit->setText(QString()); 0430 d->ui->m_nrToEdit->setText(QString()); 0431 d->ui->m_nrButton->setChecked(true); 0432 d->ui->m_nrRangeButton->setChecked(false); 0433 } 0434 0435 // 0436 // Amount Filter 0437 // 0438 0439 MyMoneyMoney from, to; 0440 if (rep.amountFilter(from, to)) { // bool getAmountFilter(MyMoneyMoney&,MyMoneyMoney&); 0441 if (from == to) { 0442 d->ui->m_amountEdit->setEnabled(true); 0443 d->ui->m_amountFromEdit->setEnabled(false); 0444 d->ui->m_amountToEdit->setEnabled(false); 0445 d->ui->m_amountEdit->setText(QString::number(from.toDouble())); 0446 d->ui->m_amountFromEdit->setText(QString()); 0447 d->ui->m_amountToEdit->setText(QString()); 0448 d->ui->m_amountButton->setChecked(true); 0449 d->ui->m_amountRangeButton->setChecked(false); 0450 } else { 0451 d->ui->m_amountEdit->setEnabled(false); 0452 d->ui->m_amountFromEdit->setEnabled(true); 0453 d->ui->m_amountToEdit->setEnabled(true); 0454 d->ui->m_amountEdit->setText(QString()); 0455 d->ui->m_amountFromEdit->setText(QString::number(from.toDouble())); 0456 d->ui->m_amountToEdit->setText(QString::number(to.toDouble())); 0457 d->ui->m_amountButton->setChecked(false); 0458 d->ui->m_amountRangeButton->setChecked(true); 0459 } 0460 } else { 0461 d->ui->m_amountEdit->setEnabled(true); 0462 d->ui->m_amountFromEdit->setEnabled(false); 0463 d->ui->m_amountToEdit->setEnabled(false); 0464 d->ui->m_amountEdit->setText(QString()); 0465 d->ui->m_amountFromEdit->setText(QString()); 0466 d->ui->m_amountToEdit->setText(QString()); 0467 d->ui->m_amountButton->setChecked(true); 0468 d->ui->m_amountRangeButton->setChecked(false); 0469 } 0470 0471 // 0472 // Payees Filter 0473 // 0474 0475 QStringList payees; 0476 if (rep.payees(payees)) { 0477 if (payees.empty()) { 0478 d->ui->m_emptyPayeesButton->setChecked(true); 0479 } else { 0480 d->selectAllItems(d->ui->m_payeesView, false); 0481 d->selectItems(d->ui->m_payeesView, payees, true); 0482 } 0483 } else { 0484 d->selectAllItems(d->ui->m_payeesView, true); 0485 } 0486 0487 // 0488 // Tags Filter 0489 // 0490 0491 QStringList tags; 0492 if (rep.tags(tags)) { 0493 if (tags.empty()) { 0494 d->ui->m_emptyTagsButton->setChecked(true); 0495 } else { 0496 d->selectAllItems(d->ui->m_tagsView, false); 0497 d->selectItems(d->ui->m_tagsView, tags, true); 0498 } 0499 } else { 0500 d->selectAllItems(d->ui->m_tagsView, true); 0501 } 0502 0503 // 0504 // Accounts Filter 0505 // 0506 0507 QStringList accounts; 0508 if (rep.accounts(accounts)) { 0509 // in case the presentation of closed accounts is turned off ... 0510 if (d->accountSet.isHidingClosedAccounts()) { 0511 // ... we need to turn them on again in case our own 0512 // configuration references a closed account 0513 const MyMoneyFile* file = MyMoneyFile::instance(); 0514 for (const auto& accId : qAsConst(accounts)) { 0515 try { 0516 if (file->account(accId).isClosed()) { 0517 d->accountSet.setHideClosedAccounts(false); 0518 d->accountSet.load(d->ui->m_accountsView); 0519 break; 0520 } 0521 } catch (const MyMoneyException&) { 0522 } 0523 } 0524 } 0525 d->ui->m_accountsView->selectAllItems(false); 0526 d->ui->m_accountsView->selectItems(accounts, true); 0527 } else 0528 d->ui->m_accountsView->selectAllItems(true); 0529 0530 // 0531 // Categories Filter 0532 // 0533 0534 if (rep.categories(accounts)) { 0535 d->ui->m_categoriesView->selectAllItems(false); 0536 d->ui->m_categoriesView->selectItems(accounts, true); 0537 } else 0538 d->ui->m_categoriesView->selectAllItems(true); 0539 0540 // 0541 // Date Filter 0542 // 0543 0544 // the following call implies a call to slotUpdateSelections, 0545 // that's why we call it last 0546 0547 if (d->m_dateRange) { 0548 rep.updateDateFilter(); 0549 QDate dateFrom, dateTo; 0550 if (rep.dateFilter(dateFrom, dateTo)) { 0551 if (rep.isDateUserDefined()) { 0552 d->m_dateRange->setDateRange(dateFrom, dateTo); 0553 } else { 0554 d->m_dateRange->setDateRange(rep.dateRange()); 0555 } 0556 } else { 0557 d->m_dateRange->setDateRange(eMyMoney::TransactionFilter::Date::All); 0558 } 0559 } 0560 0561 } 0562 0563 KMyMoneyAccountSelector* KTransactionFilter::categoriesView() 0564 { 0565 Q_D(KTransactionFilter); 0566 return d->ui->m_categoriesView; 0567 } 0568 0569 DateRangeDlg* KTransactionFilter::dateRange() 0570 { 0571 Q_D(KTransactionFilter); 0572 return d->m_dateRange; 0573 }