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

0001 /*
0002     SPDX-FileCopyrightText: 2006 Thomas Baumgart <ipwizard@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kgloballedgerview_p.h"
0008 
0009 #include <typeinfo>
0010 
0011 // ----------------------------------------------------------------------------
0012 // QT Includes
0013 
0014 #include <QFrame>
0015 #include <QList>
0016 #include <QLabel>
0017 #include <QEvent>
0018 #include <QApplication>
0019 #include <QTimer>
0020 #include <QMenu>
0021 #include <QClipboard>
0022 
0023 // ----------------------------------------------------------------------------
0024 // KDE Includes
0025 
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 #include <KToolBar>
0029 #include <KActionCollection>
0030 #include <KXmlGuiWindow>
0031 
0032 // ----------------------------------------------------------------------------
0033 // Project Includes
0034 
0035 #include "mymoneyaccount.h"
0036 #include "mymoneyfile.h"
0037 #include "kmymoneyaccountcombo.h"
0038 #include "kmymoneypayeecombo.h"
0039 #include "keditscheduledlg.h"
0040 #include "kendingbalancedlg.h"
0041 #include "register.h"
0042 #include "transactioneditor.h"
0043 #include "selectedtransactions.h"
0044 #include "kmymoneysettings.h"
0045 #include "registersearchline.h"
0046 #include "kfindtransactiondlg.h"
0047 #include "accountsmodel.h"
0048 #include "models.h"
0049 #include "mymoneyschedule.h"
0050 #include "mymoneysecurity.h"
0051 #include "mymoneytransaction.h"
0052 #include "mymoneytransactionfilter.h"
0053 #include "mymoneysplit.h"
0054 #include "transaction.h"
0055 #include "transactionform.h"
0056 #include "widgetenums.h"
0057 #include "mymoneyenums.h"
0058 #include "menuenums.h"
0059 
0060 using namespace eMenu;
0061 
0062 QDate KGlobalLedgerViewPrivate::m_lastPostDate;
0063 
0064 KGlobalLedgerView::KGlobalLedgerView(QWidget *parent) :
0065     KMyMoneyViewBase(*new KGlobalLedgerViewPrivate(this), parent)
0066 {
0067     // clang-format off
0068     const QHash<Action, std::function<void()>> actionConnections {
0069         {Action::NewTransaction,            [this](){ KGlobalLedgerView::slotNewTransaction(); }},
0070         {Action::EditTransaction,           [this](){ KGlobalLedgerView::slotEditTransaction(); }},
0071         {Action::DeleteTransaction,         [this](){ KGlobalLedgerView::slotDeleteTransaction(); }},
0072         {Action::DuplicateTransaction,      [this](){ KGlobalLedgerView::slotDuplicateTransaction(); }},
0073         {Action::AddReversingTransaction,   [this](){ KGlobalLedgerView::slotDuplicateTransaction(true); }},
0074         {Action::EnterTransaction,          [this](){ KGlobalLedgerView::slotEnterTransaction(); }},
0075         {Action::AcceptTransaction,         [this](){ KGlobalLedgerView::slotAcceptTransaction(); }},
0076         {Action::CancelTransaction,         [this](){ KGlobalLedgerView::slotCancelTransaction(); }},
0077         {Action::EditSplits,                [this](){ KGlobalLedgerView::slotEditSplits(); }},
0078         {Action::CopySplits,                [this](){ KGlobalLedgerView::slotCopySplits(); }},
0079         {Action::GoToPayee,                 [this](){ KGlobalLedgerView::slotGoToPayee(); }},
0080         {Action::GoToAccount,               [this](){ KGlobalLedgerView::slotGoToAccount(); }},
0081         {Action::MatchTransaction,          [this](){ KGlobalLedgerView::slotMatchTransactions(); }},
0082         {Action::CombineTransactions,       [this](){ KGlobalLedgerView::slotCombineTransactions(); }},
0083         {Action::ToggleReconciliationFlag,  [this](){ KGlobalLedgerView::slotToggleReconciliationFlag(); }},
0084         {Action::MarkCleared,               [this](){ KGlobalLedgerView::slotMarkCleared(); }},
0085         {Action::MarkReconciled,            [this](){ KGlobalLedgerView::slotMarkReconciled(); }},
0086         {Action::MarkNotReconciled,         [this](){ KGlobalLedgerView::slotMarkNotReconciled(); }},
0087         {Action::SelectAllTransactions,     [this](){ KGlobalLedgerView::slotSelectAllTransactions(); }},
0088         {Action::NewScheduledTransaction,   [this](){ KGlobalLedgerView::slotCreateScheduledTransaction(); }},
0089         {Action::AssignTransactionsNumber,  [this](){ KGlobalLedgerView::slotAssignNumber(); }},
0090         {Action::StartReconciliation,       [this](){ KGlobalLedgerView::slotStartReconciliation(); }},
0091         {Action::FinishReconciliation,      [this](){ KGlobalLedgerView::slotFinishReconciliation(); }},
0092         {Action::PostponeReconciliation,    [this](){ KGlobalLedgerView::slotPostponeReconciliation(); }},
0093         {Action::OpenAccount,               [this](){ KGlobalLedgerView::slotOpenAccount(); }},
0094         {Action::EditFindTransaction,       [this](){ KGlobalLedgerView::slotFindTransaction(); }},
0095     };
0096     // clang-format on
0097 
0098     for (auto a = actionConnections.cbegin(); a != actionConnections.cend(); ++a)
0099         connect(pActions[a.key()], &QAction::triggered, this, a.value());
0100 
0101     KXmlGuiWindow* mw = KMyMoneyUtils::mainWindow();
0102     KStandardAction::copy(this, &KGlobalLedgerView::slotCopyTransactionToClipboard,  mw->actionCollection());
0103 
0104     Q_D(KGlobalLedgerView);
0105     d->m_balanceWarning.reset(new KBalanceWarning(this));
0106 }
0107 
0108 KGlobalLedgerView::~KGlobalLedgerView()
0109 {
0110 }
0111 
0112 void KGlobalLedgerView::executeCustomAction(eView::Action action)
0113 {
0114     Q_D(KGlobalLedgerView);
0115     switch(action) {
0116     case eView::Action::Refresh:
0117         refresh();
0118         break;
0119 
0120     case eView::Action::SetDefaultFocus:
0121         // delay the setFocus call until the event loop is running
0122         QMetaObject::invokeMethod(d->m_registerSearchLine->searchLine(), "setFocus", Qt::QueuedConnection);
0123         break;
0124 
0125     case eView::Action::DisableViewDepenedendActions:
0126         pActions[Action::SelectAllTransactions]->setEnabled(false);
0127         break;
0128 
0129     case eView::Action::InitializeAfterFileOpen:
0130         d->m_lastSelectedAccountID.clear();
0131         d->m_currentAccount = MyMoneyAccount();
0132         if (d->m_accountComboBox) {
0133             d->m_accountComboBox->setSelected(QString());
0134         }
0135         break;
0136 
0137     case eView::Action::CleanupBeforeFileClose:
0138         if (d->m_inEditMode) {
0139             d->deleteTransactionEditor();
0140         }
0141         break;
0142 
0143     default:
0144         break;
0145     }
0146 }
0147 
0148 void KGlobalLedgerView::refresh()
0149 {
0150     Q_D(KGlobalLedgerView);
0151     if (isVisible()) {
0152         if (!d->m_inEditMode) {
0153             setUpdatesEnabled(false);
0154             d->loadView();
0155             setUpdatesEnabled(true);
0156             d->m_needsRefresh = false;
0157             // force a new account if the current one is empty
0158             d->m_newAccountLoaded = d->m_currentAccount.id().isEmpty();
0159         }
0160     } else {
0161         d->m_needsRefresh = true;
0162     }
0163 }
0164 
0165 void KGlobalLedgerView::showEvent(QShowEvent* event)
0166 {
0167     if (MyMoneyFile::instance()->storageAttached()) {
0168         Q_D(KGlobalLedgerView);
0169         if (d->m_needLoad)
0170             d->init();
0171 
0172         emit customActionRequested(View::Ledgers, eView::Action::AboutToShow);
0173 
0174         if (d->m_needsRefresh) {
0175             if (!d->m_inEditMode) {
0176                 setUpdatesEnabled(false);
0177                 d->loadView();
0178                 setUpdatesEnabled(true);
0179                 d->m_needsRefresh = false;
0180                 d->m_newAccountLoaded = false;
0181             }
0182 
0183         } else {
0184             if (!d->m_lastSelectedAccountID.isEmpty()) {
0185                 try {
0186                     const auto acc = MyMoneyFile::instance()->account(d->m_lastSelectedAccountID);
0187                     slotSelectAccount(acc.id());
0188                 } catch (const MyMoneyException &) {
0189                     d->m_lastSelectedAccountID.clear();                                               // account is invalid
0190                 }
0191             } else {
0192                 slotSelectAccount(d->m_accountComboBox->getSelected());
0193             }
0194 
0195             KMyMoneyRegister::SelectedTransactions list(d->m_register);
0196             updateLedgerActions(list);
0197             emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
0198         }
0199     }
0200 
0201     pActions[Action::SelectAllTransactions]->setEnabled(true);
0202     // don't forget base class implementation
0203     QWidget::showEvent(event);
0204 }
0205 
0206 void KGlobalLedgerView::updateActions(const MyMoneyObject& obj)
0207 {
0208     Q_D(KGlobalLedgerView);
0209 //  if (typeid(obj) != typeid(MyMoneyAccount) &&
0210 //      (obj.id().isEmpty() && d->m_currentAccount.id().isEmpty())) // do not disable actions that were already disabled))
0211 //    return;
0212 
0213     const auto& acc = static_cast<const MyMoneyAccount&>(obj);
0214 
0215     const QVector<Action> actionsToBeDisabled {
0216         Action::StartReconciliation,
0217         Action::FinishReconciliation,
0218         Action::PostponeReconciliation,
0219         Action::OpenAccount,
0220         Action::NewTransaction,
0221     };
0222 
0223     for (const auto& a : actionsToBeDisabled)
0224         pActions[a]->setEnabled(false);
0225 
0226     auto b = acc.isClosed() ? false : true;
0227     pMenus[Menu::MoveTransaction]->setEnabled(b);
0228 
0229     QString tooltip;
0230     pActions[Action::NewTransaction]->setEnabled(canCreateTransactions(tooltip) || !isVisible());
0231     pActions[Action::NewTransaction]->setToolTip(tooltip);
0232 
0233     const auto file = MyMoneyFile::instance();
0234     if (!acc.id().isEmpty() && !file->isStandardAccount(acc.id())) {
0235         switch (acc.accountGroup()) {
0236         case eMyMoney::Account::Type::Asset:
0237         case eMyMoney::Account::Type::Liability:
0238         case eMyMoney::Account::Type::Equity:
0239             pActions[Action::OpenAccount]->setEnabled(true);
0240             if (acc.accountGroup() != eMyMoney::Account::Type::Equity) {
0241                 if (d->m_reconciliationAccount.id().isEmpty()) {
0242                     pActions[Action::StartReconciliation]->setEnabled(true);
0243                     pActions[Action::StartReconciliation]->setToolTip(i18n("Reconcile"));
0244                 } else {
0245                     auto tip = i18n("Reconcile - disabled because you are currently reconciling <b>%1</b>", d->m_reconciliationAccount.name());
0246                     pActions[Action::StartReconciliation]->setToolTip(tip);
0247                     if (!d->m_transactionEditor) {
0248                         pActions[Action::FinishReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id());
0249                         pActions[Action::PostponeReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id());
0250                     }
0251                 }
0252             }
0253             break;
0254         case eMyMoney::Account::Type::Income :
0255         case eMyMoney::Account::Type::Expense :
0256             pActions[Action::OpenAccount]->setEnabled(true);
0257             break;
0258         default:
0259             break;
0260         }
0261     }
0262 
0263     d->m_currentAccount = acc;
0264 //  slotSelectAccount(acc);
0265 }
0266 
0267 void KGlobalLedgerView::updateLedgerActions(const KMyMoneyRegister::SelectedTransactions& list)
0268 {
0269     Q_D(KGlobalLedgerView);
0270 
0271     d->selectTransactions(list);
0272     updateLedgerActionsInternal();
0273 }
0274 
0275 void KGlobalLedgerView::updateLedgerActionsInternal()
0276 {
0277     Q_D(KGlobalLedgerView);
0278     const QVector<Action> actionsToBeDisabled {
0279         Action::EditTransaction, Action::EditSplits, Action::EnterTransaction,
0280         Action::CancelTransaction, Action::DeleteTransaction, Action::MatchTransaction,
0281         Action::AcceptTransaction, Action::DuplicateTransaction, Action::AddReversingTransaction, Action::ToggleReconciliationFlag, Action::MarkCleared,
0282         Action::GoToAccount, Action::GoToPayee, Action::AssignTransactionsNumber, Action::NewScheduledTransaction,
0283         Action::CombineTransactions, Action::CopySplits,
0284     };
0285 
0286     for (const auto& a : actionsToBeDisabled)
0287         pActions[a]->setEnabled(false);
0288 
0289     const auto file = MyMoneyFile::instance();
0290 
0291     pActions[Action::MatchTransaction]->setText(i18nc("Button text for match transaction", "Match"));
0292 //  pActions[Action::TransactionNew]->setToolTip(i18n("Create a new transaction"));
0293 
0294     pMenus[Menu::MoveTransaction]->setEnabled(false);
0295     pMenus[Menu::MarkTransaction]->setEnabled(false);
0296     pMenus[Menu::MarkTransactionContext]->setEnabled(false);
0297 
0298     if (!d->m_selectedTransactions.isEmpty() && !d->m_selectedTransactions.first().isScheduled()) {
0299         // enable 'delete transaction' only if at least one of the
0300         // selected transactions does not reference a closed account
0301         bool enable = false;
0302         KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
0303         for (it_t = d->m_selectedTransactions.constBegin(); (enable == false) && (it_t != d->m_selectedTransactions.constEnd()); ++it_t) {
0304             enable = !(*it_t).transaction().id().isEmpty() && !file->referencesClosedAccount((*it_t).transaction());
0305         }
0306         pActions[Action::DeleteTransaction]->setEnabled(enable);
0307 
0308         if (!d->m_transactionEditor) {
0309             QString tooltip = i18n("Duplicate the current selected transactions");
0310             pActions[Action::DuplicateTransaction]->setEnabled(canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty());
0311             pActions[Action::DuplicateTransaction]->setToolTip(tooltip);
0312 
0313             tooltip = i18n("Add reversing transactions to the currently selected");
0314             pActions[Action::AddReversingTransaction]->setEnabled(canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty());
0315             pActions[Action::AddReversingTransaction]->setToolTip(tooltip);
0316 
0317             if (canEditTransactions(d->m_selectedTransactions, tooltip)) {
0318                 pActions[Action::EditTransaction]->setEnabled(true);
0319                 // editing splits is allowed only if we have one transaction selected
0320                 if (d->m_selectedTransactions.count() == 1) {
0321                     pActions[Action::EditSplits]->setEnabled(true);
0322                 }
0323                 if (d->m_currentAccount.isAssetLiability() && (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment)
0324                     && canCreateTransactions(tooltip)) {
0325                     pActions[Action::NewScheduledTransaction]->setEnabled(d->m_selectedTransactions.count() == 1);
0326                 }
0327             }
0328             pActions[Action::EditTransaction]->setToolTip(tooltip);
0329 
0330             if (!d->m_currentAccount.isClosed())
0331                 pMenus[Menu::MoveTransaction]->setEnabled(true);
0332 
0333             pMenus[Menu::MarkTransaction]->setEnabled(true);
0334             pMenus[Menu::MarkTransactionContext]->setEnabled(true);
0335 
0336             // Allow marking the transaction if at least one is selected
0337             pActions[Action::MarkCleared]->setEnabled(true);
0338             pActions[Action::MarkReconciled]->setEnabled(true);
0339             pActions[Action::MarkNotReconciled]->setEnabled(true);
0340             pActions[Action::ToggleReconciliationFlag]->setEnabled(true);
0341 
0342             if (!d->m_accountGoto.isEmpty())
0343                 pActions[Action::GoToAccount]->setEnabled(true);
0344             if (!d->m_payeeGoto.isEmpty())
0345                 pActions[Action::GoToPayee]->setEnabled(true);
0346 
0347             // Matching is enabled as soon as one regular and one imported transaction is selected
0348             int matchedCount = 0;
0349             int importedCount = 0;
0350             KMyMoneyRegister::SelectedTransactions::const_iterator it;
0351             for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) {
0352                 if ((*it).transaction().isImported())
0353                     ++importedCount;
0354                 if ((*it).split().isMatched())
0355                     ++matchedCount;
0356             }
0357 
0358             if (d->m_selectedTransactions.count() == 2 /* && pActions[Action::TransactionEdit]->isEnabled() */) {
0359                 pActions[Action::MatchTransaction]->setEnabled(true);
0360             }
0361             if (importedCount != 0 || matchedCount != 0)
0362                 pActions[Action::AcceptTransaction]->setEnabled(true);
0363             if (matchedCount != 0) {
0364                 pActions[Action::MatchTransaction]->setEnabled(true);
0365                 pActions[Action::MatchTransaction]->setText(i18nc("Button text for unmatch transaction", "Unmatch"));
0366                 pActions[Action::MatchTransaction]->setIcon(QIcon("process-stop"));
0367             }
0368 
0369             if (d->m_selectedTransactions.count() > 1) {
0370                 pActions[Action::CombineTransactions]->setEnabled(true);
0371             }
0372             if (d->m_selectedTransactions.count() >= 2) {
0373                 int singleSplitTransactions = 0;
0374                 int multipleSplitTransactions = 0;
0375                 foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
0376                     switch (st.transaction().splitCount()) {
0377                     case 0:
0378                         break;
0379                     case 1:
0380                         singleSplitTransactions++;
0381                         break;
0382                     default:
0383                         multipleSplitTransactions++;
0384                         break;
0385                     }
0386                 }
0387                 if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
0388                     pActions[Action::CopySplits]->setEnabled(true);
0389                 }
0390             }
0391             if (d->m_selectedTransactions.count() >= 2) {
0392                 int singleSplitTransactions = 0;
0393                 int multipleSplitTransactions = 0;
0394                 foreach(const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
0395                     switch(st.transaction().splitCount()) {
0396                     case 0:
0397                         break;
0398                     case 1:
0399                         singleSplitTransactions++;
0400                         break;
0401                     default:
0402                         multipleSplitTransactions++;
0403                         break;
0404                     }
0405                 }
0406                 if(singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
0407                     pActions[Action::CopySplits]->setEnabled(true);
0408                 }
0409             }
0410         } else {
0411             pActions[Action::AssignTransactionsNumber]->setEnabled(d->m_transactionEditor->canAssignNumber());
0412             pActions[Action::NewTransaction]->setEnabled(false);
0413             pActions[Action::DeleteTransaction]->setEnabled(false);
0414             QString reason;
0415             pActions[Action::EnterTransaction]->setEnabled(d->m_transactionEditor->isComplete(reason));
0416             //FIXME: Port to KDE4
0417             // the next line somehow worked in KDE3 but does not have
0418             // any influence under KDE4
0419             ///  Works for me when 'reason' is set. Allan
0420             pActions[Action::EnterTransaction]->setToolTip(reason);
0421             pActions[Action::CancelTransaction]->setEnabled(true);
0422         }
0423     }
0424 }
0425 
0426 void KGlobalLedgerView::slotAboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect)
0427 {
0428     Q_UNUSED(item);
0429     slotCancelOrEnterTransactions(okToSelect);
0430 }
0431 
0432 void KGlobalLedgerView::slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions& selection)
0433 {
0434     Q_D(KGlobalLedgerView);
0435     if (selection.count() > 1) {
0436         MyMoneyMoney balance;
0437         foreach (const KMyMoneyRegister::SelectedTransaction& t, selection) {
0438             if (!t.isScheduled()) {
0439                 balance += t.split().shares();
0440             }
0441         }
0442         d->m_rightSummaryLabel->setText(QString("%1: %2").arg(QChar(0x2211), balance.formatMoney("", -1)));
0443 
0444     } else {
0445         if (d->isReconciliationAccount()) {
0446             d->m_rightSummaryLabel->setText(i18n("Difference: %1", d->m_totalBalance.formatMoney("", d->m_precision)));
0447 
0448         } else {
0449             if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
0450                 d->m_rightSummaryLabel->setText(i18n("Balance: %1", d->m_totalBalance.formatMoney("", d->m_precision)));
0451                 bool showNegative = d->m_totalBalance.isNegative();
0452                 if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Liability && !d->m_totalBalance.isZero())
0453                     showNegative = !showNegative;
0454                 if (showNegative) {
0455                     QPalette palette = d->m_rightSummaryLabel->palette();
0456                     palette.setColor(d->m_rightSummaryLabel->foregroundRole(), KMyMoneySettings::schemeColor(SchemeColor::Negative));
0457                     d->m_rightSummaryLabel->setPalette(palette);
0458                 }
0459             } else {
0460                 d->m_rightSummaryLabel->setText(i18n("Investment value: %1%2",
0461                                                      d->m_balanceIsApproximated ? "~" : "",
0462                                                      d->m_totalBalance.formatMoney(MyMoneyFile::instance()->baseCurrency().tradingSymbol(), d->m_precision)));
0463             }
0464         }
0465     }
0466 }
0467 
0468 void KGlobalLedgerView::resizeEvent(QResizeEvent* ev)
0469 {
0470     if (MyMoneyFile::instance()->storageAttached()) {
0471         Q_D(KGlobalLedgerView);
0472         if (d->m_needLoad)
0473             d->init();
0474 
0475         d->m_register->resize((int)eWidgets::eTransaction::Column::Detail);
0476         d->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1);
0477     }
0478     KMyMoneyViewBase::resizeEvent(ev);
0479 }
0480 
0481 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance)
0482 {
0483     Q_D(KGlobalLedgerView);
0484     if(d->m_needLoad)
0485         d->init();
0486 
0487     if (d->m_reconciliationAccount.id() != acc.id()) {
0488         // make sure the account is selected
0489         if (!acc.id().isEmpty())
0490             slotSelectAccount(acc.id());
0491 
0492         d->m_reconciliationAccount = acc;
0493         d->m_reconciliationDate = reconciliationDate;
0494         d->m_endingBalance = endingBalance;
0495         if (acc.accountGroup() == eMyMoney::Account::Type::Liability)
0496             d->m_endingBalance = -endingBalance;
0497 
0498         d->m_newAccountLoaded = true;
0499 
0500         if (acc.id().isEmpty()) {
0501             d->m_buttonbar->removeAction(pActions[Action::PostponeReconciliation]);
0502             d->m_buttonbar->removeAction(pActions[Action::FinishReconciliation]);
0503         } else {
0504             d->m_buttonbar->addAction(pActions[Action::PostponeReconciliation]);
0505             d->m_buttonbar->addAction(pActions[Action::FinishReconciliation]);
0506             // when we start reconciliation, we need to reload the view
0507             // because no data has been changed. When postponing or finishing
0508             // reconciliation, the data change in the engine takes care of updating
0509             // the view.
0510             refresh();
0511         }
0512     }
0513 }
0514 
0515 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate)
0516 {
0517     slotSetReconcileAccount(acc, reconciliationDate, MyMoneyMoney());
0518 }
0519 
0520 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc)
0521 {
0522     slotSetReconcileAccount(acc, QDate(), MyMoneyMoney());
0523 }
0524 
0525 void KGlobalLedgerView::slotSetReconcileAccount()
0526 {
0527     slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
0528 }
0529 
0530 void KGlobalLedgerView::slotShowTransactionMenu(const MyMoneySplit& sp)
0531 {
0532     Q_UNUSED(sp)
0533     pMenus[Menu::Transaction]->exec(QCursor::pos());
0534 }
0535 
0536 void KGlobalLedgerView::slotContinueReconciliation()
0537 {
0538     Q_D(KGlobalLedgerView);
0539     const auto file = MyMoneyFile::instance();
0540     MyMoneyAccount account;
0541 
0542     try {
0543         account = file->account(d->m_currentAccount.id());
0544         // get rid of previous run.
0545         delete d->m_endingBalanceDlg;
0546         d->m_endingBalanceDlg = new KEndingBalanceDlg(account, this);
0547         if (account.isAssetLiability()) {
0548 
0549             if (d->m_endingBalanceDlg->exec() == QDialog::Accepted) {
0550                 if (KMyMoneySettings::autoReconciliation()) {
0551                     MyMoneyMoney startBalance = d->m_endingBalanceDlg->previousBalance();
0552                     MyMoneyMoney endBalance = d->m_endingBalanceDlg->endingBalance();
0553                     QDate endDate = d->m_endingBalanceDlg->statementDate();
0554 
0555                     QList<QPair<MyMoneyTransaction, MyMoneySplit> > transactionList;
0556                     MyMoneyTransactionFilter filter(account.id());
0557                     filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
0558                     filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
0559                     filter.setDateFilter(QDate(), endDate);
0560                     filter.setConsiderCategory(false);
0561                     filter.setReportAllSplits(true);
0562                     file->transactionList(transactionList, filter);
0563                     QList<QPair<MyMoneyTransaction, MyMoneySplit> > result = d->automaticReconciliation(account, transactionList, endBalance - startBalance);
0564 
0565                     if (!result.empty()) {
0566                         QString message = i18n("KMyMoney has detected transactions matching your reconciliation data.\nWould you like KMyMoney to clear these transactions for you?");
0567                         if (KMessageBox::questionYesNo(this,
0568                                                        message,
0569                                                        i18n("Automatic reconciliation"),
0570                                                        KStandardGuiItem::yes(),
0571                                                        KStandardGuiItem::no(),
0572                                                        "AcceptAutomaticReconciliation") == KMessageBox::Yes) {
0573                             // mark the transactions cleared
0574                             KMyMoneyRegister::SelectedTransactions oldSelection = d->m_selectedTransactions;
0575                             d->m_selectedTransactions.clear();
0576                             QListIterator<QPair<MyMoneyTransaction, MyMoneySplit> > itTransactionSplitResult(result);
0577                             while (itTransactionSplitResult.hasNext()) {
0578                                 const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = itTransactionSplitResult.next();
0579                                 d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second, QString()));
0580                             }
0581                             // mark all transactions in d->m_selectedTransactions as 'Cleared'
0582                             d->markTransaction(eMyMoney::Split::State::Cleared);
0583                             d->m_selectedTransactions = oldSelection;
0584                         }
0585                     }
0586                 }
0587 
0588                 if (!file->isStandardAccount(account.id()) &&
0589                         account.isAssetLiability()) {
0590                     if (!isVisible())
0591                         emit customActionRequested(View::Ledgers, eView::Action::SwitchView);
0592                     Models::instance()->accountsModel()->slotReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
0593                     slotSetReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
0594 
0595                     // check if the user requests us to create interest
0596                     // or charge transactions.
0597                     auto ti = d->m_endingBalanceDlg->interestTransaction();
0598                     auto tc = d->m_endingBalanceDlg->chargeTransaction();
0599                     MyMoneyFileTransaction ft;
0600                     try {
0601                         if (ti != MyMoneyTransaction()) {
0602                             MyMoneyFile::instance()->addTransaction(ti);
0603                         }
0604                         if (tc != MyMoneyTransaction()) {
0605                             MyMoneyFile::instance()->addTransaction(tc);
0606                         }
0607                         ft.commit();
0608 
0609                     } catch (const MyMoneyException &e) {
0610                         qWarning("interest transaction not stored: '%s'", e.what());
0611                     }
0612 
0613                     // reload the account object as it might have changed in the meantime
0614                     d->m_reconciliationAccount = file->account(account.id());
0615                     updateActions(d->m_currentAccount);
0616                     updateLedgerActionsInternal();
0617                     //            slotUpdateActions();
0618                 }
0619             }
0620         }
0621     } catch (const MyMoneyException &) {
0622     }
0623 }
0624 
0625 void KGlobalLedgerView::slotLedgerSelected(const QString& _accId, const QString& transaction)
0626 {
0627     auto acc = MyMoneyFile::instance()->account(_accId);
0628     QString accId(_accId);
0629 
0630     switch (acc.accountType()) {
0631     case Account::Type::Stock:
0632         // if a stock account is selected, we show the
0633         // the corresponding parent (investment) account
0634         acc = MyMoneyFile::instance()->account(acc.parentAccountId());
0635         accId = acc.id();
0636     // intentional fall through
0637 
0638     case Account::Type::Checkings:
0639     case Account::Type::Savings:
0640     case Account::Type::Cash:
0641     case Account::Type::CreditCard:
0642     case Account::Type::Loan:
0643     case Account::Type::Asset:
0644     case Account::Type::Liability:
0645     case Account::Type::AssetLoan:
0646     case Account::Type::Income:
0647     case Account::Type::Expense:
0648     case Account::Type::Investment:
0649     case Account::Type::Equity:
0650         if (!isVisible())
0651             emit customActionRequested(View::Ledgers, eView::Action::SwitchView);
0652         slotSelectAccount(accId, transaction);
0653         break;
0654 
0655     case Account::Type::CertificateDep:
0656     case Account::Type::MoneyMarket:
0657     case Account::Type::Currency:
0658         qDebug("No ledger view available for account type %d", (int)acc.accountType());
0659         break;
0660 
0661     default:
0662         qDebug("Unknown account type %d in KMyMoneyView::slotLedgerSelected", (int)acc.accountType());
0663         break;
0664     }
0665 }
0666 
0667 void KGlobalLedgerView::slotSelectByObject(const MyMoneyObject& obj, eView::Intent intent)
0668 {
0669     switch(intent) {
0670     case eView::Intent::UpdateActions:
0671         updateActions(obj);
0672         break;
0673 
0674     case eView::Intent::FinishEnteringOverdueScheduledTransactions:
0675         slotContinueReconciliation();
0676         break;
0677 
0678     case eView::Intent::SynchronizeAccountInLedgersView:
0679         slotSelectAccount(obj);
0680         break;
0681 
0682     default:
0683         break;
0684     }
0685 }
0686 
0687 void KGlobalLedgerView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent)
0688 {
0689     switch(intent) {
0690     case eView::Intent::ShowTransaction:
0691         if (variant.count() == 2)
0692             slotLedgerSelected(variant.at(0).toString(), variant.at(1).toString());
0693         break;
0694     case eView::Intent::SelectRegisterTransactions:
0695         if (variant.count() == 1)
0696             updateLedgerActions(variant.at(0).value<KMyMoneyRegister::SelectedTransactions>());
0697         break;
0698     default:
0699         break;
0700     }
0701 }
0702 
0703 void KGlobalLedgerView::slotSelectAccount(const MyMoneyObject& obj)
0704 {
0705     Q_D(KGlobalLedgerView);
0706     if (typeid(obj) != typeid(MyMoneyAccount))
0707         return/* false */;
0708 
0709     d->m_lastSelectedAccountID = obj.id();
0710 }
0711 
0712 void KGlobalLedgerView::slotSelectAccount(const QString& id)
0713 {
0714     slotSelectAccount(id, QString());
0715 }
0716 
0717 bool KGlobalLedgerView::slotSelectAccount(const QString& id, const QString& transactionId)
0718 {
0719     Q_D(KGlobalLedgerView);
0720     auto rc = true;
0721 
0722     if (!id.isEmpty()) {
0723         if (d->m_currentAccount.id() != id) {
0724             try {
0725                 d->m_currentAccount = MyMoneyFile::instance()->account(id);
0726                 // if a stock account is selected, we show the
0727                 // the corresponding parent (investment) account
0728                 if (d->m_currentAccount.isInvest()) {
0729                     d->m_currentAccount = MyMoneyFile::instance()->account(d->m_currentAccount.parentAccountId());
0730                 }
0731                 d->m_lastSelectedAccountID = d->m_currentAccount.id();
0732                 d->m_newAccountLoaded = true;
0733                 refresh();
0734             } catch (const MyMoneyException &) {
0735                 qDebug("Unable to retrieve account %s", qPrintable(id));
0736                 rc = false;
0737             }
0738         } else {
0739             // we need to refresh m_account.m_accountList, a child could have been deleted
0740             d->m_currentAccount = MyMoneyFile::instance()->account(id);
0741 
0742             emit selectByObject(d->m_currentAccount, eView::Intent::None);
0743             emit selectByObject(d->m_currentAccount, eView::Intent::SynchronizeAccountInInvestmentView);
0744         }
0745         d->selectTransaction(transactionId);
0746     }
0747     return rc;
0748 }
0749 
0750 bool KGlobalLedgerView::selectEmptyTransaction()
0751 {
0752     Q_D(KGlobalLedgerView);
0753     bool rc = false;
0754 
0755     if (!d->m_inEditMode) {
0756         // in case we don't know the type of transaction to be created,
0757         // have at least one selected transaction and the id of
0758         // this transaction is not empty, we take it as template for the
0759         // transaction to be created
0760         KMyMoneyRegister::SelectedTransactions list(d->m_register);
0761         if ((d->m_action == eWidgets::eRegister::Action::None) && (!list.isEmpty()) && (!list[0].transaction().id().isEmpty())) {
0762             // the new transaction to be created will have the same type
0763             // as the one that currently has the focus
0764             KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(d->m_register->focusItem());
0765             if (t)
0766                 d->m_action = t->actionType();
0767             d->m_register->clearSelection();
0768         }
0769 
0770         // if we still don't have an idea which type of transaction
0771         // to create, we use the default.
0772         if (d->m_action == eWidgets::eRegister::Action::None) {
0773             d->setupDefaultAction();
0774         }
0775 
0776         d->m_register->selectItem(d->m_register->lastItem());
0777         d->m_register->updateRegister();
0778         rc = true;
0779     }
0780     return rc;
0781 }
0782 
0783 TransactionEditor* KGlobalLedgerView::startEdit(const KMyMoneyRegister::SelectedTransactions& list)
0784 {
0785     Q_D(KGlobalLedgerView);
0786     // we use the warnlevel to keep track, if we have to warn the
0787     // user that some or all splits have been reconciled or if the
0788     // user cannot modify the transaction if at least one split
0789     // has the status frozen. The following value are used:
0790     //
0791     // 0 - no sweat, user can modify
0792     // 1 - user should be warned that at least one split has been reconciled
0793     //     already
0794     // 2 - editing is not possible, only viewing the transaction is allowed.
0795     //     a tooltip shows that this transaction cannot be changed anymore
0796     // 3 - editing is not possible, only viewing the transaction is allowed.
0797     //     a tooltip shows that this transaction cannot be changed anymore
0798 
0799     int warnLevel = list.warnLevel();
0800     Q_ASSERT(warnLevel < 4); // otherwise the edit action should not be enabled
0801 
0802     if (warnLevel == 1) {
0803         if (KMessageBox::warningContinueCancel(this,
0804                                                i18n(
0805                                                        "At least one split of the selected transactions has been reconciled. "
0806                                                        "Do you wish to continue to edit the transactions anyway?"
0807                                                ),
0808                                                i18n("Transaction already reconciled"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
0809                                                "EditReconciledTransaction") == KMessageBox::Cancel) {
0810             d->m_register->endEdit();
0811             return 0;
0812         }
0813     }
0814 
0815     TransactionEditor* editor = 0;
0816     KMyMoneyRegister::Transaction* item = dynamic_cast<KMyMoneyRegister::Transaction*>(d->m_register->focusItem());
0817 
0818     if (item) {
0819         // in case the current focus item is not selected, we move the focus to the first selected transaction
0820         if (!item->isSelected()) {
0821             KMyMoneyRegister::RegisterItem* p;
0822             for (p = d->m_register->firstItem(); p; p = p->nextItem()) {
0823                 KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
0824                 if (t && t->isSelected()) {
0825                     d->m_register->setFocusItem(t);
0826                     item = t;
0827                     break;
0828                 }
0829             }
0830         }
0831 
0832         // decide, if we edit in the register or in the form
0833         TransactionEditorContainer* parent;
0834         if (d->m_formFrame->isVisible())
0835             parent = d->m_form;
0836         else {
0837             parent = d->m_register;
0838         }
0839 
0840         editor = item->createEditor(parent, list, KGlobalLedgerViewPrivate::m_lastPostDate);
0841 
0842         // check that we use the same transaction commodity in all selected transactions
0843         // if not, we need to update this in the editor's list. The user can also bail out
0844         // of this operation which means that we have to stop editing here.
0845         if (editor) {
0846             if (!editor->fixTransactionCommodity(d->m_currentAccount)) {
0847                 // if the user wants to quit, we need to destroy the editor
0848                 // and bail out
0849                 delete editor;
0850                 editor = 0;
0851             }
0852         }
0853 
0854         if (editor) {
0855             if (parent == d->m_register) {
0856                 // make sure, the height of the table is correct
0857                 d->m_register->updateRegister(KMyMoneySettings::ledgerLens() | !KMyMoneySettings::transactionForm());
0858             }
0859 
0860             /// possibly switch editor to read-only mode
0861             editor->setReadOnlyMode(warnLevel > 1);
0862 
0863             d->m_inEditMode = true;
0864             connect(editor, &TransactionEditor::transactionDataSufficient, pActions[Action::EnterTransaction], &QAction::setEnabled);
0865             connect(editor, &TransactionEditor::returnPressed, pActions[Action::EnterTransaction], &QAction::trigger);
0866             connect(editor, &TransactionEditor::escapePressed, pActions[Action::CancelTransaction], &QAction::trigger);
0867 
0868             connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets);
0869             connect(editor, &TransactionEditor::finishEdit, this, &KGlobalLedgerView::slotLeaveEditMode);
0870             connect(editor, &TransactionEditor::objectCreation, d->m_mousePressFilter, &MousePressFilter::setFilterDeactive);
0871             connect(editor, &TransactionEditor::lastPostDateUsed, this, &KGlobalLedgerView::slotKeepPostDate);
0872 
0873             // create the widgets, place them in the parent and load them with data
0874             // setup tab order
0875             d->m_tabOrderWidgets.clear();
0876             editor->setup(d->m_tabOrderWidgets, d->m_currentAccount, d->m_action);
0877 
0878             Q_ASSERT(!d->m_tabOrderWidgets.isEmpty());
0879 
0880             // install event filter in all taborder widgets
0881             QWidgetList::const_iterator it_w = d->m_tabOrderWidgets.constBegin();
0882             for (; it_w != d->m_tabOrderWidgets.constEnd(); ++it_w) {
0883                 (*it_w)->installEventFilter(this);
0884             }
0885             // Install a filter that checks if a mouse press happened outside
0886             // of one of our own widgets.
0887             qApp->installEventFilter(d->m_mousePressFilter);
0888 
0889             // Check if the editor has some preference on where to set the focus
0890             // If not, set the focus to the first widget in the tab order
0891             QWidget* focusWidget = editor->firstWidget();
0892             if (!focusWidget)
0893                 focusWidget = d->m_tabOrderWidgets.first();
0894 
0895             // for some reason, this only works reliably if delayed a bit
0896             QTimer::singleShot(10, focusWidget, SLOT(setFocus()));
0897 
0898             // preset to 'I have no idea which type to create' for the next round.
0899             d->m_action = eWidgets::eRegister::Action::None;
0900         }
0901     }
0902     return editor;
0903 }
0904 
0905 void KGlobalLedgerView::slotTransactionsContextMenuRequested()
0906 {
0907     Q_D(KGlobalLedgerView);
0908     auto transactions = d->m_selectedTransactions;
0909     updateLedgerActionsInternal();
0910 //  emit transactionsSelected(d->m_selectedTransactions); // that should select MyMoneySchedule in KScheduledView
0911     if (!transactions.isEmpty() && transactions.first().isScheduled())
0912         emit selectByObject(MyMoneyFile::instance()->schedule(transactions.first().scheduleId()), eView::Intent::OpenContextMenu);
0913     else
0914         slotShowTransactionMenu(MyMoneySplit());
0915 }
0916 
0917 void KGlobalLedgerView::slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list)
0918 {
0919     Q_D(KGlobalLedgerView);
0920     d->m_inEditMode = false;
0921     qApp->removeEventFilter(d->m_mousePressFilter);
0922 
0923     // a possible focusOut event may have removed the focus, so we
0924     // install it back again.
0925     d->m_register->focusItem()->setFocus(true);
0926 
0927     // if we come back from editing a new item, we make sure that
0928     // we always select the very last known transaction entry no
0929     // matter if the transaction has been created or not.
0930 
0931     if (list.count() && list[0].transaction().id().isEmpty()) {
0932         // block signals to prevent some infinite loops that might occur here.
0933         d->m_register->blockSignals(true);
0934         d->m_register->clearSelection();
0935         KMyMoneyRegister::RegisterItem* p = d->m_register->lastItem();
0936         if (p && p->prevItem())
0937             p = p->prevItem();
0938         d->m_register->selectItem(p);
0939         d->m_register->updateRegister(true);
0940         d->m_register->blockSignals(false);
0941         // we need to update the form manually as sending signals was blocked
0942         KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
0943         if (t)
0944             d->m_form->slotSetTransaction(t);
0945     } else {
0946         if (!KMyMoneySettings::transactionForm()) {
0947             // update the row height of the transactions because it might differ between viewing/editing mode when not using the transaction form
0948             d->m_register->blockSignals(true);
0949             d->m_register->updateRegister(true);
0950             d->m_register->blockSignals(false);
0951         }
0952     }
0953     d->m_needsRefresh = true; // TODO: Why transaction in view doesn't update without this?
0954     if (d->m_needsRefresh)
0955         refresh();
0956 
0957     d->m_register->endEdit();
0958     d->m_register->setFocus();
0959 }
0960 
0961 bool KGlobalLedgerView::focusNextPrevChild(bool next)
0962 {
0963     Q_D(KGlobalLedgerView);
0964     bool  rc = false;
0965     // qDebug() << "----------------------------------------------------------";
0966     // qDebug() << "KGlobalLedgerView::focusNextPrevChild, editmode=" << d->m_inEditMode;
0967     if (d->m_inEditMode) {
0968         QWidget *w = 0;
0969 
0970         w = qApp->focusWidget();
0971         int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
0972         const auto startIndex = currentWidgetIndex;
0973         // qDebug() << "Focus is at currentWidgetIndex" <<  currentWidgetIndex << w->objectName();
0974         do {
0975             while (w && currentWidgetIndex == -1) {
0976                 // qDebug() << w->objectName() << "not in list, use parent";
0977                 w = w->parentWidget();
0978                 currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
0979             }
0980             // qDebug() << "Focus is at currentWidgetIndex" <<  currentWidgetIndex << w->objectName();
0981 
0982             if (currentWidgetIndex != -1) {
0983                 // if(w) qDebug() << "tab order is at" << w->objectName();
0984                 currentWidgetIndex += next ? 1 : -1;
0985                 if (currentWidgetIndex < 0)
0986                     currentWidgetIndex = d->m_tabOrderWidgets.size() - 1;
0987                 else if (currentWidgetIndex >= d->m_tabOrderWidgets.size())
0988                     currentWidgetIndex = 0;
0989 
0990                 w = d->m_tabOrderWidgets[currentWidgetIndex];
0991                 // qDebug() << "currentWidgetIndex" <<  currentWidgetIndex << w->objectName() << w->isVisible();
0992 
0993                 if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) {
0994                     // qDebug() << "Set focus to" << w->objectName();
0995                     w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason);
0996                     rc = true;
0997                     break;
0998                 }
0999             } else {
1000                 break;
1001             }
1002         } while(currentWidgetIndex != startIndex);
1003     } else
1004         rc = KMyMoneyViewBase::focusNextPrevChild(next);
1005     return rc;
1006 }
1007 
1008 bool KGlobalLedgerView::eventFilter(QObject* o, QEvent* e)
1009 {
1010     Q_D(KGlobalLedgerView);
1011     bool rc = false;
1012     //  Need to capture mouse position here as QEvent::ToolTip is too slow
1013     d->m_tooltipPosn = QCursor::pos();
1014 
1015     if (e->type() == QEvent::KeyPress) {
1016         if (d->m_inEditMode) {
1017             // qDebug("object = %s, key = %d", o->className(), k->key());
1018             if (o == d->m_register) {
1019                 // we hide all key press events from the register
1020                 // while editing a transaction
1021                 rc = true;
1022             }
1023         }
1024     }
1025 
1026     if (!rc)
1027         rc = KMyMoneyViewBase::eventFilter(o, e);
1028 
1029     return rc;
1030 }
1031 
1032 void KGlobalLedgerView::slotSortOptions()
1033 {
1034     Q_D(KGlobalLedgerView);
1035     QPointer<KSortOptionDlg> dlg = new KSortOptionDlg(this);
1036 
1037     QString key;
1038     QString sortOrder, def;
1039     if (d->isReconciliationAccount()) {
1040         key = "kmm-sort-reconcile";
1041         def = KMyMoneySettings::sortReconcileView();
1042     } else {
1043         key = "kmm-sort-std";
1044         def = KMyMoneySettings::sortNormalView();
1045     }
1046 
1047     // check if we have an account override of the sort order
1048     if (!d->m_currentAccount.value(key).isEmpty())
1049         sortOrder = d->m_currentAccount.value(key);
1050 
1051     QString oldOrder = sortOrder;
1052 
1053     dlg->setSortOption(sortOrder, def);
1054 
1055     if (dlg->exec() == QDialog::Accepted) {
1056         if (dlg != 0) {
1057             sortOrder = dlg->sortOption();
1058             if (sortOrder != oldOrder) {
1059                 if (sortOrder.isEmpty()) {
1060                     d->m_currentAccount.deletePair(key);
1061                 } else {
1062                     d->m_currentAccount.setValue(key, sortOrder);
1063                 }
1064                 MyMoneyFileTransaction ft;
1065                 try {
1066                     MyMoneyFile::instance()->modifyAccount(d->m_currentAccount);
1067                     ft.commit();
1068                 } catch (const MyMoneyException &e) {
1069                     qDebug("Unable to update sort order for account '%s': %s", qPrintable(d->m_currentAccount.name()), e.what());
1070                 }
1071             }
1072         }
1073     }
1074     delete dlg;
1075 }
1076 
1077 void KGlobalLedgerView::slotToggleTransactionMark(KMyMoneyRegister::Transaction* /* t */)
1078 {
1079     Q_D(KGlobalLedgerView);
1080     if (!d->m_inEditMode) {
1081         slotToggleReconciliationFlag();
1082     }
1083 }
1084 
1085 void KGlobalLedgerView::slotKeepPostDate(const QDate& date)
1086 {
1087     KGlobalLedgerViewPrivate::m_lastPostDate = date;
1088 }
1089 
1090 QString KGlobalLedgerView::accountId() const
1091 {
1092     Q_D(const KGlobalLedgerView);
1093     return d->m_currentAccount.id();
1094 }
1095 
1096 bool KGlobalLedgerView::canCreateTransactions(QString& tooltip) const
1097 {
1098     Q_D(const KGlobalLedgerView);
1099     bool rc = true;
1100 
1101     if (d->m_currentAccount.id().isEmpty()) {
1102         tooltip = i18n("Cannot create transactions when no account is selected.");
1103         rc = false;
1104     }
1105     if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income
1106             || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) {
1107         tooltip = i18n("Cannot create transactions in the context of a category.");
1108         d->showTooltip(tooltip);
1109         rc = false;
1110     }
1111     if (d->m_currentAccount.isClosed()) {
1112         tooltip = i18n("Cannot create transactions in a closed account.");
1113         d->showTooltip(tooltip);
1114         rc = false;
1115     }
1116     return rc;
1117 }
1118 
1119 bool KGlobalLedgerView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1120 {
1121     Q_D(const KGlobalLedgerView);
1122     return d->canProcessTransactions(list, tooltip) && list.canModify();
1123 }
1124 
1125 bool KGlobalLedgerView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1126 {
1127     Q_D(const KGlobalLedgerView);
1128     return d->canProcessTransactions(list, tooltip) && list.canDuplicate();
1129 }
1130 
1131 bool KGlobalLedgerView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1132 {
1133     Q_D(const KGlobalLedgerView);
1134     // check if we can edit the list of transactions. We can edit, if
1135     //
1136     //   a) no mix of standard and investment transactions exist
1137     //   b) if a split transaction is selected, this is the only selection
1138     //   c) the transaction having the current focus is selected
1139 
1140     // check for c)
1141     if (!d->canProcessTransactions(list, tooltip))
1142         return false;
1143 
1144     bool rc = true;
1145     int investmentTransactions = 0;
1146     int normalTransactions = 0;
1147 
1148     if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income
1149             || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) {
1150         tooltip = i18n("Cannot edit transactions in the context of a category.");
1151         d->showTooltip(tooltip);
1152         rc = false;
1153     }
1154 
1155     // check for a)
1156     KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
1157     QString action;
1158     for (it_t = list.begin(); rc && it_t != list.end(); ++it_t) {
1159         if ((*it_t).transaction().id().isEmpty()) {
1160             tooltip.clear();
1161             rc = false;
1162             continue;
1163         }
1164 
1165         if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) {
1166             if (action.isEmpty()) {
1167                 action = (*it_t).split().action();
1168                 continue;
1169             }
1170             if (action == (*it_t).split().action()) {
1171                 continue;
1172             } else {
1173                 tooltip = (i18n("Cannot edit mixed investment action/type transactions together."));
1174                 d->showTooltip(tooltip);
1175                 rc = false;
1176                 break;
1177             }
1178         }
1179 
1180         if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction)
1181             ++investmentTransactions;
1182         else
1183             ++normalTransactions;
1184 
1185         // check for a)
1186         if (investmentTransactions != 0 && normalTransactions != 0) {
1187             tooltip = i18n("Cannot edit investment transactions and non-investment transactions together.");
1188             d->showTooltip(tooltip);
1189             rc = false;
1190             break;
1191         }
1192 
1193         // check for b) but only for normalTransactions
1194         if ((*it_t).transaction().splitCount() > 2 && normalTransactions != 0) {
1195             if (list.count() > 1) {
1196                 tooltip = i18n("Cannot edit multiple split transactions at once.");
1197                 d->showTooltip(tooltip);
1198                 rc = false;
1199                 break;
1200             }
1201         }
1202     }
1203 
1204     // check for multiple transactions being selected in an investment account
1205     // we do not allow editing in this case: https://bugs.kde.org/show_bug.cgi?id=240816
1206     // later on, we might allow to edit investment transactions of the same type
1207     ///  Can now disable the following check.
1208 
1209     /*  if (rc == true && investmentTransactions > 1) {
1210         tooltip = i18n("Cannot edit multiple investment transactions at once");
1211         rc = false;
1212       }*/
1213 
1214     // now check that we have the correct account type for investment transactions
1215     if (rc == true && investmentTransactions != 0) {
1216         if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
1217             tooltip = i18n("Cannot edit investment transactions in the context of this account.");
1218             rc = false;
1219         }
1220     }
1221     return rc;
1222 }
1223 
1224 void KGlobalLedgerView::slotMoveToAccount(const QString& id)
1225 {
1226     Q_D(KGlobalLedgerView);
1227     // close the menu, if it is still open
1228     if (pMenus[Menu::Transaction]->isVisible())
1229         pMenus[Menu::Transaction]->close();
1230 
1231     if (!d->m_selectedTransactions.isEmpty()) {
1232         const auto file = MyMoneyFile::instance();
1233         MyMoneyFileTransaction ft;
1234         try {
1235             foreach (const auto selection, d->m_selectedTransactions) {
1236                 if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) {
1237                     d->moveInvestmentTransaction(d->m_currentAccount.id(), id, selection.transaction());
1238                 } else {
1239                     // we get the data afresh from the engine as
1240                     // it might have changed by a previous iteration
1241                     // in this loop. Use case: two splits point to
1242                     // the same account and both are selected.
1243                     auto tid = selection.transaction().id();
1244                     auto sid = selection.split().id();
1245                     auto t = file->transaction(tid);
1246                     auto s = t.splitById(sid);
1247                     s.setAccountId(id);
1248                     t.modifySplit(s);
1249                     file->modifyTransaction(t);
1250                 }
1251             }
1252             ft.commit();
1253         } catch (const MyMoneyException &) {
1254         }
1255     }
1256 }
1257 
1258 void KGlobalLedgerView::slotUpdateMoveToAccountMenu()
1259 {
1260     Q_D(KGlobalLedgerView);
1261     d->createTransactionMoveMenu();
1262 
1263     // in case we were not able to create the selector, we
1264     // better get out of here. Anything else would cause
1265     // a crash later on (accountSet.load)
1266     if (!d->m_moveToAccountSelector)
1267         return;
1268 
1269     if (!d->m_currentAccount.id().isEmpty()) {
1270         AccountSet accountSet;
1271         if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) {
1272             accountSet.addAccountType(eMyMoney::Account::Type::Investment);
1273         } else if (d->m_currentAccount.isAssetLiability()) {
1274 
1275             accountSet.addAccountType(eMyMoney::Account::Type::Checkings);
1276             accountSet.addAccountType(eMyMoney::Account::Type::Savings);
1277             accountSet.addAccountType(eMyMoney::Account::Type::Cash);
1278             accountSet.addAccountType(eMyMoney::Account::Type::AssetLoan);
1279             accountSet.addAccountType(eMyMoney::Account::Type::CertificateDep);
1280             accountSet.addAccountType(eMyMoney::Account::Type::MoneyMarket);
1281             accountSet.addAccountType(eMyMoney::Account::Type::Asset);
1282             accountSet.addAccountType(eMyMoney::Account::Type::Currency);
1283             accountSet.addAccountType(eMyMoney::Account::Type::CreditCard);
1284             accountSet.addAccountType(eMyMoney::Account::Type::Loan);
1285             accountSet.addAccountType(eMyMoney::Account::Type::Liability);
1286         } else if (d->m_currentAccount.isIncomeExpense()) {
1287             accountSet.addAccountType(eMyMoney::Account::Type::Income);
1288             accountSet.addAccountType(eMyMoney::Account::Type::Expense);
1289         }
1290 
1291         accountSet.load(d->m_moveToAccountSelector);
1292         // remove those accounts that we currently reference
1293         // with the selected items
1294         foreach (const auto selection, d->m_selectedTransactions) {
1295             d->m_moveToAccountSelector->removeItem(selection.split().accountId());
1296         }
1297         // remove those accounts from the list that are denominated
1298         // in a different currency
1299         auto list = d->m_moveToAccountSelector->accountList();
1300         QList<QString>::const_iterator it_a;
1301         for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) {
1302             auto acc = MyMoneyFile::instance()->account(*it_a);
1303             if (acc.currencyId() != d->m_currentAccount.currencyId())
1304                 d->m_moveToAccountSelector->removeItem((*it_a));
1305         }
1306     }
1307 }
1308 
1309 void KGlobalLedgerView::slotObjectDestroyed(QObject* o)
1310 {
1311     Q_D(KGlobalLedgerView);
1312     if (o == d->m_moveToAccountSelector) {
1313         d->m_moveToAccountSelector = nullptr;
1314     }
1315 }
1316 
1317 void KGlobalLedgerView::slotCancelOrEnterTransactions(bool& okToSelect)
1318 {
1319     Q_D(KGlobalLedgerView);
1320     static bool oneTime = false;
1321     if (!oneTime) {
1322         oneTime = true;
1323         auto dontShowAgain = "CancelOrEditTransaction";
1324         // qDebug("KMyMoneyApp::slotCancelOrEndEdit");
1325         if (d->m_transactionEditor) {
1326             if (KMyMoneySettings::focusChangeIsEnter() && pActions[Action::EnterTransaction]->isEnabled()) {
1327                 slotEnterTransaction();
1328                 if (d->m_transactionEditor) {
1329                     // if at this stage the editor is still there that means that entering the transaction was cancelled
1330                     // for example by pressing cancel on the exchange rate editor so we must stay in edit mode
1331                     okToSelect = false;
1332                 }
1333             } else {
1334                 // okToSelect is preset to true if a cancel of the dialog is useful and false if it is not
1335                 int rc;
1336                 KGuiItem noGuiItem = KStandardGuiItem::save();
1337                 KGuiItem yesGuiItem = KStandardGuiItem::discard();
1338                 KGuiItem cancelGuiItem = KStandardGuiItem::cont();
1339 
1340                 // if the transaction can't be entered make sure that it can't be entered by pressing no either
1341                 if (!pActions[Action::EnterTransaction]->isEnabled()) {
1342                     noGuiItem.setEnabled(false);
1343                     noGuiItem.setToolTip(pActions[Action::EnterTransaction]->toolTip());
1344                 }
1345 
1346                 // in case we have a new transaction and cannot save it we simply cancel
1347                 if (!pActions[Action::EnterTransaction]->isEnabled() && d->m_transactionEditor && d->m_transactionEditor->createNewTransaction()) {
1348                     rc = KMessageBox::Yes;
1349 
1350                 } else if (okToSelect == true) {
1351                     rc = KMessageBox::warningYesNoCancel(this, i18n("<p>Please select what you want to do: discard the changes, save the changes or continue to edit the transaction.</p><p>You can also set an option to save the transaction automatically when e.g. selecting another transaction.</p>"), i18n("End transaction edit"), yesGuiItem, noGuiItem, cancelGuiItem, dontShowAgain);
1352 
1353                 } else {
1354                     rc = KMessageBox::warningYesNo(this, i18n("<p>Please select what you want to do: discard or save the changes.</p><p>You can also set an option to save the transaction automatically when e.g. selecting another transaction.</p>"), i18n("End transaction edit"), yesGuiItem, noGuiItem, dontShowAgain);
1355                 }
1356 
1357                 switch (rc) {
1358                 case KMessageBox::Yes:
1359                     slotCancelTransaction();
1360                     break;
1361                 case KMessageBox::No:
1362                     slotEnterTransaction();
1363                     // make sure that we'll see this message the next time no matter
1364                     // if the user has chosen the 'Don't show again' checkbox
1365                     KMessageBox::enableMessage(dontShowAgain);
1366                     if (d->m_transactionEditor) {
1367                         // if at this stage the editor is still there that means that entering the transaction was cancelled
1368                         // for example by pressing cancel on the exchange rate editor so we must stay in edit mode
1369                         okToSelect = false;
1370                     }
1371                     break;
1372                 case KMessageBox::Cancel:
1373                     // make sure that we'll see this message the next time no matter
1374                     // if the user has chosen the 'Don't show again' checkbox
1375                     KMessageBox::enableMessage(dontShowAgain);
1376                     okToSelect = false;
1377                     break;
1378                 }
1379             }
1380         }
1381         oneTime = false;
1382     }
1383 }
1384 
1385 void KGlobalLedgerView::slotNewSchedule(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence)
1386 {
1387     KEditScheduleDlg::newSchedule(_t, occurrence);
1388 }
1389 
1390 void KGlobalLedgerView::slotNewTransactionForm(eWidgets::eRegister::Action id)
1391 {
1392     Q_D(KGlobalLedgerView);
1393     if (!d->m_inEditMode) {
1394         d->m_action = id;
1395         // since we jump here via code, we have to make sure to react only
1396         // if the action is enabled
1397         if (pActions[Action::NewTransaction]->isEnabled()) {
1398             if (d->createNewTransaction()) {
1399                 d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1400                 if (d->m_transactionEditor) {
1401                     KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1402                     KMyMoneyPayeeCombo* payeeEdit = dynamic_cast<KMyMoneyPayeeCombo*>(d->m_transactionEditor->haveWidget("payee"));
1403                     if (payeeEdit && !d->m_lastPayeeEnteredId.isEmpty()) {
1404                         // in case we entered a new transaction before and used a payee,
1405                         // we reuse it here. Save the text to the edit widget, select it
1406                         // so that hitting any character will start entering another payee.
1407                         payeeEdit->setSelectedItem(d->m_lastPayeeEnteredId);
1408                         payeeEdit->lineEdit()->selectAll();
1409                     }
1410                     if (d->m_transactionEditor) {
1411                         connect(d->m_transactionEditor.data(), &TransactionEditor::statusProgress, this, &KGlobalLedgerView::slotStatusProgress);
1412                         connect(d->m_transactionEditor.data(), &TransactionEditor::statusMsg, this, &KGlobalLedgerView::slotStatusMsg);
1413                         connect(d->m_transactionEditor.data(), &TransactionEditor::scheduleTransaction, this, &KGlobalLedgerView::slotNewSchedule);
1414                     }
1415                     updateLedgerActionsInternal();
1416 //          emit transactionsSelected(d->m_selectedTransactions);
1417                 }
1418             }
1419         }
1420     }
1421 }
1422 
1423 void KGlobalLedgerView::slotNewTransaction()
1424 {
1425     // in case the view is not visible ...
1426     if (!isVisible()) {
1427         // we switch to it
1428         pActions[Action::ShowLedgersView]->activate(QAction::ActionEvent::Trigger);
1429         QString tooltip;
1430         if (!canCreateTransactions(tooltip)) {
1431             // and inform the user via a dialog about the reason
1432             // why a transaction cannot be created
1433             KMessageBox::sorry(this, tooltip);
1434             return;
1435         }
1436     }
1437     slotNewTransactionForm(eWidgets::eRegister::Action::None);
1438 }
1439 
1440 void KGlobalLedgerView::slotEditTransaction()
1441 {
1442     Q_D(KGlobalLedgerView);
1443     // qDebug("KMyMoneyApp::slotTransactionsEdit()");
1444     // since we jump here via code, we have to make sure to react only
1445     // if the action is enabled
1446     if (pActions[Action::EditTransaction]->isEnabled()) {
1447         // as soon as we edit a transaction, we don't remember the last payee entered
1448         d->m_lastPayeeEnteredId.clear();
1449         d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1450         KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1451         updateLedgerActionsInternal();
1452     }
1453 }
1454 
1455 void KGlobalLedgerView::slotDeleteTransaction()
1456 {
1457     Q_D(KGlobalLedgerView);
1458     // since we may jump here via code, we have to make sure to react only
1459     // if the action is enabled
1460     if (!pActions[Action::DeleteTransaction]->isEnabled())
1461         return;
1462     if (d->m_selectedTransactions.isEmpty())
1463         return;
1464     if (d->m_selectedTransactions.warnLevel() == 1) {
1465         if (KMessageBox::warningContinueCancel(this,
1466                                                i18n("At least one split of the selected transactions has been reconciled. "
1467                                                        "Do you wish to delete the transactions anyway?"),
1468                                                i18n("Transaction already reconciled")) == KMessageBox::Cancel)
1469             return;
1470     }
1471     auto msg =
1472         i18np("Do you really want to delete the selected transaction?",
1473               "Do you really want to delete all %1 selected transactions?",
1474               d->m_selectedTransactions.count());
1475 
1476     if (KMessageBox::questionYesNo(this, msg, i18n("Delete transaction")) == KMessageBox::Yes) {
1477         //KMSTATUS(i18n("Deleting transactions"));
1478         d->doDeleteTransactions();
1479     }
1480 }
1481 
1482 void KGlobalLedgerView::slotDuplicateTransaction(bool reverse)
1483 {
1484     Q_D(KGlobalLedgerView);
1485     // since we may jump here via code, we have to make sure to react only
1486     // if the action is enabled
1487     if (pActions[Action::DuplicateTransaction]->isEnabled()) {
1488         KMyMoneyRegister::SelectedTransactions selectionList = d->m_selectedTransactions;
1489         KMyMoneyRegister::SelectedTransactions::iterator it_t;
1490 
1491         int i = 0;
1492         int cnt = d->m_selectedTransactions.count();
1493         //    KMSTATUS(i18n("Duplicating transactions"));
1494         emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress);
1495         MyMoneyFileTransaction ft;
1496         MyMoneyTransaction lt;
1497         try {
1498             foreach (const auto selection, selectionList) {
1499                 auto t = selection.transaction();
1500                 // wipe out any reconciliation information
1501                 for (auto& split : t.splits()) {
1502                     split.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1503                     split.setReconcileDate(QDate());
1504                     split.setBankID(QString());
1505                     split.removeMatch();
1506                 }
1507                 // clear invalid data
1508                 t.setEntryDate(QDate());
1509                 t.clearId();
1510 
1511                 if (reverse)
1512                     // reverse transaction
1513                     t.reverse();
1514                 else
1515                     // set the post date to today
1516                     t.setPostDate(QDate::currentDate());
1517 
1518                 MyMoneyFile::instance()->addTransaction(t);
1519                 lt = t;
1520                 emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress);
1521             }
1522             ft.commit();
1523 
1524             // select the new transaction in the ledger
1525             if (!d->m_currentAccount.id().isEmpty())
1526                 slotLedgerSelected(d->m_currentAccount.id(), lt.id());
1527         } catch (const MyMoneyException &e) {
1528             KMessageBox::detailedSorry(this, i18n("Unable to duplicate transaction(s)"), QString::fromLatin1(e.what()));
1529         }
1530         // switch off the progress bar
1531         emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress);
1532     }
1533 }
1534 
1535 void KGlobalLedgerView::slotEnterTransaction()
1536 {
1537     Q_D(KGlobalLedgerView);
1538     // since we jump here via code, we have to make sure to react only
1539     // if the action is enabled
1540     if (pActions[Action::EnterTransaction]->isEnabled()) {
1541         // disable the action while we process it to make sure it's processed only once since
1542         // d->m_transactionEditor->enterTransactions(newId) will run QCoreApplication::processEvents
1543         // we could end up here twice which will cause a crash slotUpdateActions() will enable the action again
1544         pActions[Action::EnterTransaction]->setEnabled(false);
1545         if (d->m_transactionEditor) {
1546             QString accountId = d->m_currentAccount.id();
1547             QString newId;
1548             connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage);
1549             if (d->m_transactionEditor->enterTransactions(newId)) {
1550                 KMyMoneyPayeeCombo* payeeEdit = dynamic_cast<KMyMoneyPayeeCombo*>(d->m_transactionEditor->haveWidget("payee"));
1551                 if (payeeEdit && !newId.isEmpty()) {
1552                     d->m_lastPayeeEnteredId = payeeEdit->selectedItem();
1553                 }
1554                 d->deleteTransactionEditor();
1555             }
1556             if (!newId.isEmpty()) {
1557                 slotLedgerSelected(accountId, newId);
1558             }
1559         }
1560         updateLedgerActionsInternal();
1561     }
1562 }
1563 
1564 void KGlobalLedgerView::slotAcceptTransaction()
1565 {
1566     Q_D(KGlobalLedgerView);
1567     KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions;
1568     KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
1569     int cnt = list.count();
1570     int i = 0;
1571     emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress);
1572     MyMoneyFileTransaction ft;
1573     try {
1574         for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) {
1575             // reload transaction in case it got changed during the course of this loop
1576             MyMoneyTransaction t = MyMoneyFile::instance()->transaction((*it_t).transaction().id());
1577             if (t.isImported()) {
1578                 t.setImported(false);
1579                 if (!d->m_currentAccount.id().isEmpty()) {
1580                     foreach (const auto split, t.splits()) {
1581                         if (split.accountId() == d->m_currentAccount.id()) {
1582                             if (split.reconcileFlag() == eMyMoney::Split::State::NotReconciled) {
1583                                 MyMoneySplit s = split;
1584                                 s.setReconcileFlag(eMyMoney::Split::State::Cleared);
1585                                 t.modifySplit(s);
1586                             }
1587                         }
1588                     }
1589                 }
1590                 MyMoneyFile::instance()->modifyTransaction(t);
1591             }
1592             if ((*it_t).split().isMatched()) {
1593                 // reload split in case it got changed during the course of this loop
1594                 MyMoneySplit s = t.splitById((*it_t).split().id());
1595                 TransactionMatcher matcher(d->m_currentAccount);
1596                 matcher.accept(t, s);
1597             }
1598             emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress);
1599         }
1600         emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress);
1601         ft.commit();
1602     } catch (const MyMoneyException &e) {
1603         KMessageBox::detailedSorry(this, i18n("Unable to accept transaction"), QString::fromLatin1(e.what()));
1604     }
1605 }
1606 
1607 void KGlobalLedgerView::slotCancelTransaction()
1608 {
1609     Q_D(KGlobalLedgerView);
1610     // since we jump here via code, we have to make sure to react only
1611     // if the action is enabled
1612     if (pActions[Action::CancelTransaction]->isEnabled()) {
1613         // make sure, we block the enter function
1614         pActions[Action::EnterTransaction]->setEnabled(false);
1615         // qDebug("KMyMoneyApp::slotTransactionsCancel");
1616         d->deleteTransactionEditor();
1617         updateLedgerActions(d->m_selectedTransactions);
1618         emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1619     }
1620 }
1621 
1622 void KGlobalLedgerView::slotEditSplits()
1623 {
1624     Q_D(KGlobalLedgerView);
1625     // since we jump here via code, we have to make sure to react only
1626     // if the action is enabled
1627     if (pActions[Action::EditSplits]->isEnabled()) {
1628         // as soon as we edit a transaction, we don't remember the last payee entered
1629         d->m_lastPayeeEnteredId.clear();
1630         d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1631         updateLedgerActions(d->m_selectedTransactions);
1632         emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1633 
1634         if (d->m_transactionEditor) {
1635             KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1636             if (d->m_transactionEditor->slotEditSplits() == QDialog::Accepted) {
1637                 MyMoneyFileTransaction ft;
1638                 try {
1639                     QString id;
1640                     connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage);
1641                     d->m_transactionEditor->enterTransactions(id);
1642                     ft.commit();
1643                 } catch (const MyMoneyException &e) {
1644                     KMessageBox::detailedSorry(this, i18n("Unable to modify transaction"), QString::fromLatin1(e.what()));
1645                 }
1646             }
1647         }
1648         d->deleteTransactionEditor();
1649         updateLedgerActions(d->m_selectedTransactions);
1650         emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1651     }
1652 }
1653 
1654 void KGlobalLedgerView::slotCopyTransactionToClipboard()
1655 {
1656     Q_D(KGlobalLedgerView);
1657 
1658     // suppress copy transactions if view not visible
1659     // or in edit mode
1660     if (!isVisible() || d->m_inEditMode)
1661         return;
1662 
1663     // format transactions into text
1664     QString txt;
1665     const auto file = MyMoneyFile::instance();
1666     const auto acc = file->account(d->m_lastSelectedAccountID);
1667     const auto currency = file->currency(acc.currencyId());
1668 
1669     foreach (const auto& st, d->m_selectedTransactions) {
1670         if (!txt.isEmpty() || (d->m_selectedTransactions.count() > 1)) {
1671             txt += QStringLiteral("----------------------------\n");
1672         }
1673         try {
1674             const auto& s = st.split();
1675             // Date
1676             txt += i18n("Date: %1", st.transaction().postDate().toString(Qt::DefaultLocaleShortDate));
1677             txt += QStringLiteral("\n");
1678             // Payee
1679             QString payee = i18nc("Name for unknown payee", "Unknown");
1680             if (!s.payeeId().isEmpty()) {
1681                 payee = file->payee(s.payeeId()).name();
1682             }
1683             txt += i18n("Payee: %1", payee);
1684             txt += QStringLiteral("\n");
1685             // Amount
1686             txt += i18n("Amount: %1", s.value().formatMoney(currency.tradingSymbol(),  MyMoneyMoney::denomToPrec(acc.fraction(currency))));
1687             txt += QStringLiteral("\n");
1688             // Memo
1689             txt += i18n("Memo: %1", s.memo());
1690             txt += QStringLiteral("\n");
1691 
1692         } catch (MyMoneyException &) {
1693             qDebug() << "Cannot copy transaction" << st.transaction().id() << "to clipboard";
1694         }
1695     }
1696     if (d->m_selectedTransactions.count() > 1) {
1697         txt += QStringLiteral("----------------------------\n");
1698     }
1699 
1700     if (!txt.isEmpty()) {
1701         QClipboard *clipboard = QGuiApplication::clipboard();
1702         clipboard->setText(txt);
1703     }
1704 }
1705 
1706 void KGlobalLedgerView::slotCopySplits()
1707 {
1708     Q_D(KGlobalLedgerView);
1709     const auto file = MyMoneyFile::instance();
1710 
1711     if (d->m_selectedTransactions.count() >= 2) {
1712         int singleSplitTransactions = 0;
1713         int multipleSplitTransactions = 0;
1714         KMyMoneyRegister::SelectedTransaction selectedSourceTransaction;
1715         foreach (const auto& st, d->m_selectedTransactions) {
1716             switch (st.transaction().splitCount()) {
1717             case 0:
1718                 break;
1719             case 1:
1720                 singleSplitTransactions++;
1721                 break;
1722             default:
1723                 selectedSourceTransaction = st;
1724                 multipleSplitTransactions++;
1725                 break;
1726             }
1727         }
1728         if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
1729             MyMoneyFileTransaction ft;
1730             try {
1731                 const auto& sourceTransaction = selectedSourceTransaction.transaction();
1732                 const auto& sourceSplit = selectedSourceTransaction.split();
1733                 foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
1734                     auto t = st.transaction();
1735 
1736                     // don't process the source transaction
1737                     if (sourceTransaction.id() == t.id()) {
1738                         continue;
1739                     }
1740 
1741                     const auto& baseSplit = st.split();
1742 
1743                     if (t.splitCount() == 1) {
1744                         foreach (const auto& split, sourceTransaction.splits()) {
1745                             // Don't copy the source split, as we already have that
1746                             // as part of the destination transaction
1747                             if (split.id() == sourceSplit.id()) {
1748                                 continue;
1749                             }
1750 
1751                             MyMoneySplit sp(split);
1752                             // clear the ID and reconciliation state
1753                             sp.clearId();
1754                             sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1755                             sp.setReconcileDate(QDate());
1756 
1757                             // in case it is a simple transaction consisting of two splits,
1758                             // we can adjust the share and value part of the second split we
1759                             // just created. We need to keep a possible price in mind in case
1760                             // of different currencies
1761                             if (sourceTransaction.splitCount() == 2) {
1762                                 sp.setValue(-baseSplit.value());
1763                                 sp.setShares(-(baseSplit.shares() * baseSplit.price()));
1764                             }
1765                             t.addSplit(sp);
1766                         }
1767                         // check if we need to add/update a VAT assignment
1768                         file->updateVAT(t);
1769 
1770                         // and store the modified transaction
1771                         file->modifyTransaction(t);
1772                     }
1773                 }
1774                 ft.commit();
1775             } catch (const MyMoneyException &) {
1776                 qDebug() << "transactionCopySplits() failed";
1777             }
1778         }
1779     }
1780 }
1781 
1782 void KGlobalLedgerView::slotGoToPayee()
1783 {
1784     Q_D(KGlobalLedgerView);
1785     if (!d->m_payeeGoto.isEmpty()) {
1786         try {
1787             QString transactionId;
1788             if (d->m_selectedTransactions.count() == 1) {
1789                 transactionId = d->m_selectedTransactions[0].transaction().id();
1790             }
1791             // make sure to pass copies, as d->myMoneyView->slotPayeeSelected() overrides
1792             // d->m_payeeGoto and d->m_currentAccount while calling slotUpdateActions()
1793             QString payeeId = d->m_payeeGoto;
1794             QString accountId = d->m_currentAccount.id();
1795             emit selectByVariant(QVariantList {QVariant(payeeId), QVariant(accountId), QVariant(transactionId)}, eView::Intent::ShowPayee);
1796 //      emit openPayeeRequested(payeeId, accountId, transactionId);
1797         } catch (const MyMoneyException &) {
1798         }
1799     }
1800 }
1801 
1802 void KGlobalLedgerView::slotGoToAccount()
1803 {
1804     Q_D(KGlobalLedgerView);
1805     if (!d->m_accountGoto.isEmpty()) {
1806         try {
1807             QString transactionId;
1808             if (d->m_selectedTransactions.count() == 1) {
1809                 transactionId = d->m_selectedTransactions[0].transaction().id();
1810             }
1811             // make sure to pass a copy, as d->myMoneyView->slotLedgerSelected() overrides
1812             // d->m_accountGoto while calling slotUpdateActions()
1813             slotLedgerSelected(d->m_accountGoto, transactionId);
1814         } catch (const MyMoneyException &) {
1815         }
1816     }
1817 }
1818 
1819 void KGlobalLedgerView::slotMatchTransactions()
1820 {
1821     Q_D(KGlobalLedgerView);
1822     // if the menu action is retrieved it can contain an '&' character for the accelerator causing the comparison to fail if not removed
1823     QString transactionActionText = pActions[Action::MatchTransaction]->text();
1824     transactionActionText.remove('&');
1825     if (transactionActionText == i18nc("Button text for match transaction", "Match"))
1826         d->transactionMatch();
1827     else
1828         d->transactionUnmatch();
1829 }
1830 
1831 void KGlobalLedgerView::slotCombineTransactions()
1832 {
1833     qDebug("slotTransactionCombine() not implemented yet");
1834 }
1835 
1836 void KGlobalLedgerView::slotToggleReconciliationFlag()
1837 {
1838     Q_D(KGlobalLedgerView);
1839     d->markTransaction(eMyMoney::Split::State::Unknown);
1840 }
1841 
1842 void KGlobalLedgerView::slotMarkCleared()
1843 {
1844     Q_D(KGlobalLedgerView);
1845     d->markTransaction(eMyMoney::Split::State::Cleared);
1846 }
1847 
1848 void KGlobalLedgerView::slotMarkReconciled()
1849 {
1850     Q_D(KGlobalLedgerView);
1851     d->markTransaction(eMyMoney::Split::State::Reconciled);
1852 }
1853 
1854 void KGlobalLedgerView::slotMarkNotReconciled()
1855 {
1856     Q_D(KGlobalLedgerView);
1857     d->markTransaction(eMyMoney::Split::State::NotReconciled);
1858 }
1859 
1860 void KGlobalLedgerView::slotSelectAllTransactions()
1861 {
1862     Q_D(KGlobalLedgerView);
1863     if(d->m_needLoad)
1864         d->init();
1865 
1866     d->m_register->clearSelection();
1867     KMyMoneyRegister::RegisterItem* p = d->m_register->firstItem();
1868     while (p) {
1869         KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
1870         if (t) {
1871             if (t->isVisible() && t->isSelectable() && !t->isScheduled() && !t->id().isEmpty()) {
1872                 t->setSelected(true);
1873             }
1874         }
1875         p = p->nextItem();
1876     }
1877     // this is here only to re-paint the items without selecting anything because the data (including the selection) is not really held in the model right now
1878     d->m_register->selectAll();
1879 
1880     // inform everyone else about the selected items
1881     KMyMoneyRegister::SelectedTransactions list(d->m_register);
1882     updateLedgerActions(list);
1883     emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
1884 }
1885 
1886 void KGlobalLedgerView::slotCreateScheduledTransaction()
1887 {
1888     Q_D(KGlobalLedgerView);
1889     if (d->m_selectedTransactions.count() == 1) {
1890         // make sure to have the current selected split as first split in the schedule
1891         MyMoneyTransaction t = d->m_selectedTransactions[0].transaction();
1892         MyMoneySplit s = d->m_selectedTransactions[0].split();
1893         QString splitId = s.id();
1894         s.clearId();
1895         s.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1896         s.setReconcileDate(QDate());
1897         t.removeSplits();
1898         t.addSplit(s);
1899         foreach (const auto split, d->m_selectedTransactions[0].transaction().splits()) {
1900             if (split.id() != splitId) {
1901                 MyMoneySplit s0 = split;
1902                 s0.clearId();
1903                 s0.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1904                 s0.setReconcileDate(QDate());
1905                 t.addSplit(s0);
1906             }
1907         }
1908         KEditScheduleDlg::newSchedule(t, eMyMoney::Schedule::Occurrence::Monthly);
1909     }
1910 }
1911 
1912 void KGlobalLedgerView::slotAssignNumber()
1913 {
1914     Q_D(KGlobalLedgerView);
1915     if (d->m_transactionEditor)
1916         d->m_transactionEditor->assignNextNumber();
1917 }
1918 
1919 void KGlobalLedgerView::slotStartReconciliation()
1920 {
1921     Q_D(KGlobalLedgerView);
1922 
1923     // we cannot reconcile standard accounts
1924     if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id())) {
1925         emit selectByObject(d->m_currentAccount, eView::Intent::SynchronizeAccountInLedgersView);
1926         emit selectByObject(d->m_currentAccount, eView::Intent::StartEnteringOverdueScheduledTransactions);
1927         // asynchronous call to KScheduledView::slotEnterOverdueSchedules is made here
1928         // after that all activity should be continued in KGlobalLedgerView::slotContinueReconciliation()
1929     }
1930 }
1931 
1932 void KGlobalLedgerView::slotFinishReconciliation()
1933 {
1934     Q_D(KGlobalLedgerView);
1935     const auto file = MyMoneyFile::instance();
1936 
1937     if (!d->m_reconciliationAccount.id().isEmpty()) {
1938         // retrieve list of all transactions that are not reconciled or cleared
1939         QList<QPair<MyMoneyTransaction, MyMoneySplit> > transactionList;
1940         MyMoneyTransactionFilter filter(d->m_reconciliationAccount.id());
1941         filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
1942         filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
1943         filter.setDateFilter(QDate(), d->m_endingBalanceDlg->statementDate());
1944         filter.setConsiderCategory(false);
1945         filter.setReportAllSplits(true);
1946         file->transactionList(transactionList, filter);
1947 
1948         auto balance = MyMoneyFile::instance()->balance(d->m_reconciliationAccount.id(), d->m_endingBalanceDlg->statementDate());
1949         MyMoneyMoney actBalance, clearedBalance;
1950         actBalance = clearedBalance = balance;
1951 
1952         // walk the list of transactions to figure out the balance(s)
1953         for (auto it = transactionList.constBegin(); it != transactionList.constEnd(); ++it) {
1954             if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) {
1955                 clearedBalance -= (*it).second.shares();
1956             }
1957         }
1958 
1959         if (d->m_endingBalanceDlg->endingBalance() != clearedBalance) {
1960             auto message = i18n("You are about to finish the reconciliation of this account with a difference between your bank statement and the transactions marked as cleared.\n"
1961                                 "Are you sure you want to finish the reconciliation?");
1962             if (KMessageBox::questionYesNo(this, message, i18n("Confirm end of reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::No)
1963                 return;
1964         }
1965 
1966         MyMoneyFileTransaction ft;
1967 
1968         // refresh object
1969         d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
1970 
1971         // Turn off reconciliation mode
1972 //    Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
1973 //    slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
1974 //    d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount);
1975 
1976         // only update the last statement balance here, if we haven't a newer one due
1977         // to download of online statements.
1978         if (d->m_reconciliationAccount.value("lastImportedTransactionDate").isEmpty()
1979                 || QDate::fromString(d->m_reconciliationAccount.value("lastImportedTransactionDate"), Qt::ISODate) < d->m_endingBalanceDlg->statementDate()) {
1980             d->m_reconciliationAccount.setValue("lastStatementBalance", d->m_endingBalanceDlg->endingBalance().toString());
1981             // in case we override the last statement balance here, we have to make sure
1982             // that we don't show the online balance anymore, as it might be different
1983             d->m_reconciliationAccount.deletePair("lastImportedTransactionDate");
1984         }
1985         d->m_reconciliationAccount.setLastReconciliationDate(d->m_endingBalanceDlg->statementDate());
1986 
1987         // keep a record of this reconciliation
1988         d->m_reconciliationAccount.addReconciliation(d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
1989 
1990         d->m_reconciliationAccount.deletePair("lastReconciledBalance");
1991         d->m_reconciliationAccount.deletePair("statementBalance");
1992         d->m_reconciliationAccount.deletePair("statementDate");
1993 
1994         try {
1995             // update the account data
1996             file->modifyAccount(d->m_reconciliationAccount);
1997 
1998             /*
1999             // collect the list of cleared splits for this account
2000             filter.clear();
2001             filter.addAccount(d->m_reconciliationAccount.id());
2002             filter.addState(eMyMoney::TransactionFilter::Cleared);
2003             filter.setConsiderCategory(false);
2004             filter.setReportAllSplits(true);
2005             file->transactionList(transactionList, filter);
2006             */
2007 
2008             // walk the list of transactions/splits and mark the cleared ones as reconciled
2009 
2010             for (auto it = transactionList.begin(); it != transactionList.end(); ++it) {
2011                 MyMoneySplit sp = (*it).second;
2012                 // skip the ones that are not marked cleared
2013                 if (sp.reconcileFlag() != eMyMoney::Split::State::Cleared)
2014                     continue;
2015 
2016                 // always retrieve a fresh copy of the transaction because we
2017                 // might have changed it already with another split
2018                 MyMoneyTransaction t = file->transaction((*it).first.id());
2019                 sp.setReconcileFlag(eMyMoney::Split::State::Reconciled);
2020                 sp.setReconcileDate(d->m_endingBalanceDlg->statementDate());
2021                 t.modifySplit(sp);
2022 
2023                 // update the engine ...
2024                 file->modifyTransaction(t);
2025 
2026                 // ... and the list
2027                 (*it) = qMakePair(t, sp);
2028             }
2029             ft.commit();
2030 
2031             // reload account data from engine as the data might have changed in the meantime
2032             d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2033 
2034             /**
2035               * This signal is emitted when an account has been successfully reconciled
2036               * and all transactions are updated in the engine. It can be used by plugins
2037               * to create reconciliation reports.
2038               *
2039               * @param account the account data
2040               * @param date the reconciliation date as provided through the dialog
2041               * @param startingBalance the starting balance as provided through the dialog
2042               * @param endingBalance the ending balance as provided through the dialog
2043               * @param transactionList reference to QList of QPair containing all
2044               *        transaction/split pairs processed by the reconciliation.
2045               */
2046             emit selectByVariant(QVariantList {
2047                 QVariant::fromValue(d->m_reconciliationAccount),
2048                 QVariant::fromValue(d->m_endingBalanceDlg->statementDate()),
2049                 QVariant::fromValue(d->m_endingBalanceDlg->previousBalance()),
2050                 QVariant::fromValue(d->m_endingBalanceDlg->endingBalance()),
2051                 QVariant::fromValue(transactionList)
2052             }, eView::Intent::AccountReconciled);
2053 
2054         } catch (const MyMoneyException &) {
2055             qDebug("Unexpected exception when setting cleared to reconcile");
2056         }
2057         // Turn off reconciliation mode
2058         Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2059         slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2060     }
2061     // Turn off reconciliation mode
2062     d->m_reconciliationAccount = MyMoneyAccount();
2063     updateActions(d->m_currentAccount);
2064     updateLedgerActionsInternal();
2065     d->loadView();
2066 //  slotUpdateActions();
2067 }
2068 
2069 void KGlobalLedgerView::slotPostponeReconciliation()
2070 {
2071     Q_D(KGlobalLedgerView);
2072     MyMoneyFileTransaction ft;
2073     const auto file = MyMoneyFile::instance();
2074 
2075     if (!d->m_reconciliationAccount.id().isEmpty()) {
2076         // refresh object
2077         d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2078 
2079         // Turn off reconciliation mode
2080 //    Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2081 //    slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2082 //    d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount);
2083 
2084         d->m_reconciliationAccount.setValue("lastReconciledBalance", d->m_endingBalanceDlg->previousBalance().toString());
2085         d->m_reconciliationAccount.setValue("statementBalance", d->m_endingBalanceDlg->endingBalance().toString());
2086         d->m_reconciliationAccount.setValue("statementDate", d->m_endingBalanceDlg->statementDate().toString(Qt::ISODate));
2087 
2088         try {
2089             file->modifyAccount(d->m_reconciliationAccount);
2090             ft.commit();
2091             d->m_reconciliationAccount = MyMoneyAccount();
2092             updateActions(d->m_currentAccount);
2093             updateLedgerActionsInternal();
2094 //      slotUpdateActions();
2095         } catch (const MyMoneyException &) {
2096             qDebug("Unexpected exception when setting last reconcile info into account");
2097             ft.rollback();
2098             d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2099         }
2100         // Turn off reconciliation mode
2101         Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2102         slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2103         d->loadView();
2104     }
2105 }
2106 
2107 void KGlobalLedgerView::slotOpenAccount()
2108 {
2109     Q_D(KGlobalLedgerView);
2110     if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id()))
2111         slotLedgerSelected(d->m_currentAccount.id(), QString());
2112 }
2113 
2114 void KGlobalLedgerView::slotFindTransaction()
2115 {
2116     Q_D(KGlobalLedgerView);
2117     if (!d->m_searchDlg) {
2118         d->m_searchDlg = new KFindTransactionDlg(this);
2119         connect(d->m_searchDlg, &QObject::destroyed, this, &KGlobalLedgerView::slotCloseSearchDialog);
2120         connect(d->m_searchDlg, &KFindTransactionDlg::transactionSelected,
2121                 this, &KGlobalLedgerView::slotLedgerSelected);
2122     }
2123     d->m_searchDlg->show();
2124     d->m_searchDlg->raise();
2125     d->m_searchDlg->activateWindow();
2126 }
2127 
2128 void KGlobalLedgerView::slotCloseSearchDialog()
2129 {
2130     Q_D(KGlobalLedgerView);
2131     if (d->m_searchDlg)
2132         d->m_searchDlg->deleteLater();
2133     d->m_searchDlg = nullptr;
2134 }
2135 
2136 void KGlobalLedgerView::slotStatusMsg(const QString& txt)
2137 {
2138     emit selectByVariant(QVariantList {QVariant(txt)}, eView::Intent::ReportProgressMessage);
2139 }
2140 
2141 void KGlobalLedgerView::slotStatusProgress(int cnt, int base)
2142 {
2143     emit selectByVariant(QVariantList {QVariant(cnt), QVariant(base)}, eView::Intent::ReportProgress);
2144 }
2145 
2146 void KGlobalLedgerView::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list)
2147 {
2148     updateLedgerActions(list);
2149     emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
2150 }