File indexing completed on 2024-05-19 16:16:55

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