File indexing completed on 2024-05-12 16:43:50

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000-2002 Javier Campos Morales <javi_c@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2000-2002 Felix Rodriguez <frodriguez@users.sourceforge.net>
0005     SPDX-FileCopyrightText: 2000-2002 John C <thetacoturtle@users.sourceforge.net>
0006     SPDX-FileCopyrightText: 2000-2002 Thomas Baumgart <ipwizard@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2000-2002 Kevin Tambascio <ktambascio@users.sourceforge.net>
0008     SPDX-FileCopyrightText: 2000-2002 Andreas Nicolai <Andreas.Nicolai@gmx.net>
0009     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #ifndef KPAYEESVIEW_P_H
0014 #define KPAYEESVIEW_P_H
0015 
0016 #include "kpayeesview.h"
0017 
0018 // ----------------------------------------------------------------------------
0019 // QT Includes
0020 
0021 #include <QMap>
0022 #include <QList>
0023 #include <QTimer>
0024 #include <QDesktopServices>
0025 #include <QIcon>
0026 
0027 // ----------------------------------------------------------------------------
0028 // KDE Includes
0029 
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 #include <KHelpClient>
0033 #include <KSharedConfig>
0034 #include <KListWidgetSearchLine>
0035 
0036 // ----------------------------------------------------------------------------
0037 // Project Includes
0038 
0039 #include <config-kmymoney.h>
0040 #include "ui_kpayeesview.h"
0041 #include "kmymoneyviewbase_p.h"
0042 #include "kmymoneyutils.h"
0043 #include "kmymoneymvccombo.h"
0044 #include "mymoneypayee.h"
0045 #include "mymoneyexception.h"
0046 #include "mymoneyfile.h"
0047 #include "mymoneyaccount.h"
0048 #include "mymoneymoney.h"
0049 #include "mymoneytransactionfilter.h"
0050 #include "kmymoneysettings.h"
0051 #include "kpayeereassigndlg.h"
0052 #include "models.h"
0053 #include "accountsmodel.h"
0054 #include "mymoneysecurity.h"
0055 #include "mymoneyschedule.h"
0056 #include "mymoneycontact.h"
0057 #include "mymoneyaccountloan.h"
0058 #include "mymoneysplit.h"
0059 #include "mymoneyprice.h"
0060 #include "mymoneytransaction.h"
0061 #include "icons/icons.h"
0062 #include "transaction.h"
0063 #include "widgetenums.h"
0064 #include "mymoneyenums.h"
0065 #include "modelenums.h"
0066 #include "menuenums.h"
0067 
0068 using namespace Icons;
0069 
0070 // *** KPayeeListItem Implementation ***
0071 
0072 class KPayeeListItem : public QListWidgetItem
0073 {
0074 public:
0075     /**
0076       * Constructor to be used to construct a payee entry object.
0077       *
0078       * @param parent pointer to the QListWidget object this entry should be
0079       *               added to.
0080       * @param payee const reference to MyMoneyPayee for which
0081       *               the QListWidget entry is constructed
0082       */
0083     explicit KPayeeListItem(QListWidget *parent, const MyMoneyPayee& payee) :
0084         QListWidgetItem(parent, QListWidgetItem::UserType),
0085         m_payee(payee)
0086     {
0087         setText(payee.name());
0088         // allow in column rename
0089         setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
0090     }
0091 
0092     ~KPayeeListItem()
0093     {
0094     }
0095 
0096     const MyMoneyPayee& payee() const {
0097         return m_payee;
0098     }
0099 
0100 private:
0101     MyMoneyPayee  m_payee;
0102 };
0103 
0104 enum filterTypeE { eAllPayees = 0, eReferencedPayees = 1, eUnusedPayees = 2 };
0105 
0106 class KPayeesViewPrivate : public KMyMoneyViewBasePrivate
0107 {
0108     Q_DECLARE_PUBLIC(KPayeesView)
0109 
0110 public:
0111     explicit KPayeesViewPrivate(KPayeesView *qq) :
0112         KMyMoneyViewBasePrivate(),
0113         q_ptr(qq),
0114         ui(new Ui::KPayeesView),
0115         m_contact(nullptr),
0116         m_payeeRow(0),
0117         m_needLoad(true),
0118         m_searchWidget(nullptr),
0119         m_inSelection(false),
0120         m_allowEditing(true),
0121         m_payeeFilterType(0),
0122         m_filterProxyModel(nullptr)
0123     {
0124     }
0125 
0126     ~KPayeesViewPrivate()
0127     {
0128         if(!m_needLoad) {
0129             // remember the splitter settings for startup
0130             auto grp = KSharedConfig::openConfig()->group("Last Use Settings");
0131             grp.writeEntry("KPayeesViewSplitterSize", ui->m_splitter->saveState());
0132             grp.sync();
0133         }
0134         delete ui;
0135     }
0136 
0137     void init()
0138     {
0139         Q_Q(KPayeesView);
0140         m_needLoad = false;
0141         ui->setupUi(q);
0142 
0143         m_contact = new MyMoneyContact(q);
0144         m_filterProxyModel = new AccountNamesFilterProxyModel(q);
0145         m_filterProxyModel->setHideEquityAccounts(!KMyMoneySettings::expertMode());
0146         m_filterProxyModel->addAccountGroup(QVector<eMyMoney::Account::Type> {eMyMoney::Account::Type::Asset, eMyMoney::Account::Type::Liability, eMyMoney::Account::Type::Income, eMyMoney::Account::Type::Expense, eMyMoney::Account::Type::Equity});
0147 
0148         auto const model = Models::instance()->accountsModel();
0149         m_filterProxyModel->setSourceColumns(model->getColumns());
0150         m_filterProxyModel->setSourceModel(model);
0151         m_filterProxyModel->sort((int)eAccountsModel::Column::Account);
0152         ui->comboDefaultCategory->setModel(m_filterProxyModel);
0153 
0154         ui->matchTypeCombo->addItem(i18nc("@item No matching", "No matching"), static_cast<int>(eMyMoney::Payee::MatchType::Disabled));
0155         ui->matchTypeCombo->addItem(i18nc("@item Match Payees name partially", "Match Payees name (partial)"), static_cast<int>(eMyMoney::Payee::MatchType::Name));
0156         ui->matchTypeCombo->addItem(i18nc("@item Match Payees name exactly", "Match Payees name (exact)"), static_cast<int>(eMyMoney::Payee::MatchType::NameExact));
0157         ui->matchTypeCombo->addItem(i18nc("@item Search match in list", "Match on a name listed below"), static_cast<int>(eMyMoney::Payee::MatchType::Key));
0158 
0159         // create the searchline widget
0160         // and insert it into the existing layout
0161         m_searchWidget = new KListWidgetSearchLine(q, ui->m_payeesList);
0162         m_searchWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
0163         ui->m_payeesList->setContextMenuPolicy(Qt::CustomContextMenu);
0164         ui->m_listTopHLayout->insertWidget(0, m_searchWidget);
0165 
0166         //load the filter type
0167         ui->m_filterBox->addItem(i18nc("@item Show all payees", "All"));
0168         ui->m_filterBox->addItem(i18nc("@item Show only used payees", "Used"));
0169         ui->m_filterBox->addItem(i18nc("@item Show only unused payees", "Unused"));
0170         ui->m_filterBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
0171 
0172         ui->m_newButton->setIcon(Icons::get(Icon::ListAddUser));
0173         ui->m_renameButton->setIcon(Icons::get(Icon::UserProperties));
0174         ui->m_deleteButton->setIcon(Icons::get(Icon::ListRemoveUser));
0175         ui->m_mergeButton->setIcon(Icons::get(Icon::Merge));
0176         ui->m_updateButton->setIcon(Icons::get(Icon::DialogOK));
0177         ui->m_syncAddressbook->setIcon(Icons::get(Icon::Refresh));
0178         ui->m_sendMail->setIcon(Icons::get(Icon::MailMessage));
0179 
0180         ui->m_updateButton->setEnabled(false);
0181         ui->m_syncAddressbook->setEnabled(false);
0182 #ifndef ENABLE_ADDRESSBOOK
0183         ui->m_syncAddressbook->hide();
0184 #endif
0185         ui->matchTypeCombo->setCurrentIndex(0);
0186 
0187         ui->checkMatchIgnoreCase->setEnabled(false);
0188 
0189         ui->checkEnableDefaultCategory->setChecked(false);
0190         ui->labelDefaultCategory->setEnabled(false);
0191         ui->comboDefaultCategory->setEnabled(false);
0192 
0193         QList<eWidgets::eTransaction::Column> cols {
0194             eWidgets::eTransaction::Column::Date,
0195             eWidgets::eTransaction::Column::Account,
0196             eWidgets::eTransaction::Column::Detail,
0197             eWidgets::eTransaction::Column::ReconcileFlag,
0198             eWidgets::eTransaction::Column::Payment,
0199             eWidgets::eTransaction::Column::Deposit};
0200         ui->m_register->setupRegister(MyMoneyAccount(), cols);
0201         ui->m_register->setSelectionMode(QTableWidget::SingleSelection);
0202         ui->m_register->setDetailsColumnType(eWidgets::eRegister::DetailColumn::AccountFirst);
0203         ui->m_balanceLabel->hide();
0204 
0205         q->connect(m_contact, &MyMoneyContact::contactFetched, q, &KPayeesView::slotContactFetched);
0206 
0207         q->connect(ui->m_payeesList, static_cast<void (QListWidget::*)(QListWidgetItem*, QListWidgetItem*)>(&QListWidget::currentItemChanged), q, static_cast<void (KPayeesView::*)(QListWidgetItem*, QListWidgetItem*)>(&KPayeesView::slotSelectPayee));
0208         q->connect(ui->m_payeesList, &QListWidget::itemSelectionChanged, q,   static_cast<void (KPayeesView::*)()>(&KPayeesView::slotSelectPayee));
0209         q->connect(ui->m_payeesList, &QListWidget::itemDoubleClicked, q,      &KPayeesView::slotStartRename);
0210         q->connect(ui->m_payeesList, &QListWidget::itemChanged, q,            &KPayeesView::slotRenameSinglePayee);
0211         q->connect(ui->m_payeesList, &QWidget::customContextMenuRequested, q, &KPayeesView::slotShowPayeesMenu);
0212 
0213         q->connect(ui->m_newButton,     &QAbstractButton::clicked, q, &KPayeesView::slotNewPayee);
0214         q->connect(ui->m_renameButton,  &QAbstractButton::clicked, q, &KPayeesView::slotRenamePayee);
0215         q->connect(ui->m_deleteButton,  &QAbstractButton::clicked, q, &KPayeesView::slotDeletePayee);
0216         q->connect(ui->m_mergeButton,   &QAbstractButton::clicked, q, &KPayeesView::slotMergePayee);
0217 
0218         q->connect(ui->addressEdit,   &QTextEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0219         q->connect(ui->payeecityEdit,  &QLineEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0220         q->connect(ui->payeestateEdit,  &QLineEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0221         q->connect(ui->postcodeEdit,  &QLineEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0222         q->connect(ui->telephoneEdit, &QLineEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0223         q->connect(ui->emailEdit,     &QLineEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0224         q->connect(ui->notesEdit,     &QTextEdit::textChanged, q, &KPayeesView::slotPayeeDataChanged);
0225         q->connect(ui->matchKeyEditList, &KEditListWidget::changed, q, &KPayeesView::slotKeyListChanged);
0226 
0227         q->connect(ui->matchTypeCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), q, &KPayeesView::slotPayeeDataChanged);
0228         q->connect(ui->checkMatchIgnoreCase, &QAbstractButton::toggled, q, &KPayeesView::slotPayeeDataChanged);
0229 
0230         q->connect(ui->checkEnableDefaultCategory,  &QAbstractButton::toggled, q,               &KPayeesView::slotPayeeDataChanged);
0231         q->connect(ui->comboDefaultCategory,        &KMyMoneyAccountCombo::accountSelected, q,  &KPayeesView::slotPayeeDataChanged);
0232         q->connect(ui->buttonSuggestACategory,      &QAbstractButton::clicked, q,               &KPayeesView::slotChooseDefaultAccount);
0233 
0234         q->connect(ui->m_updateButton,    &QAbstractButton::clicked, q, &KPayeesView::slotUpdatePayee);
0235         q->connect(ui->m_syncAddressbook, &QAbstractButton::clicked, q, &KPayeesView::slotSyncAddressBook);
0236         q->connect(ui->m_helpButton,      &QAbstractButton::clicked, q, &KPayeesView::slotHelp);
0237         q->connect(ui->m_sendMail,        &QAbstractButton::clicked, q, &KPayeesView::slotSendMail);
0238 
0239         q->connect(ui->m_register, &KMyMoneyRegister::Register::editTransaction, q, &KPayeesView::slotSelectTransaction);
0240 
0241         q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KPayeesView::refresh);
0242 
0243         q->connect(ui->m_filterBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), q, &KPayeesView::slotChangeFilter);
0244 
0245         q->connect(ui->payeeIdentifiers, &KPayeeIdentifierView::dataChanged, q, &KPayeesView::slotPayeeDataChanged);
0246 
0247         // use the size settings of the last run (if any)
0248         KConfigGroup grp = KSharedConfig::openConfig()->group("Last Use Settings");
0249         ui->m_splitter->restoreState(grp.readEntry("KPayeesViewSplitterSize", QByteArray()));
0250         ui->m_splitter->setChildrenCollapsible(false);
0251 
0252         //At start we haven't any payee selected
0253         ui->m_tabWidget->setEnabled(false); // disable tab widget
0254         ui->m_deleteButton->setEnabled(false); //disable delete, rename and merge buttons
0255         ui->m_renameButton->setEnabled(false);
0256         ui->m_mergeButton->setEnabled(false);
0257         m_payee = MyMoneyPayee(); // make sure we don't access an undefined payee
0258         clearItemData();
0259     }
0260 
0261     void loadPayees()
0262     {
0263         Q_Q(KPayeesView);
0264         if (m_inSelection)
0265             return;
0266 
0267         QMap<QString, bool> isSelected;
0268         QString id;
0269         MyMoneyFile* file = MyMoneyFile::instance();
0270 
0271         // remember which items are selected in the list
0272         QList<QListWidgetItem *> selectedItems = ui->m_payeesList->selectedItems();
0273         QList<QListWidgetItem *>::const_iterator payeesIt = selectedItems.constBegin();
0274 
0275         while (payeesIt != selectedItems.constEnd()) {
0276             KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(*payeesIt);
0277             if (item)
0278                 isSelected[item->payee().id()] = true;
0279             ++payeesIt;
0280         }
0281 
0282         // keep current selected item
0283         KPayeeListItem *currentItem = static_cast<KPayeeListItem *>(ui->m_payeesList->currentItem());
0284         if (currentItem)
0285             id = currentItem->payee().id();
0286 
0287         m_allowEditing = false;
0288         // clear the list
0289         m_searchWidget->clear();
0290         m_searchWidget->updateSearch();
0291         ui->m_payeesList->clear();
0292         ui->m_register->clear();
0293         currentItem = 0;
0294 
0295         QList<MyMoneyPayee>list = file->payeeList();
0296         QList<MyMoneyPayee>::ConstIterator it;
0297 
0298         for (it = list.constBegin(); it != list.constEnd(); ++it) {
0299             if (m_payeeFilterType == eAllPayees ||
0300                     (m_payeeFilterType == eReferencedPayees && file->isReferenced(*it)) ||
0301                     (m_payeeFilterType == eUnusedPayees && !file->isReferenced(*it))) {
0302                 KPayeeListItem* item = new KPayeeListItem(ui->m_payeesList, *it);
0303                 if (item->payee().id() == id)
0304                     currentItem = item;
0305                 if (isSelected[item->payee().id()])
0306                     item->setSelected(true);
0307             }
0308         }
0309         ui->m_payeesList->sortItems();
0310 
0311         if (currentItem) {
0312             ui->m_payeesList->setCurrentItem(currentItem);
0313             ui->m_payeesList->scrollToItem(currentItem);
0314         }
0315 
0316         m_filterProxyModel->invalidate();
0317         ui->comboDefaultCategory->expandAll();
0318 
0319         q->slotSelectPayee(0, 0);
0320         m_allowEditing = true;
0321     }
0322 
0323     void selectedPayees(QList<MyMoneyPayee>& payeesList) const
0324     {
0325         QList<QListWidgetItem *> selectedItems = ui->m_payeesList->selectedItems();
0326         QList<QListWidgetItem *>::ConstIterator itemsIt = selectedItems.constBegin();
0327         while (itemsIt != selectedItems.constEnd()) {
0328             KPayeeListItem* item = dynamic_cast<KPayeeListItem*>(*itemsIt);
0329             if (item)
0330                 payeesList << item->payee();
0331             ++itemsIt;
0332         }
0333     }
0334 
0335     void ensurePayeeVisible(const QString& id)
0336     {
0337         for (int i = 0; i < ui->m_payeesList->count(); ++i) {
0338             KPayeeListItem* p = dynamic_cast<KPayeeListItem*>(ui->m_payeesList->item(0));
0339             if (p && p->payee().id() == id) {
0340                 ui->m_payeesList->scrollToItem(p, QAbstractItemView::PositionAtCenter);
0341 
0342                 ui->m_payeesList->setCurrentItem(p);      // active item and deselect all others
0343                 ui->m_payeesList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect);   // and select it
0344                 break;
0345             }
0346         }
0347     }
0348 
0349     void clearItemData()
0350     {
0351         ui->addressEdit->setText(QString());
0352         ui->payeecityEdit->setText(QString());
0353         ui->payeestateEdit->setText(QString());
0354         ui->postcodeEdit->setText(QString());
0355         ui->telephoneEdit->setText(QString());
0356         ui->emailEdit->setText(QString());
0357         ui->notesEdit->setText(QString());
0358         showTransactions();
0359     }
0360 
0361     /**
0362       * This method loads the m_transactionList, clears
0363       * the m_TransactionPtrVector and rebuilds and sorts
0364       * it according to the current settings. Then it
0365       * loads the m_transactionView with the transaction data.
0366       */
0367     void showTransactions()
0368     {
0369         MyMoneyMoney balance;
0370         const auto file = MyMoneyFile::instance();
0371         MyMoneySecurity base = file->baseCurrency();
0372 
0373         // setup sort order
0374         ui->m_register->setSortOrder(KMyMoneySettings::sortSearchView());
0375 
0376         // clear the register
0377         ui->m_register->clear();
0378 
0379         if (m_selectedPayeesList.isEmpty() || !ui->m_tabWidget->isEnabled()) {
0380             ui->m_balanceLabel->setText(i18n("Balance: %1", balance.formatMoney(file->baseCurrency().smallestAccountFraction())));
0381             return;
0382         }
0383 
0384         // setup the list and the pointer vector
0385         MyMoneyTransactionFilter filter;
0386 
0387         for (QList<MyMoneyPayee>::const_iterator it = m_selectedPayeesList.constBegin();
0388                 it != m_selectedPayeesList.constEnd();
0389                 ++it)
0390             filter.addPayee((*it).id());
0391 
0392         filter.setDateFilter(KMyMoneySettings::startDate().date(), QDate());
0393         filter.setConsiderCategorySplits(true);
0394 
0395         // retrieve the list from the engine
0396         file->transactionList(m_transactionList, filter);
0397 
0398         // create the elements for the register
0399         QList<QPair<MyMoneyTransaction, MyMoneySplit> >::const_iterator it;
0400         QMap<QString, int> uniqueMap;
0401         MyMoneyMoney deposit, payment;
0402 
0403         int splitCount = 0;
0404         bool balanceAccurate = true;
0405         for (it = m_transactionList.constBegin(); it != m_transactionList.constEnd(); ++it) {
0406             const MyMoneySplit& split = (*it).second;
0407             MyMoneyAccount acc = file->account(split.accountId());
0408             if (!acc.isIncomeExpense()) {
0409                 ++splitCount;
0410                 uniqueMap[(*it).first.id()]++;
0411 
0412                 KMyMoneyRegister::Register::transactionFactory(ui->m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]);
0413 
0414                 // take care of foreign currencies
0415                 MyMoneyMoney val = split.shares().abs();
0416                 if (acc.currencyId() != base.id()) {
0417                     const MyMoneyPrice& price = file->price(acc.currencyId(), base.id());
0418                     // in case the price is valid, we use it. Otherwise, we keep
0419                     // a flag that tells us that the balance is somewhat inaccurate
0420                     if (price.isValid()) {
0421                         val *= price.rate(base.id());
0422                     } else {
0423                         balanceAccurate = false;
0424                     }
0425                 }
0426 
0427                 if (split.shares().isNegative()) {
0428                     payment += val;
0429                 } else {
0430                     deposit += val;
0431                 }
0432             }
0433         }
0434         balance = deposit - payment;
0435 
0436         // add the group markers
0437         ui->m_register->addGroupMarkers();
0438 
0439         // sort the transactions according to the sort setting
0440         ui->m_register->sortItems();
0441 
0442         // remove trailing and adjacent markers
0443         ui->m_register->removeUnwantedGroupMarkers();
0444 
0445         ui->m_register->updateRegister(true);
0446 
0447         // we might end up here with updates disabled on the register so
0448         // make sure that we enable updates here
0449         ui->m_register->setUpdatesEnabled(true);
0450         ui->m_balanceLabel->setText(i18n("Balance: %1%2",
0451                                          balanceAccurate ? "" : "~",
0452                                          balance.formatMoney(file->baseCurrency().smallestAccountFraction())));
0453     }
0454 
0455     /**
0456       * Implement common task when deleting or merging payees
0457       */
0458     bool payeeReassign(int type)
0459     {
0460         Q_Q(KPayeesView);
0461         if (!(type >= 0 && type < KPayeeReassignDlg::TypeCount))
0462             return false;
0463 
0464         auto addPatternOrName = [&](const QString& pattern) {
0465             if (pattern.startsWith(QLatin1String("^")) && pattern.startsWith(QLatin1String("^"))) {
0466                 return pattern;
0467             }
0468             return QRegularExpression::escape(pattern);
0469         };
0470 
0471         const auto file = MyMoneyFile::instance();
0472 
0473         MyMoneyFileTransaction ft;
0474         try {
0475             // create a transaction filter that contains all payees selected for removal
0476             MyMoneyTransactionFilter f = MyMoneyTransactionFilter();
0477             for (QList<MyMoneyPayee>::const_iterator it = m_selectedPayeesList.constBegin();
0478                     it != m_selectedPayeesList.constEnd(); ++it) {
0479                 f.addPayee((*it).id());
0480             }
0481             f.setConsiderCategorySplits(true);
0482             // request a list of all transactions that still use the payees in question
0483             QList<MyMoneyTransaction> translist = file->transactionList(f);
0484             //     qDebug() << "[KPayeesView::slotDeletePayee]  " << translist.count() << " transaction still assigned to payees";
0485 
0486             // now get a list of all schedules that make use of one of the payees
0487             QList<MyMoneySchedule> used_schedules;
0488             foreach (const auto schedule, file->scheduleList()) {
0489                 // loop over all splits in the transaction of the schedule
0490                 foreach (const auto split, schedule.transaction().splits()) {
0491                     // is the payee in the split to be deleted?
0492                     if (payeeInList(m_selectedPayeesList, split.payeeId())) {
0493                         used_schedules.push_back(schedule); // remember this schedule
0494                         break;
0495                     }
0496                 }
0497             }
0498             //     qDebug() << "[KPayeesView::slotDeletePayee]  " << used_schedules.count() << " schedules use one of the selected payees";
0499 
0500             // and a list of all loan accounts that references one of the payees
0501             QList<MyMoneyAccount> allAccounts;
0502             QList<MyMoneyAccount> usedAccounts;
0503             file->accountList(allAccounts);
0504             foreach (const MyMoneyAccount &account, allAccounts) {
0505                 if (account.isLoan()) {
0506                     MyMoneyAccountLoan loanAccount(account);
0507                     foreach (const MyMoneyPayee &payee, m_selectedPayeesList) {
0508                         if (loanAccount.hasReferenceTo(payee.id())) {
0509                             usedAccounts.append(account);
0510                         }
0511                     }
0512                 }
0513             }
0514 
0515 
0516             MyMoneyPayee newPayee;
0517             bool addToMatchList = false;
0518             // if at least one payee is still referenced, we need to reassign its transactions first
0519             if (!translist.isEmpty() || !used_schedules.isEmpty() || !usedAccounts.isEmpty()) {
0520 
0521                 // first create list with all non-selected payees
0522                 QList<MyMoneyPayee> remainingPayees;
0523                 if (type == KPayeeReassignDlg::TypeMerge) {
0524                     remainingPayees = m_selectedPayeesList;
0525                 } else {
0526                     remainingPayees = file->payeeList();
0527                     QList<MyMoneyPayee>::iterator it_p;
0528                     for (it_p = remainingPayees.begin(); it_p != remainingPayees.end();) {
0529                         if (m_selectedPayeesList.contains(*it_p)) {
0530                             it_p = remainingPayees.erase(it_p);
0531                         } else {
0532                             ++it_p;
0533                         }
0534                     }
0535                 }
0536 
0537                 // show error message if no payees remain
0538                 if (remainingPayees.isEmpty()) {
0539                     KMessageBox::sorry(q, i18n("At least one transaction/scheduled transaction or loan account is still referenced by a payee. "
0540                                                "Currently you have all payees selected. However, at least one payee must remain so "
0541                                                "that the transaction/scheduled transaction or loan account can be reassigned."));
0542                     return false;
0543                 }
0544 
0545                 // show transaction reassignment dialog
0546                 KPayeeReassignDlg * dlg = new KPayeeReassignDlg(static_cast<KPayeeReassignDlg::OperationType>(type), q);
0547                 KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart());
0548                 QString payee_id = dlg->show(remainingPayees);
0549                 addToMatchList = dlg->addToMatchList();
0550                 delete dlg; // and kill the dialog
0551                 if (payee_id.isEmpty())
0552                     return false; // the user aborted the dialog, so let's abort as well
0553 
0554                 // try to get selected payee. If not possible and we are merging payees,
0555                 // then we create a new one
0556                 try {
0557                     newPayee = file->payee(payee_id);
0558                 } catch (const MyMoneyException &) {
0559                     if (type == KPayeeReassignDlg::TypeMerge) {
0560                         // it's ok to use payee_id for both arguments since the first is const,
0561                         // so it's guaranteed not to change its content
0562                         if (!KMyMoneyUtils::newPayee(payee_id, payee_id))
0563                             return false; // the user aborted the dialog, so let's abort as well
0564                         newPayee = file->payee(payee_id);
0565                     } else {
0566                         return false;
0567                     }
0568                 }
0569 
0570                 // TODO : check if we have a report that explicitly uses one of our payees
0571                 //        and issue an appropriate warning
0572                 try {
0573                     // now loop over all transactions and reassign payee
0574                     for (auto& transaction : translist) {
0575                         // create a copy of the splits list in the transaction
0576                         // loop over all splits
0577                         for (auto& split : transaction.splits()) {
0578                             // if the split is assigned to one of the selected payees, we need to modify it
0579                             if (split.isMatched()) {
0580                                 auto tm = split.matchedTransaction();
0581                                 for (auto& sm : tm.splits()) {
0582                                     if (payeeInList(m_selectedPayeesList, sm.payeeId())) {
0583                                         sm.setPayeeId(payee_id); // first modify payee in current split
0584                                         // then modify the split in our local copy of the transaction list
0585                                         tm.modifySplit(sm); // this does not modify the list object 'splits'!
0586                                     }
0587                                 }
0588                                 split.addMatch(tm);
0589                                 transaction.modifySplit(split); // this does not modify the list object 'splits'!
0590                             }
0591                             if (payeeInList(m_selectedPayeesList, split.payeeId())) {
0592                                 split.setPayeeId(payee_id); // first modify payee in current split
0593                                 // then modify the split in our local copy of the transaction list
0594                                 transaction.modifySplit(split); // this does not modify the list object 'splits'!
0595                             }
0596                         } // for - Splits
0597                         file->modifyTransaction(transaction);  // modify the transaction in the MyMoney object
0598                     } // for - Transactions
0599 
0600                     // now loop over all schedules and reassign payees
0601                     for (auto& schedule : used_schedules) {
0602                         // create copy of transaction in current schedule
0603                         auto trans = schedule.transaction();
0604                         // create copy of lists of splits
0605                         for (auto& split : trans.splits()) {
0606                             if (payeeInList(m_selectedPayeesList, split.payeeId())) {
0607                                 split.setPayeeId(payee_id);
0608                                 trans.modifySplit(split); // does not modify the list object 'splits'!
0609                             }
0610                         } // for - Splits
0611                         // store transaction in current schedule
0612                         schedule.setTransaction(trans);
0613                         file->modifySchedule(schedule);  // modify the schedule in the MyMoney engine
0614                     } // for - Schedules
0615 
0616                     // reassign the payees in the loans that reference the deleted payees
0617                     foreach (const MyMoneyAccount &account, usedAccounts) {
0618                         MyMoneyAccountLoan loanAccount(account);
0619                         loanAccount.setPayee(payee_id);
0620                         file->modifyAccount(loanAccount);
0621                     }
0622 
0623                 } catch (const MyMoneyException &e) {
0624                     KMessageBox::detailedSorry(q, i18n("Unable to reassign payee of transaction/split"), e.what());
0625                 }
0626             } else { // if !translist.isEmpty()
0627                 if (type == KPayeeReassignDlg::TypeMerge) {
0628                     KMessageBox::sorry(q, i18n("Nothing to merge."), i18n("Merge Payees"));
0629                     return false;
0630                 }
0631             }
0632 
0633             bool ignorecase;
0634             QStringList payeeNames;
0635             auto matchType = newPayee.matchData(ignorecase, payeeNames);
0636             QStringList deletedMatchPattern;
0637 
0638             // now loop over all selected payees and remove them
0639             for (QList<MyMoneyPayee>::iterator it = m_selectedPayeesList.begin();
0640                     it != m_selectedPayeesList.end(); ++it) {
0641                 if (newPayee.id() != (*it).id()) {
0642                     if (addToMatchList) {
0643                         QStringList matchPattern;
0644                         auto mType = (*it).matchData(ignorecase, matchPattern);
0645                         switch (mType) {
0646                         case eMyMoney::Payee::MatchType::NameExact:
0647                             matchPattern << QStringLiteral("^%1$").arg((*it).name());
0648                             break;
0649                         case eMyMoney::Payee::MatchType::Name:
0650                             matchPattern << (*it).name();
0651                             break;
0652                         default:
0653                             break;
0654                         }
0655                         if (!matchPattern.isEmpty()) {
0656                             deletedMatchPattern << matchPattern;
0657                         }
0658                     }
0659                     file->removePayee(*it);
0660                 }
0661             }
0662 
0663             // if we initially have no matching turned on, we just ignore the case (default)
0664             if (matchType == eMyMoney::Payee::MatchType::Disabled)
0665                 ignorecase = true;
0666 
0667             // update the destination payee if this was requested by the user
0668             if (addToMatchList && deletedMatchPattern.count() > 0) {
0669                 // add new names to the list
0670                 // TODO: it would be cool to somehow shrink the list to make better use
0671                 //       of regular expressions at this point. For now, we leave this task
0672                 //       to the user himeself.
0673                 QStringList::const_iterator it_n;
0674                 for (it_n = deletedMatchPattern.constBegin(); it_n != deletedMatchPattern.constEnd(); ++it_n) {
0675                     if (matchType == eMyMoney::Payee::MatchType::Key) {
0676                         // make sure we really need it and it is not caught by an existing regexp
0677                         QStringList::const_iterator it_k;
0678                         for (it_k = payeeNames.constBegin(); it_k != payeeNames.constEnd(); ++it_k) {
0679                             QRegExp exp(*it_k, ignorecase ? Qt::CaseInsensitive : Qt::CaseSensitive);
0680                             if (exp.indexIn(*it_n) != -1)
0681                                 break;
0682                         }
0683                         if (it_k == payeeNames.constEnd()) {
0684                             payeeNames << addPatternOrName(*it_n);
0685                         }
0686                     } else if (payeeNames.contains(*it_n) == 0)
0687                         payeeNames << addPatternOrName(*it_n);
0688                 }
0689 
0690                 // and update the payee in the engine context
0691                 // make sure to turn on matching for this payee in the right mode
0692                 newPayee.setMatchData(eMyMoney::Payee::MatchType::Key, ignorecase, payeeNames);
0693                 file->modifyPayee(newPayee);
0694             }
0695             ft.commit();
0696 
0697             // If we just deleted the payees, they sure don't exist anymore
0698             m_selectedPayeesList.clear();
0699 
0700         } catch (const MyMoneyException &e) {
0701             KMessageBox::detailedSorry(q, i18n("Unable to remove payee(s)"), e.what());
0702         }
0703 
0704         return true;
0705     }
0706 
0707     /**
0708       * Check if a list contains a payee with a given id
0709       *
0710       * @param list const reference to value list
0711       * @param id const reference to id
0712       *
0713       * @retval true object has been found
0714       * @retval false object is not in list
0715       */
0716     bool payeeInList(const QList<MyMoneyPayee>& list, const QString& id) const
0717     {
0718         bool rc = false;
0719         QList<MyMoneyPayee>::const_iterator it_p = list.begin();
0720         while (it_p != list.end()) {
0721             if ((*it_p).id() == id) {
0722                 rc = true;
0723                 break;
0724             }
0725             ++it_p;
0726         }
0727         return rc;
0728     }
0729 
0730     /** Checks whether the currently selected payee is "dirty"
0731      * @return true, if payee is modified (is "dirty"); false otherwise
0732      */
0733     bool isDirty() const
0734     {
0735         return ui->m_updateButton->isEnabled();
0736     }
0737 
0738     /** Sets the payee's "dirty" (modified) status
0739      * @param dirty if true (default), payee will be set to dirty
0740      */
0741     void setDirty(bool dirty)
0742     {
0743         ui->m_updateButton->setEnabled(dirty);
0744     }
0745 
0746     KPayeesView      *q_ptr;
0747     Ui::KPayeesView  *ui;
0748     MyMoneyPayee      m_payee;
0749     QString           m_newName;
0750     MyMoneyContact   *m_contact;
0751     int               m_payeeRow;
0752     QList<int>        m_payeeRows;
0753 
0754     /**
0755       * List of selected payees
0756       */
0757     QList<MyMoneyPayee> m_selectedPayeesList;
0758 
0759     /**
0760       * q member holds a list of all transactions
0761       */
0762     QList<QPair<MyMoneyTransaction, MyMoneySplit> > m_transactionList;
0763 
0764     /**
0765       * q member holds the load state of page
0766       */
0767     bool m_needLoad;
0768 
0769     /**
0770       * Search widget for the list
0771       */
0772     KListWidgetSearchLine*  m_searchWidget;
0773 
0774     /**
0775      * Semaphore to suppress loading during selection
0776      */
0777     bool m_inSelection;
0778 
0779     /**
0780      * q signals whether a payee can be edited
0781      **/
0782     bool m_allowEditing;
0783 
0784     /**
0785       * q holds the filter type
0786       */
0787     int m_payeeFilterType;
0788 
0789     AccountNamesFilterProxyModel *m_filterProxyModel;
0790 };
0791 
0792 #endif