1 /***************************************************************************
2                           kgloballedgerview.cpp  -  description
3                              -------------------
4     begin                : Wed Jul 26 2006
5     copyright            : (C) 2006 by Thomas Baumgart
6     email                : Thomas Baumgart <ipwizard@users.sourceforge.net>
7                            (C) 2017 by Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 #include "kgloballedgerview_p.h"
20 
21 #include <typeinfo>
22 
23 // ----------------------------------------------------------------------------
24 // QT Includes
25 
26 #include <QFrame>
27 #include <QList>
28 #include <QLabel>
29 #include <QEvent>
30 #include <QApplication>
31 #include <QTimer>
32 #include <QMenu>
33 #include <QClipboard>
34 
35 // ----------------------------------------------------------------------------
36 // KDE Includes
37 
38 #include <KLocalizedString>
39 #include <KMessageBox>
40 #include <KToolBar>
41 #include <KActionCollection>
42 #include <KXmlGuiWindow>
43 
44 // ----------------------------------------------------------------------------
45 // Project Includes
46 
47 #include "mymoneyaccount.h"
48 #include "mymoneyfile.h"
49 #include "kmymoneyaccountcombo.h"
50 #include "kmymoneypayeecombo.h"
51 #include "keditscheduledlg.h"
52 #include "kendingbalancedlg.h"
53 #include "register.h"
54 #include "transactioneditor.h"
55 #include "selectedtransactions.h"
56 #include "kmymoneysettings.h"
57 #include "registersearchline.h"
58 #include "kfindtransactiondlg.h"
59 #include "accountsmodel.h"
60 #include "models.h"
61 #include "mymoneyschedule.h"
62 #include "mymoneysecurity.h"
63 #include "mymoneytransaction.h"
64 #include "mymoneytransactionfilter.h"
65 #include "mymoneysplit.h"
66 #include "transaction.h"
67 #include "transactionform.h"
68 #include "widgetenums.h"
69 #include "mymoneyenums.h"
70 #include "menuenums.h"
71 
72 using namespace eMenu;
73 
74 QDate KGlobalLedgerViewPrivate::m_lastPostDate;
75 
KGlobalLedgerView(QWidget * parent)76 KGlobalLedgerView::KGlobalLedgerView(QWidget *parent) :
77   KMyMoneyViewBase(*new KGlobalLedgerViewPrivate(this), parent)
78 {
79   const QHash<Action, std::function<void()>> actionConnections {
80     {Action::NewTransaction,            [this](){ KGlobalLedgerView::slotNewTransaction(); }},
81     {Action::EditTransaction,           [this](){ KGlobalLedgerView::slotEditTransaction(); }},
82     {Action::DeleteTransaction,         [this](){ KGlobalLedgerView::slotDeleteTransaction(); }},
83     {Action::DuplicateTransaction,      [this](){ KGlobalLedgerView::slotDuplicateTransaction(); }},
84     {Action::AddReversingTransaction,   [this](){ KGlobalLedgerView::slotDuplicateTransaction(true); }},
85     {Action::EnterTransaction,          [this](){ KGlobalLedgerView::slotEnterTransaction(); }},
86     {Action::AcceptTransaction,         [this](){ KGlobalLedgerView::slotAcceptTransaction(); }},
87     {Action::CancelTransaction,         [this](){ KGlobalLedgerView::slotCancelTransaction(); }},
88     {Action::EditSplits,                [this](){ KGlobalLedgerView::slotEditSplits(); }},
89     {Action::CopySplits,                [this](){ KGlobalLedgerView::slotCopySplits(); }},
90     {Action::GoToPayee,                 [this](){ KGlobalLedgerView::slotGoToPayee(); }},
91     {Action::GoToAccount,               [this](){ KGlobalLedgerView::slotGoToAccount(); }},
92     {Action::MatchTransaction,          [this](){ KGlobalLedgerView::slotMatchTransactions(); }},
93     {Action::CombineTransactions,       [this](){ KGlobalLedgerView::slotCombineTransactions(); }},
94     {Action::ToggleReconciliationFlag,  [this](){ KGlobalLedgerView::slotToggleReconciliationFlag(); }},
95     {Action::MarkCleared,               [this](){ KGlobalLedgerView::slotMarkCleared(); }},
96     {Action::MarkReconciled,            [this](){ KGlobalLedgerView::slotMarkReconciled(); }},
97     {Action::MarkNotReconciled,         [this](){ KGlobalLedgerView::slotMarkNotReconciled(); }},
98     {Action::SelectAllTransactions,     [this](){ KGlobalLedgerView::slotSelectAllTransactions(); }},
99     {Action::NewScheduledTransaction,   [this](){ KGlobalLedgerView::slotCreateScheduledTransaction(); }},
100     {Action::AssignTransactionsNumber,  [this](){ KGlobalLedgerView::slotAssignNumber(); }},
101     {Action::StartReconciliation,       [this](){ KGlobalLedgerView::slotStartReconciliation(); }},
102     {Action::FinishReconciliation,      [this](){ KGlobalLedgerView::slotFinishReconciliation(); }},
103     {Action::PostponeReconciliation,    [this](){ KGlobalLedgerView::slotPostponeReconciliation(); }},
104     {Action::OpenAccount,               [this](){ KGlobalLedgerView::slotOpenAccount(); }},
105     {Action::EditFindTransaction,       [this](){ KGlobalLedgerView::slotFindTransaction(); }},
106   };
107 
108   for (auto a = actionConnections.cbegin(); a != actionConnections.cend(); ++a)
109     connect(pActions[a.key()], &QAction::triggered, this, a.value());
110 
111   KXmlGuiWindow* mw = KMyMoneyUtils::mainWindow();
112   KStandardAction::copy(this, &KGlobalLedgerView::slotCopyTransactionToClipboard,  mw->actionCollection());
113 
114   Q_D(KGlobalLedgerView);
115   d->m_balanceWarning.reset(new KBalanceWarning(this));
116 }
117 
~KGlobalLedgerView()118 KGlobalLedgerView::~KGlobalLedgerView()
119 {
120 }
121 
executeCustomAction(eView::Action action)122 void KGlobalLedgerView::executeCustomAction(eView::Action action)
123 {
124   Q_D(KGlobalLedgerView);
125   switch(action) {
126     case eView::Action::Refresh:
127       refresh();
128       break;
129 
130     case eView::Action::SetDefaultFocus:
131       // delay the setFocus call until the event loop is running
132       QMetaObject::invokeMethod(d->m_registerSearchLine->searchLine(), "setFocus", Qt::QueuedConnection);
133       break;
134 
135     case eView::Action::DisableViewDepenedendActions:
136       pActions[Action::SelectAllTransactions]->setEnabled(false);
137       break;
138 
139     case eView::Action::InitializeAfterFileOpen:
140       d->m_lastSelectedAccountID.clear();
141       d->m_currentAccount = MyMoneyAccount();
142       if (d->m_accountComboBox) {
143         d->m_accountComboBox->setSelected(QString());
144       }
145       break;
146 
147     case eView::Action::CleanupBeforeFileClose:
148       if (d->m_inEditMode) {
149         d->deleteTransactionEditor();
150       }
151       break;
152 
153     default:
154       break;
155   }
156 }
157 
refresh()158 void KGlobalLedgerView::refresh()
159 {
160   Q_D(KGlobalLedgerView);
161   if (isVisible()) {
162     if (!d->m_inEditMode) {
163       setUpdatesEnabled(false);
164       d->loadView();
165       setUpdatesEnabled(true);
166       d->m_needsRefresh = false;
167       // force a new account if the current one is empty
168       d->m_newAccountLoaded = d->m_currentAccount.id().isEmpty();
169     }
170   } else {
171     d->m_needsRefresh = true;
172   }
173 }
174 
showEvent(QShowEvent * event)175 void KGlobalLedgerView::showEvent(QShowEvent* event)
176 {
177   if (MyMoneyFile::instance()->storageAttached()) {
178     Q_D(KGlobalLedgerView);
179     if (d->m_needLoad)
180       d->init();
181 
182     emit customActionRequested(View::Ledgers, eView::Action::AboutToShow);
183 
184     if (d->m_needsRefresh) {
185       if (!d->m_inEditMode) {
186         setUpdatesEnabled(false);
187         d->loadView();
188         setUpdatesEnabled(true);
189         d->m_needsRefresh = false;
190         d->m_newAccountLoaded = false;
191       }
192 
193     } else {
194       if (!d->m_lastSelectedAccountID.isEmpty()) {
195         try {
196           const auto acc = MyMoneyFile::instance()->account(d->m_lastSelectedAccountID);
197           slotSelectAccount(acc.id());
198         } catch (const MyMoneyException &) {
199           d->m_lastSelectedAccountID.clear();                                               // account is invalid
200         }
201       } else {
202         slotSelectAccount(d->m_accountComboBox->getSelected());
203       }
204 
205       KMyMoneyRegister::SelectedTransactions list(d->m_register);
206       updateLedgerActions(list);
207       emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
208     }
209   }
210 
211   pActions[Action::SelectAllTransactions]->setEnabled(true);
212   // don't forget base class implementation
213   QWidget::showEvent(event);
214 }
215 
updateActions(const MyMoneyObject & obj)216 void KGlobalLedgerView::updateActions(const MyMoneyObject& obj)
217 {
218   Q_D(KGlobalLedgerView);
219 //  if (typeid(obj) != typeid(MyMoneyAccount) &&
220 //      (obj.id().isEmpty() && d->m_currentAccount.id().isEmpty())) // do not disable actions that were already disabled))
221 //    return;
222 
223   const auto& acc = static_cast<const MyMoneyAccount&>(obj);
224 
225   const QVector<Action> actionsToBeDisabled {
226         Action::StartReconciliation,
227         Action::FinishReconciliation,
228         Action::PostponeReconciliation,
229         Action::OpenAccount,
230         Action::NewTransaction
231   };
232 
233   for (const auto& a : actionsToBeDisabled)
234     pActions[a]->setEnabled(false);
235 
236   auto b = acc.isClosed() ? false : true;
237   pMenus[Menu::MoveTransaction]->setEnabled(b);
238 
239   QString tooltip;
240   pActions[Action::NewTransaction]->setEnabled(canCreateTransactions(tooltip) || !isVisible());
241   pActions[Action::NewTransaction]->setToolTip(tooltip);
242 
243   const auto file = MyMoneyFile::instance();
244   if (!acc.id().isEmpty() && !file->isStandardAccount(acc.id())) {
245     switch (acc.accountGroup()) {
246       case eMyMoney::Account::Type::Asset:
247       case eMyMoney::Account::Type::Liability:
248       case eMyMoney::Account::Type::Equity:
249         pActions[Action::OpenAccount]->setEnabled(true);
250         if (acc.accountGroup() != eMyMoney::Account::Type::Equity) {
251           if (d->m_reconciliationAccount.id().isEmpty()) {
252             pActions[Action::StartReconciliation]->setEnabled(true);
253             pActions[Action::StartReconciliation]->setToolTip(i18n("Reconcile"));
254           } else {
255             auto tip = i18n("Reconcile - disabled because you are currently reconciling <b>%1</b>", d->m_reconciliationAccount.name());
256             pActions[Action::StartReconciliation]->setToolTip(tip);
257             if (!d->m_transactionEditor) {
258               pActions[Action::FinishReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id());
259               pActions[Action::PostponeReconciliation]->setEnabled(acc.id() == d->m_reconciliationAccount.id());
260             }
261           }
262         }
263         break;
264       case eMyMoney::Account::Type::Income :
265       case eMyMoney::Account::Type::Expense :
266         pActions[Action::OpenAccount]->setEnabled(true);
267         break;
268       default:
269         break;
270     }
271   }
272 
273   d->m_currentAccount = acc;
274 //  slotSelectAccount(acc);
275 }
276 
updateLedgerActions(const KMyMoneyRegister::SelectedTransactions & list)277 void KGlobalLedgerView::updateLedgerActions(const KMyMoneyRegister::SelectedTransactions& list)
278 {
279   Q_D(KGlobalLedgerView);
280 
281   d->selectTransactions(list);
282   updateLedgerActionsInternal();
283 }
284 
updateLedgerActionsInternal()285 void KGlobalLedgerView::updateLedgerActionsInternal()
286 {
287   Q_D(KGlobalLedgerView);
288   const QVector<Action> actionsToBeDisabled {
289     Action::EditTransaction, Action::EditSplits, Action::EnterTransaction,
290     Action::CancelTransaction, Action::DeleteTransaction, Action::MatchTransaction,
291     Action::AcceptTransaction, Action::DuplicateTransaction, Action::AddReversingTransaction, Action::ToggleReconciliationFlag, Action::MarkCleared,
292     Action::GoToAccount, Action::GoToPayee, Action::AssignTransactionsNumber, Action::NewScheduledTransaction,
293     Action::CombineTransactions, Action::CopySplits,
294   };
295 
296   for (const auto& a : actionsToBeDisabled)
297     pActions[a]->setEnabled(false);
298 
299   const auto file = MyMoneyFile::instance();
300 
301   pActions[Action::MatchTransaction]->setText(i18nc("Button text for match transaction", "Match"));
302 //  pActions[Action::TransactionNew]->setToolTip(i18n("Create a new transaction"));
303 
304   pMenus[Menu::MoveTransaction]->setEnabled(false);
305   pMenus[Menu::MarkTransaction]->setEnabled(false);
306   pMenus[Menu::MarkTransactionContext]->setEnabled(false);
307 
308   if (!d->m_selectedTransactions.isEmpty() && !d->m_selectedTransactions.first().isScheduled()) {
309     // enable 'delete transaction' only if at least one of the
310     // selected transactions does not reference a closed account
311     bool enable = false;
312     KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
313     for (it_t = d->m_selectedTransactions.constBegin(); (enable == false) && (it_t != d->m_selectedTransactions.constEnd()); ++it_t) {
314       enable = !(*it_t).transaction().id().isEmpty() && !file->referencesClosedAccount((*it_t).transaction());
315     }
316     pActions[Action::DeleteTransaction]->setEnabled(enable);
317 
318     if (!d->m_transactionEditor) {
319       QString tooltip = i18n("Duplicate the current selected transactions");
320       pActions[Action::DuplicateTransaction]->setEnabled(canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty());
321       pActions[Action::DuplicateTransaction]->setToolTip(tooltip);
322 
323       tooltip = i18n("Add reversing transactions to the currently selected");
324       pActions[Action::AddReversingTransaction]->setEnabled(canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty());
325       pActions[Action::AddReversingTransaction]->setToolTip(tooltip);
326 
327       if (canEditTransactions(d->m_selectedTransactions, tooltip)) {
328         pActions[Action::EditTransaction]->setEnabled(true);
329         // editing splits is allowed only if we have one transaction selected
330         if (d->m_selectedTransactions.count() == 1) {
331           pActions[Action::EditSplits]->setEnabled(true);
332         }
333         if (d->m_currentAccount.isAssetLiability() && d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
334           pActions[Action::NewScheduledTransaction]->setEnabled(d->m_selectedTransactions.count() == 1);
335         }
336       }
337       pActions[Action::EditTransaction]->setToolTip(tooltip);
338 
339       if (!d->m_currentAccount.isClosed())
340         pMenus[Menu::MoveTransaction]->setEnabled(true);
341 
342       pMenus[Menu::MarkTransaction]->setEnabled(true);
343       pMenus[Menu::MarkTransactionContext]->setEnabled(true);
344 
345       // Allow marking the transaction if at least one is selected
346       pActions[Action::MarkCleared]->setEnabled(true);
347       pActions[Action::MarkReconciled]->setEnabled(true);
348       pActions[Action::MarkNotReconciled]->setEnabled(true);
349       pActions[Action::ToggleReconciliationFlag]->setEnabled(true);
350 
351       if (!d->m_accountGoto.isEmpty())
352         pActions[Action::GoToAccount]->setEnabled(true);
353       if (!d->m_payeeGoto.isEmpty())
354         pActions[Action::GoToPayee]->setEnabled(true);
355 
356       // Matching is enabled as soon as one regular and one imported transaction is selected
357       int matchedCount = 0;
358       int importedCount = 0;
359       KMyMoneyRegister::SelectedTransactions::const_iterator it;
360       for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) {
361         if ((*it).transaction().isImported())
362           ++importedCount;
363         if ((*it).split().isMatched())
364           ++matchedCount;
365       }
366 
367       if (d->m_selectedTransactions.count() == 2 /* && pActions[Action::TransactionEdit]->isEnabled() */) {
368         pActions[Action::MatchTransaction]->setEnabled(true);
369       }
370       if (importedCount != 0 || matchedCount != 0)
371         pActions[Action::AcceptTransaction]->setEnabled(true);
372       if (matchedCount != 0) {
373         pActions[Action::MatchTransaction]->setEnabled(true);
374         pActions[Action::MatchTransaction]->setText(i18nc("Button text for unmatch transaction", "Unmatch"));
375         pActions[Action::MatchTransaction]->setIcon(QIcon("process-stop"));
376       }
377 
378       if (d->m_selectedTransactions.count() > 1) {
379         pActions[Action::CombineTransactions]->setEnabled(true);
380       }
381       if (d->m_selectedTransactions.count() >= 2) {
382         int singleSplitTransactions = 0;
383         int multipleSplitTransactions = 0;
384         foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
385           switch (st.transaction().splitCount()) {
386             case 0:
387               break;
388             case 1:
389               singleSplitTransactions++;
390               break;
391             default:
392               multipleSplitTransactions++;
393               break;
394           }
395         }
396         if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
397           pActions[Action::CopySplits]->setEnabled(true);
398         }
399       }
400       if (d->m_selectedTransactions.count() >= 2) {
401         int singleSplitTransactions = 0;
402         int multipleSplitTransactions = 0;
403         foreach(const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
404           switch(st.transaction().splitCount()) {
405             case 0:
406               break;
407             case 1:
408               singleSplitTransactions++;
409               break;
410             default:
411               multipleSplitTransactions++;
412               break;
413           }
414         }
415         if(singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
416           pActions[Action::CopySplits]->setEnabled(true);
417         }
418       }
419     } else {
420       pActions[Action::AssignTransactionsNumber]->setEnabled(d->m_transactionEditor->canAssignNumber());
421       pActions[Action::NewTransaction]->setEnabled(false);
422       pActions[Action::DeleteTransaction]->setEnabled(false);
423       QString reason;
424       pActions[Action::EnterTransaction]->setEnabled(d->m_transactionEditor->isComplete(reason));
425       //FIXME: Port to KDE4
426       // the next line somehow worked in KDE3 but does not have
427       // any influence under KDE4
428       ///  Works for me when 'reason' is set. Allan
429       pActions[Action::EnterTransaction]->setToolTip(reason);
430       pActions[Action::CancelTransaction]->setEnabled(true);
431     }
432   }
433 }
434 
slotAboutToSelectItem(KMyMoneyRegister::RegisterItem * item,bool & okToSelect)435 void KGlobalLedgerView::slotAboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect)
436 {
437   Q_UNUSED(item);
438   slotCancelOrEnterTransactions(okToSelect);
439 }
440 
slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions & selection)441 void KGlobalLedgerView::slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions& selection)
442 {
443   Q_D(KGlobalLedgerView);
444   if (selection.count() > 1) {
445     MyMoneyMoney balance;
446     foreach (const KMyMoneyRegister::SelectedTransaction& t, selection) {
447       if (!t.isScheduled()) {
448         balance += t.split().shares();
449       }
450     }
451     d->m_rightSummaryLabel->setText(QString("%1: %2").arg(QChar(0x2211), balance.formatMoney("", -1)));
452 
453   } else {
454     if (d->isReconciliationAccount()) {
455       d->m_rightSummaryLabel->setText(i18n("Difference: %1", d->m_totalBalance.formatMoney("", d->m_precision)));
456 
457     } else {
458       if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
459         d->m_rightSummaryLabel->setText(i18n("Balance: %1", d->m_totalBalance.formatMoney("", d->m_precision)));
460         bool showNegative = d->m_totalBalance.isNegative();
461         if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Liability && !d->m_totalBalance.isZero())
462           showNegative = !showNegative;
463         if (showNegative) {
464           QPalette palette = d->m_rightSummaryLabel->palette();
465           palette.setColor(d->m_rightSummaryLabel->foregroundRole(), KMyMoneySettings::schemeColor(SchemeColor::Negative));
466           d->m_rightSummaryLabel->setPalette(palette);
467         }
468       } else {
469         d->m_rightSummaryLabel->setText(i18n("Investment value: %1%2",
470                                              d->m_balanceIsApproximated ? "~" : "",
471                                              d->m_totalBalance.formatMoney(MyMoneyFile::instance()->baseCurrency().tradingSymbol(), d->m_precision)));
472       }
473     }
474   }
475 }
476 
resizeEvent(QResizeEvent * ev)477 void KGlobalLedgerView::resizeEvent(QResizeEvent* ev)
478 {
479   if (MyMoneyFile::instance()->storageAttached()) {
480     Q_D(KGlobalLedgerView);
481     if (d->m_needLoad)
482       d->init();
483 
484     d->m_register->resize((int)eWidgets::eTransaction::Column::Detail);
485     d->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1);
486   }
487   KMyMoneyViewBase::resizeEvent(ev);
488 }
489 
slotSetReconcileAccount(const MyMoneyAccount & acc,const QDate & reconciliationDate,const MyMoneyMoney & endingBalance)490 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance)
491 {
492   Q_D(KGlobalLedgerView);
493   if(d->m_needLoad)
494     d->init();
495 
496   if (d->m_reconciliationAccount.id() != acc.id()) {
497     // make sure the account is selected
498     if (!acc.id().isEmpty())
499       slotSelectAccount(acc.id());
500 
501     d->m_reconciliationAccount = acc;
502     d->m_reconciliationDate = reconciliationDate;
503     d->m_endingBalance = endingBalance;
504     if (acc.accountGroup() == eMyMoney::Account::Type::Liability)
505       d->m_endingBalance = -endingBalance;
506 
507     d->m_newAccountLoaded = true;
508 
509     if (acc.id().isEmpty()) {
510       d->m_buttonbar->removeAction(pActions[Action::PostponeReconciliation]);
511       d->m_buttonbar->removeAction(pActions[Action::FinishReconciliation]);
512     } else {
513       d->m_buttonbar->addAction(pActions[Action::PostponeReconciliation]);
514       d->m_buttonbar->addAction(pActions[Action::FinishReconciliation]);
515       // when we start reconciliation, we need to reload the view
516       // because no data has been changed. When postponing or finishing
517       // reconciliation, the data change in the engine takes care of updating
518       // the view.
519       refresh();
520     }
521   }
522 }
523 
slotSetReconcileAccount(const MyMoneyAccount & acc,const QDate & reconciliationDate)524 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate)
525 {
526   slotSetReconcileAccount(acc, reconciliationDate, MyMoneyMoney());
527 }
528 
slotSetReconcileAccount(const MyMoneyAccount & acc)529 void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc)
530 {
531   slotSetReconcileAccount(acc, QDate(), MyMoneyMoney());
532 }
533 
slotSetReconcileAccount()534 void KGlobalLedgerView::slotSetReconcileAccount()
535 {
536   slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
537 }
538 
slotShowTransactionMenu(const MyMoneySplit & sp)539 void KGlobalLedgerView::slotShowTransactionMenu(const MyMoneySplit& sp)
540 {
541   Q_UNUSED(sp)
542   pMenus[Menu::Transaction]->exec(QCursor::pos());
543 }
544 
slotContinueReconciliation()545 void KGlobalLedgerView::slotContinueReconciliation()
546 {
547   Q_D(KGlobalLedgerView);
548   const auto file = MyMoneyFile::instance();
549   MyMoneyAccount account;
550 
551   try {
552     account = file->account(d->m_currentAccount.id());
553     // get rid of previous run.
554     delete d->m_endingBalanceDlg;
555     d->m_endingBalanceDlg = new KEndingBalanceDlg(account, this);
556     if (account.isAssetLiability()) {
557 
558       if (d->m_endingBalanceDlg->exec() == QDialog::Accepted) {
559         if (KMyMoneySettings::autoReconciliation()) {
560           MyMoneyMoney startBalance = d->m_endingBalanceDlg->previousBalance();
561           MyMoneyMoney endBalance = d->m_endingBalanceDlg->endingBalance();
562           QDate endDate = d->m_endingBalanceDlg->statementDate();
563 
564           QList<QPair<MyMoneyTransaction, MyMoneySplit> > transactionList;
565           MyMoneyTransactionFilter filter(account.id());
566           filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
567           filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
568           filter.setDateFilter(QDate(), endDate);
569           filter.setConsiderCategory(false);
570           filter.setReportAllSplits(true);
571           file->transactionList(transactionList, filter);
572           QList<QPair<MyMoneyTransaction, MyMoneySplit> > result = d->automaticReconciliation(account, transactionList, endBalance - startBalance);
573 
574           if (!result.empty()) {
575             QString message = i18n("KMyMoney has detected transactions matching your reconciliation data.\nWould you like KMyMoney to clear these transactions for you?");
576             if (KMessageBox::questionYesNo(this,
577                                            message,
578                                            i18n("Automatic reconciliation"),
579                                            KStandardGuiItem::yes(),
580                                            KStandardGuiItem::no(),
581                                            "AcceptAutomaticReconciliation") == KMessageBox::Yes) {
582               // mark the transactions cleared
583               KMyMoneyRegister::SelectedTransactions oldSelection = d->m_selectedTransactions;
584               d->m_selectedTransactions.clear();
585               QListIterator<QPair<MyMoneyTransaction, MyMoneySplit> > itTransactionSplitResult(result);
586               while (itTransactionSplitResult.hasNext()) {
587                 const QPair<MyMoneyTransaction, MyMoneySplit> &transactionSplit = itTransactionSplitResult.next();
588                 d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second, QString()));
589               }
590               // mark all transactions in d->m_selectedTransactions as 'Cleared'
591               d->markTransaction(eMyMoney::Split::State::Cleared);
592               d->m_selectedTransactions = oldSelection;
593             }
594           }
595         }
596 
597         if (!file->isStandardAccount(account.id()) &&
598             account.isAssetLiability()) {
599           if (!isVisible())
600             emit customActionRequested(View::Ledgers, eView::Action::SwitchView);
601           Models::instance()->accountsModel()->slotReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
602           slotSetReconcileAccount(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
603 
604           // check if the user requests us to create interest
605           // or charge transactions.
606           auto ti = d->m_endingBalanceDlg->interestTransaction();
607           auto tc = d->m_endingBalanceDlg->chargeTransaction();
608           MyMoneyFileTransaction ft;
609           try {
610             if (ti != MyMoneyTransaction()) {
611               MyMoneyFile::instance()->addTransaction(ti);
612             }
613             if (tc != MyMoneyTransaction()) {
614               MyMoneyFile::instance()->addTransaction(tc);
615             }
616             ft.commit();
617 
618           } catch (const MyMoneyException &e) {
619             qWarning("interest transaction not stored: '%s'", e.what());
620           }
621 
622           // reload the account object as it might have changed in the meantime
623           d->m_reconciliationAccount = file->account(account.id());
624           updateActions(d->m_currentAccount);
625           updateLedgerActionsInternal();
626           //            slotUpdateActions();
627         }
628       }
629     }
630   } catch (const MyMoneyException &) {
631   }
632 }
633 
slotLedgerSelected(const QString & _accId,const QString & transaction)634 void KGlobalLedgerView::slotLedgerSelected(const QString& _accId, const QString& transaction)
635 {
636   auto acc = MyMoneyFile::instance()->account(_accId);
637   QString accId(_accId);
638 
639   switch (acc.accountType()) {
640     case Account::Type::Stock:
641       // if a stock account is selected, we show the
642       // the corresponding parent (investment) account
643       acc = MyMoneyFile::instance()->account(acc.parentAccountId());
644       accId = acc.id();
645       // intentional fall through
646 
647     case Account::Type::Checkings:
648     case Account::Type::Savings:
649     case Account::Type::Cash:
650     case Account::Type::CreditCard:
651     case Account::Type::Loan:
652     case Account::Type::Asset:
653     case Account::Type::Liability:
654     case Account::Type::AssetLoan:
655     case Account::Type::Income:
656     case Account::Type::Expense:
657     case Account::Type::Investment:
658     case Account::Type::Equity:
659       if (!isVisible())
660         emit customActionRequested(View::Ledgers, eView::Action::SwitchView);
661       slotSelectAccount(accId, transaction);
662       break;
663 
664     case Account::Type::CertificateDep:
665     case Account::Type::MoneyMarket:
666     case Account::Type::Currency:
667       qDebug("No ledger view available for account type %d", (int)acc.accountType());
668       break;
669 
670     default:
671       qDebug("Unknown account type %d in KMyMoneyView::slotLedgerSelected", (int)acc.accountType());
672       break;
673   }
674 }
675 
slotSelectByObject(const MyMoneyObject & obj,eView::Intent intent)676 void KGlobalLedgerView::slotSelectByObject(const MyMoneyObject& obj, eView::Intent intent)
677 {
678   switch(intent) {
679     case eView::Intent::UpdateActions:
680       updateActions(obj);
681       break;
682 
683     case eView::Intent::FinishEnteringOverdueScheduledTransactions:
684       slotContinueReconciliation();
685       break;
686 
687     case eView::Intent::SynchronizeAccountInLedgersView:
688       slotSelectAccount(obj);
689       break;
690 
691     default:
692       break;
693   }
694 }
695 
slotSelectByVariant(const QVariantList & variant,eView::Intent intent)696 void KGlobalLedgerView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent)
697 {
698   switch(intent) {
699     case eView::Intent::ShowTransaction:
700       if (variant.count() == 2)
701         slotLedgerSelected(variant.at(0).toString(), variant.at(1).toString());
702       break;
703     case eView::Intent::SelectRegisterTransactions:
704       if (variant.count() == 1)
705         updateLedgerActions(variant.at(0).value<KMyMoneyRegister::SelectedTransactions>());
706       break;
707     default:
708       break;
709   }
710 }
711 
slotSelectAccount(const MyMoneyObject & obj)712 void KGlobalLedgerView::slotSelectAccount(const MyMoneyObject& obj)
713 {
714   Q_D(KGlobalLedgerView);
715   if (typeid(obj) != typeid(MyMoneyAccount))
716     return/* false */;
717 
718   d->m_lastSelectedAccountID = obj.id();
719 }
720 
slotSelectAccount(const QString & id)721 void KGlobalLedgerView::slotSelectAccount(const QString& id)
722 {
723   slotSelectAccount(id, QString());
724 }
725 
slotSelectAccount(const QString & id,const QString & transactionId)726 bool KGlobalLedgerView::slotSelectAccount(const QString& id, const QString& transactionId)
727 {
728   Q_D(KGlobalLedgerView);
729   auto rc = true;
730 
731   if (!id.isEmpty()) {
732     if (d->m_currentAccount.id() != id) {
733       try {
734         d->m_currentAccount = MyMoneyFile::instance()->account(id);
735         // if a stock account is selected, we show the
736         // the corresponding parent (investment) account
737         if (d->m_currentAccount.isInvest()) {
738           d->m_currentAccount = MyMoneyFile::instance()->account(d->m_currentAccount.parentAccountId());
739         }
740         d->m_lastSelectedAccountID = d->m_currentAccount.id();
741         d->m_newAccountLoaded = true;
742         refresh();
743       } catch (const MyMoneyException &) {
744         qDebug("Unable to retrieve account %s", qPrintable(id));
745         rc = false;
746       }
747     } else {
748       // we need to refresh m_account.m_accountList, a child could have been deleted
749       d->m_currentAccount = MyMoneyFile::instance()->account(id);
750 
751       emit selectByObject(d->m_currentAccount, eView::Intent::None);
752       emit selectByObject(d->m_currentAccount, eView::Intent::SynchronizeAccountInInvestmentView);
753     }
754     d->selectTransaction(transactionId);
755   }
756   return rc;
757 }
758 
selectEmptyTransaction()759 bool KGlobalLedgerView::selectEmptyTransaction()
760 {
761   Q_D(KGlobalLedgerView);
762   bool rc = false;
763 
764   if (!d->m_inEditMode) {
765     // in case we don't know the type of transaction to be created,
766     // have at least one selected transaction and the id of
767     // this transaction is not empty, we take it as template for the
768     // transaction to be created
769     KMyMoneyRegister::SelectedTransactions list(d->m_register);
770     if ((d->m_action == eWidgets::eRegister::Action::None) && (!list.isEmpty()) && (!list[0].transaction().id().isEmpty())) {
771       // the new transaction to be created will have the same type
772       // as the one that currently has the focus
773       KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(d->m_register->focusItem());
774       if (t)
775         d->m_action = t->actionType();
776       d->m_register->clearSelection();
777     }
778 
779     // if we still don't have an idea which type of transaction
780     // to create, we use the default.
781     if (d->m_action == eWidgets::eRegister::Action::None) {
782       d->setupDefaultAction();
783     }
784 
785     d->m_register->selectItem(d->m_register->lastItem());
786     d->m_register->updateRegister();
787     rc = true;
788   }
789   return rc;
790 }
791 
startEdit(const KMyMoneyRegister::SelectedTransactions & list)792 TransactionEditor* KGlobalLedgerView::startEdit(const KMyMoneyRegister::SelectedTransactions& list)
793 {
794   Q_D(KGlobalLedgerView);
795   // we use the warnlevel to keep track, if we have to warn the
796   // user that some or all splits have been reconciled or if the
797   // user cannot modify the transaction if at least one split
798   // has the status frozen. The following value are used:
799   //
800   // 0 - no sweat, user can modify
801   // 1 - user should be warned that at least one split has been reconciled
802   //     already
803   // 2 - user will be informed, that this transaction cannot be changed anymore
804 
805   int warnLevel = list.warnLevel();
806   Q_ASSERT(warnLevel < 2);  // otherwise the edit action should not be enabled
807 
808   switch (warnLevel) {
809     case 0:
810       break;
811 
812     case 1:
813       if (KMessageBox::warningContinueCancel(this,
814                                              i18n(
815                                                "At least one split of the selected transactions has been reconciled. "
816                                                "Do you wish to continue to edit the transactions anyway?"
817                                                ),
818                                              i18n("Transaction already reconciled"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
819                                              "EditReconciledTransaction") == KMessageBox::Cancel) {
820         warnLevel = 2;
821       }
822       break;
823 
824     case 2:
825       KMessageBox::sorry(this,
826                          i18n("At least one split of the selected transactions has been frozen. "
827                               "Editing the transactions is therefore prohibited."),
828                          i18n("Transaction already frozen"));
829       break;
830 
831     case 3:
832       KMessageBox::sorry(this,
833                          i18n("At least one split of the selected transaction references an account that has been closed. "
834                               "Editing the transactions is therefore prohibited."),
835                          i18n("Account closed"));
836       break;
837   }
838 
839   if (warnLevel > 1) {
840     d->m_register->endEdit();
841     return 0;
842   }
843 
844 
845   TransactionEditor* editor = 0;
846   KMyMoneyRegister::Transaction* item = dynamic_cast<KMyMoneyRegister::Transaction*>(d->m_register->focusItem());
847 
848   if (item) {
849     // in case the current focus item is not selected, we move the focus to the first selected transaction
850     if (!item->isSelected()) {
851       KMyMoneyRegister::RegisterItem* p;
852       for (p = d->m_register->firstItem(); p; p = p->nextItem()) {
853         KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
854         if (t && t->isSelected()) {
855           d->m_register->setFocusItem(t);
856           item = t;
857           break;
858         }
859       }
860     }
861 
862     // decide, if we edit in the register or in the form
863     TransactionEditorContainer* parent;
864     if (d->m_formFrame->isVisible())
865       parent = d->m_form;
866     else {
867       parent = d->m_register;
868     }
869 
870     editor = item->createEditor(parent, list, KGlobalLedgerViewPrivate::m_lastPostDate);
871 
872     // check that we use the same transaction commodity in all selected transactions
873     // if not, we need to update this in the editor's list. The user can also bail out
874     // of this operation which means that we have to stop editing here.
875     if (editor) {
876       if (!editor->fixTransactionCommodity(d->m_currentAccount)) {
877         // if the user wants to quit, we need to destroy the editor
878         // and bail out
879         delete editor;
880         editor = 0;
881       }
882     }
883 
884     if (editor) {
885       if (parent == d->m_register) {
886         // make sure, the height of the table is correct
887         d->m_register->updateRegister(KMyMoneySettings::ledgerLens() | !KMyMoneySettings::transactionForm());
888       }
889 
890       d->m_inEditMode = true;
891       connect(editor, &TransactionEditor::transactionDataSufficient, pActions[Action::EnterTransaction], &QAction::setEnabled);
892       connect(editor, &TransactionEditor::returnPressed, pActions[Action::EnterTransaction], &QAction::trigger);
893       connect(editor, &TransactionEditor::escapePressed, pActions[Action::CancelTransaction], &QAction::trigger);
894 
895       connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets);
896       connect(editor, &TransactionEditor::finishEdit, this, &KGlobalLedgerView::slotLeaveEditMode);
897       connect(editor, &TransactionEditor::objectCreation, d->m_mousePressFilter, &MousePressFilter::setFilterDeactive);
898       connect(editor, &TransactionEditor::lastPostDateUsed, this, &KGlobalLedgerView::slotKeepPostDate);
899 
900       // create the widgets, place them in the parent and load them with data
901       // setup tab order
902       d->m_tabOrderWidgets.clear();
903       editor->setup(d->m_tabOrderWidgets, d->m_currentAccount, d->m_action);
904 
905       Q_ASSERT(!d->m_tabOrderWidgets.isEmpty());
906 
907       // install event filter in all taborder widgets
908       QWidgetList::const_iterator it_w = d->m_tabOrderWidgets.constBegin();
909       for (; it_w != d->m_tabOrderWidgets.constEnd(); ++it_w) {
910         (*it_w)->installEventFilter(this);
911       }
912       // Install a filter that checks if a mouse press happened outside
913       // of one of our own widgets.
914       qApp->installEventFilter(d->m_mousePressFilter);
915 
916       // Check if the editor has some preference on where to set the focus
917       // If not, set the focus to the first widget in the tab order
918       QWidget* focusWidget = editor->firstWidget();
919       if (!focusWidget)
920         focusWidget = d->m_tabOrderWidgets.first();
921 
922       // for some reason, this only works reliably if delayed a bit
923       QTimer::singleShot(10, focusWidget, SLOT(setFocus()));
924 
925       // preset to 'I have no idea which type to create' for the next round.
926       d->m_action = eWidgets::eRegister::Action::None;
927     }
928   }
929   return editor;
930 }
931 
slotTransactionsContextMenuRequested()932 void KGlobalLedgerView::slotTransactionsContextMenuRequested()
933 {
934   Q_D(KGlobalLedgerView);
935   auto transactions = d->m_selectedTransactions;
936   updateLedgerActionsInternal();
937 //  emit transactionsSelected(d->m_selectedTransactions); // that should select MyMoneySchedule in KScheduledView
938   if (!transactions.isEmpty() && transactions.first().isScheduled())
939     emit selectByObject(MyMoneyFile::instance()->schedule(transactions.first().scheduleId()), eView::Intent::OpenContextMenu);
940   else
941     slotShowTransactionMenu(MyMoneySplit());
942 }
943 
slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions & list)944 void KGlobalLedgerView::slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list)
945 {
946   Q_D(KGlobalLedgerView);
947   d->m_inEditMode = false;
948   qApp->removeEventFilter(d->m_mousePressFilter);
949 
950   // a possible focusOut event may have removed the focus, so we
951   // install it back again.
952   d->m_register->focusItem()->setFocus(true);
953 
954   // if we come back from editing a new item, we make sure that
955   // we always select the very last known transaction entry no
956   // matter if the transaction has been created or not.
957 
958   if (list.count() && list[0].transaction().id().isEmpty()) {
959     // block signals to prevent some infinite loops that might occur here.
960     d->m_register->blockSignals(true);
961     d->m_register->clearSelection();
962     KMyMoneyRegister::RegisterItem* p = d->m_register->lastItem();
963     if (p && p->prevItem())
964       p = p->prevItem();
965     d->m_register->selectItem(p);
966     d->m_register->updateRegister(true);
967     d->m_register->blockSignals(false);
968     // we need to update the form manually as sending signals was blocked
969     KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
970     if (t)
971       d->m_form->slotSetTransaction(t);
972   } else {
973     if (!KMyMoneySettings::transactionForm()) {
974       // update the row height of the transactions because it might differ between viewing/editing mode when not using the transaction form
975       d->m_register->blockSignals(true);
976       d->m_register->updateRegister(true);
977       d->m_register->blockSignals(false);
978     }
979   }
980   d->m_needsRefresh = true; // TODO: Why transaction in view doesn't update without this?
981   if (d->m_needsRefresh)
982     refresh();
983 
984   d->m_register->endEdit();
985   d->m_register->setFocus();
986 }
987 
focusNextPrevChild(bool next)988 bool KGlobalLedgerView::focusNextPrevChild(bool next)
989 {
990   Q_D(KGlobalLedgerView);
991   bool  rc = false;
992   // qDebug() << "----------------------------------------------------------";
993   // qDebug() << "KGlobalLedgerView::focusNextPrevChild, editmode=" << d->m_inEditMode;
994   if (d->m_inEditMode) {
995     QWidget *w = 0;
996 
997     w = qApp->focusWidget();
998     int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
999     const auto startIndex = currentWidgetIndex;
1000     // qDebug() << "Focus is at currentWidgetIndex" <<  currentWidgetIndex << w->objectName();
1001     do {
1002       while (w && currentWidgetIndex == -1) {
1003         // qDebug() << w->objectName() << "not in list, use parent";
1004         w = w->parentWidget();
1005         currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
1006       }
1007       // qDebug() << "Focus is at currentWidgetIndex" <<  currentWidgetIndex << w->objectName();
1008 
1009       if (currentWidgetIndex != -1) {
1010         // if(w) qDebug() << "tab order is at" << w->objectName();
1011         currentWidgetIndex += next ? 1 : -1;
1012         if (currentWidgetIndex < 0)
1013           currentWidgetIndex = d->m_tabOrderWidgets.size() - 1;
1014         else if (currentWidgetIndex >= d->m_tabOrderWidgets.size())
1015           currentWidgetIndex = 0;
1016 
1017         w = d->m_tabOrderWidgets[currentWidgetIndex];
1018         // qDebug() << "currentWidgetIndex" <<  currentWidgetIndex << w->objectName() << w->isVisible();
1019 
1020         if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) {
1021           // qDebug() << "Set focus to" << w->objectName();
1022           w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason);
1023           rc = true;
1024           break;
1025         }
1026       } else {
1027         break;
1028       }
1029     } while(currentWidgetIndex != startIndex);
1030   } else
1031     rc = KMyMoneyViewBase::focusNextPrevChild(next);
1032   return rc;
1033 }
1034 
eventFilter(QObject * o,QEvent * e)1035 bool KGlobalLedgerView::eventFilter(QObject* o, QEvent* e)
1036 {
1037   Q_D(KGlobalLedgerView);
1038   bool rc = false;
1039   //  Need to capture mouse position here as QEvent::ToolTip is too slow
1040   d->m_tooltipPosn = QCursor::pos();
1041 
1042   if (e->type() == QEvent::KeyPress) {
1043     if (d->m_inEditMode) {
1044       // qDebug("object = %s, key = %d", o->className(), k->key());
1045       if (o == d->m_register) {
1046         // we hide all key press events from the register
1047         // while editing a transaction
1048         rc = true;
1049       }
1050     }
1051   }
1052 
1053   if (!rc)
1054     rc = KMyMoneyViewBase::eventFilter(o, e);
1055 
1056   return rc;
1057 }
1058 
slotSortOptions()1059 void KGlobalLedgerView::slotSortOptions()
1060 {
1061   Q_D(KGlobalLedgerView);
1062   QPointer<KSortOptionDlg> dlg = new KSortOptionDlg(this);
1063 
1064   QString key;
1065   QString sortOrder, def;
1066   if (d->isReconciliationAccount()) {
1067     key = "kmm-sort-reconcile";
1068     def = KMyMoneySettings::sortReconcileView();
1069   } else {
1070     key = "kmm-sort-std";
1071     def = KMyMoneySettings::sortNormalView();
1072   }
1073 
1074   // check if we have an account override of the sort order
1075   if (!d->m_currentAccount.value(key).isEmpty())
1076     sortOrder = d->m_currentAccount.value(key);
1077 
1078   QString oldOrder = sortOrder;
1079 
1080   dlg->setSortOption(sortOrder, def);
1081 
1082   if (dlg->exec() == QDialog::Accepted) {
1083     if (dlg != 0) {
1084       sortOrder = dlg->sortOption();
1085       if (sortOrder != oldOrder) {
1086         if (sortOrder.isEmpty()) {
1087           d->m_currentAccount.deletePair(key);
1088         } else {
1089           d->m_currentAccount.setValue(key, sortOrder);
1090         }
1091         MyMoneyFileTransaction ft;
1092         try {
1093           MyMoneyFile::instance()->modifyAccount(d->m_currentAccount);
1094           ft.commit();
1095         } catch (const MyMoneyException &e) {
1096           qDebug("Unable to update sort order for account '%s': %s", qPrintable(d->m_currentAccount.name()), e.what());
1097         }
1098       }
1099     }
1100   }
1101   delete dlg;
1102 }
1103 
slotToggleTransactionMark(KMyMoneyRegister::Transaction *)1104 void KGlobalLedgerView::slotToggleTransactionMark(KMyMoneyRegister::Transaction* /* t */)
1105 {
1106   Q_D(KGlobalLedgerView);
1107   if (!d->m_inEditMode) {
1108     slotToggleReconciliationFlag();
1109   }
1110 }
1111 
slotKeepPostDate(const QDate & date)1112 void KGlobalLedgerView::slotKeepPostDate(const QDate& date)
1113 {
1114   KGlobalLedgerViewPrivate::m_lastPostDate = date;
1115 }
1116 
accountId() const1117 QString KGlobalLedgerView::accountId() const
1118 {
1119   Q_D(const KGlobalLedgerView);
1120   return d->m_currentAccount.id();
1121 }
1122 
canCreateTransactions(QString & tooltip) const1123 bool KGlobalLedgerView::canCreateTransactions(QString& tooltip) const
1124 {
1125   Q_D(const KGlobalLedgerView);
1126   bool rc = true;
1127 
1128   if (d->m_currentAccount.id().isEmpty()) {
1129     tooltip = i18n("Cannot create transactions when no account is selected.");
1130     rc = false;
1131   }
1132   if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income
1133       || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) {
1134     tooltip = i18n("Cannot create transactions in the context of a category.");
1135     d->showTooltip(tooltip);
1136     rc = false;
1137   }
1138   if (d->m_currentAccount.isClosed()) {
1139     tooltip = i18n("Cannot create transactions in a closed account.");
1140     d->showTooltip(tooltip);
1141     rc = false;
1142   }
1143   return rc;
1144 }
1145 
canModifyTransactions(const KMyMoneyRegister::SelectedTransactions & list,QString & tooltip) const1146 bool KGlobalLedgerView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1147 {
1148   Q_D(const KGlobalLedgerView);
1149   return d->canProcessTransactions(list, tooltip) && list.canModify();
1150 }
1151 
canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions & list,QString & tooltip) const1152 bool KGlobalLedgerView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1153 {
1154   Q_D(const KGlobalLedgerView);
1155   return d->canProcessTransactions(list, tooltip) && list.canDuplicate();
1156 }
1157 
canEditTransactions(const KMyMoneyRegister::SelectedTransactions & list,QString & tooltip) const1158 bool KGlobalLedgerView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const
1159 {
1160   Q_D(const KGlobalLedgerView);
1161   // check if we can edit the list of transactions. We can edit, if
1162   //
1163   //   a) no mix of standard and investment transactions exist
1164   //   b) if a split transaction is selected, this is the only selection
1165   //   c) none of the splits is frozen
1166   //   d) the transaction having the current focus is selected
1167 
1168   // check for d)
1169   if (!d->canProcessTransactions(list, tooltip))
1170     return false;
1171   // check for c)
1172   if (list.warnLevel() == 2) {
1173     tooltip = i18n("Cannot edit transactions with frozen splits.");
1174     d->showTooltip(tooltip);
1175     return false;
1176   }
1177 
1178   bool rc = true;
1179   int investmentTransactions = 0;
1180   int normalTransactions = 0;
1181 
1182   if (d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Income
1183       || d->m_currentAccount.accountGroup() == eMyMoney::Account::Type::Expense) {
1184     tooltip = i18n("Cannot edit transactions in the context of a category.");
1185     d->showTooltip(tooltip);
1186     rc = false;
1187   }
1188 
1189   if (d->m_currentAccount.isClosed()) {
1190     tooltip = i18n("Cannot create or edit any transactions in Account %1 as it is closed", d->m_currentAccount.name());
1191     d->showTooltip(tooltip);
1192     rc = false;
1193   }
1194 
1195   KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
1196   QString action;
1197   for (it_t = list.begin(); rc && it_t != list.end(); ++it_t) {
1198     if ((*it_t).transaction().id().isEmpty()) {
1199       tooltip.clear();
1200       rc = false;
1201       continue;
1202     }
1203 
1204     if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) {
1205       if (action.isEmpty()) {
1206         action = (*it_t).split().action();
1207         continue;
1208       }
1209       if (action == (*it_t).split().action()) {
1210         continue;
1211       } else {
1212         tooltip = (i18n("Cannot edit mixed investment action/type transactions together."));
1213         d->showTooltip(tooltip);
1214         rc = false;
1215         break;
1216       }
1217     }
1218 
1219     if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction)
1220       ++investmentTransactions;
1221     else
1222       ++normalTransactions;
1223 
1224     // check for a)
1225     if (investmentTransactions != 0 && normalTransactions != 0) {
1226       tooltip = i18n("Cannot edit investment transactions and non-investment transactions together.");
1227       d->showTooltip(tooltip);
1228       rc = false;
1229       break;
1230     }
1231 
1232     // check for b) but only for normalTransactions
1233     if ((*it_t).transaction().splitCount() > 2 && normalTransactions != 0) {
1234       if (list.count() > 1) {
1235         tooltip = i18n("Cannot edit multiple split transactions at once.");
1236         d->showTooltip(tooltip);
1237         rc = false;
1238         break;
1239       }
1240     }
1241   }
1242 
1243   // check for multiple transactions being selected in an investment account
1244   // we do not allow editing in this case: https://bugs.kde.org/show_bug.cgi?id=240816
1245   // later on, we might allow to edit investment transactions of the same type
1246   ///  Can now disable the following check.
1247 
1248   /*  if (rc == true && investmentTransactions > 1) {
1249       tooltip = i18n("Cannot edit multiple investment transactions at once");
1250       rc = false;
1251     }*/
1252 
1253   // now check that we have the correct account type for investment transactions
1254   if (rc == true && investmentTransactions != 0) {
1255     if (d->m_currentAccount.accountType() != eMyMoney::Account::Type::Investment) {
1256       tooltip = i18n("Cannot edit investment transactions in the context of this account.");
1257       rc = false;
1258     }
1259   }
1260   return rc;
1261 }
1262 
slotMoveToAccount(const QString & id)1263 void KGlobalLedgerView::slotMoveToAccount(const QString& id)
1264 {
1265   Q_D(KGlobalLedgerView);
1266   // close the menu, if it is still open
1267   if (pMenus[Menu::Transaction]->isVisible())
1268     pMenus[Menu::Transaction]->close();
1269 
1270   if (!d->m_selectedTransactions.isEmpty()) {
1271     const auto file = MyMoneyFile::instance();
1272     MyMoneyFileTransaction ft;
1273     try {
1274       foreach (const auto selection, d->m_selectedTransactions) {
1275         if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) {
1276           d->moveInvestmentTransaction(d->m_currentAccount.id(), id, selection.transaction());
1277         } else {
1278           // we get the data afresh from the engine as
1279           // it might have changed by a previous iteration
1280           // in this loop. Use case: two splits point to
1281           // the same account and both are selected.
1282           auto tid = selection.transaction().id();
1283           auto sid = selection.split().id();
1284           auto t = file->transaction(tid);
1285           auto s = t.splitById(sid);
1286           s.setAccountId(id);
1287           t.modifySplit(s);
1288           file->modifyTransaction(t);
1289         }
1290       }
1291       ft.commit();
1292     } catch (const MyMoneyException &) {
1293     }
1294   }
1295 }
1296 
slotUpdateMoveToAccountMenu()1297 void KGlobalLedgerView::slotUpdateMoveToAccountMenu()
1298 {
1299   Q_D(KGlobalLedgerView);
1300   d->createTransactionMoveMenu();
1301 
1302   // in case we were not able to create the selector, we
1303   // better get out of here. Anything else would cause
1304   // a crash later on (accountSet.load)
1305   if (!d->m_moveToAccountSelector)
1306     return;
1307 
1308   if (!d->m_currentAccount.id().isEmpty()) {
1309     AccountSet accountSet;
1310     if (d->m_currentAccount.accountType() == eMyMoney::Account::Type::Investment) {
1311       accountSet.addAccountType(eMyMoney::Account::Type::Investment);
1312     } else if (d->m_currentAccount.isAssetLiability()) {
1313 
1314       accountSet.addAccountType(eMyMoney::Account::Type::Checkings);
1315       accountSet.addAccountType(eMyMoney::Account::Type::Savings);
1316       accountSet.addAccountType(eMyMoney::Account::Type::Cash);
1317       accountSet.addAccountType(eMyMoney::Account::Type::AssetLoan);
1318       accountSet.addAccountType(eMyMoney::Account::Type::CertificateDep);
1319       accountSet.addAccountType(eMyMoney::Account::Type::MoneyMarket);
1320       accountSet.addAccountType(eMyMoney::Account::Type::Asset);
1321       accountSet.addAccountType(eMyMoney::Account::Type::Currency);
1322       accountSet.addAccountType(eMyMoney::Account::Type::CreditCard);
1323       accountSet.addAccountType(eMyMoney::Account::Type::Loan);
1324       accountSet.addAccountType(eMyMoney::Account::Type::Liability);
1325     } else if (d->m_currentAccount.isIncomeExpense()) {
1326       accountSet.addAccountType(eMyMoney::Account::Type::Income);
1327       accountSet.addAccountType(eMyMoney::Account::Type::Expense);
1328     }
1329 
1330     accountSet.load(d->m_moveToAccountSelector);
1331     // remove those accounts that we currently reference
1332     // with the selected items
1333     foreach (const auto selection, d->m_selectedTransactions) {
1334       d->m_moveToAccountSelector->removeItem(selection.split().accountId());
1335     }
1336     // remove those accounts from the list that are denominated
1337     // in a different currency
1338     auto list = d->m_moveToAccountSelector->accountList();
1339     QList<QString>::const_iterator it_a;
1340     for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) {
1341       auto acc = MyMoneyFile::instance()->account(*it_a);
1342       if (acc.currencyId() != d->m_currentAccount.currencyId())
1343         d->m_moveToAccountSelector->removeItem((*it_a));
1344     }
1345   }
1346 }
1347 
slotObjectDestroyed(QObject * o)1348 void KGlobalLedgerView::slotObjectDestroyed(QObject* o)
1349 {
1350   Q_D(KGlobalLedgerView);
1351   if (o == d->m_moveToAccountSelector) {
1352     d->m_moveToAccountSelector = nullptr;
1353   }
1354 }
1355 
slotCancelOrEnterTransactions(bool & okToSelect)1356 void KGlobalLedgerView::slotCancelOrEnterTransactions(bool& okToSelect)
1357 {
1358   Q_D(KGlobalLedgerView);
1359   static bool oneTime = false;
1360   if (!oneTime) {
1361     oneTime = true;
1362     auto dontShowAgain = "CancelOrEditTransaction";
1363     // qDebug("KMyMoneyApp::slotCancelOrEndEdit");
1364     if (d->m_transactionEditor) {
1365       if (KMyMoneySettings::focusChangeIsEnter() && pActions[Action::EnterTransaction]->isEnabled()) {
1366         slotEnterTransaction();
1367         if (d->m_transactionEditor) {
1368           // if at this stage the editor is still there that means that entering the transaction was cancelled
1369           // for example by pressing cancel on the exchange rate editor so we must stay in edit mode
1370           okToSelect = false;
1371         }
1372       } else {
1373         // okToSelect is preset to true if a cancel of the dialog is useful and false if it is not
1374         int rc;
1375         KGuiItem noGuiItem = KStandardGuiItem::save();
1376         KGuiItem yesGuiItem = KStandardGuiItem::discard();
1377         KGuiItem cancelGuiItem = KStandardGuiItem::cont();
1378 
1379         // if the transaction can't be entered make sure that it can't be entered by pressing no either
1380         if (!pActions[Action::EnterTransaction]->isEnabled()) {
1381           noGuiItem.setEnabled(false);
1382           noGuiItem.setToolTip(pActions[Action::EnterTransaction]->toolTip());
1383         }
1384 
1385         // in case we have a new transaction and cannot save it we simply cancel
1386         if (!pActions[Action::EnterTransaction]->isEnabled() && d->m_transactionEditor && d->m_transactionEditor->createNewTransaction()) {
1387           rc = KMessageBox::Yes;
1388 
1389         } else if (okToSelect == true) {
1390           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);
1391 
1392         } else {
1393           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);
1394         }
1395 
1396         switch (rc) {
1397           case KMessageBox::Yes:
1398             slotCancelTransaction();
1399             break;
1400           case KMessageBox::No:
1401             slotEnterTransaction();
1402             // make sure that we'll see this message the next time no matter
1403             // if the user has chosen the 'Don't show again' checkbox
1404             KMessageBox::enableMessage(dontShowAgain);
1405             if (d->m_transactionEditor) {
1406               // if at this stage the editor is still there that means that entering the transaction was cancelled
1407               // for example by pressing cancel on the exchange rate editor so we must stay in edit mode
1408               okToSelect = false;
1409             }
1410             break;
1411           case KMessageBox::Cancel:
1412             // make sure that we'll see this message the next time no matter
1413             // if the user has chosen the 'Don't show again' checkbox
1414             KMessageBox::enableMessage(dontShowAgain);
1415             okToSelect = false;
1416             break;
1417         }
1418       }
1419     }
1420     oneTime = false;
1421   }
1422 }
1423 
slotNewSchedule(const MyMoneyTransaction & _t,eMyMoney::Schedule::Occurrence occurrence)1424 void KGlobalLedgerView::slotNewSchedule(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence)
1425 {
1426   KEditScheduleDlg::newSchedule(_t, occurrence);
1427 }
1428 
slotNewTransactionForm(eWidgets::eRegister::Action id)1429 void KGlobalLedgerView::slotNewTransactionForm(eWidgets::eRegister::Action id)
1430 {
1431   Q_D(KGlobalLedgerView);
1432   if (!d->m_inEditMode) {
1433     d->m_action = id;
1434     // since we jump here via code, we have to make sure to react only
1435     // if the action is enabled
1436     if (pActions[Action::NewTransaction]->isEnabled()) {
1437       if (d->createNewTransaction()) {
1438         d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1439         if (d->m_transactionEditor) {
1440           KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1441           KMyMoneyPayeeCombo* payeeEdit = dynamic_cast<KMyMoneyPayeeCombo*>(d->m_transactionEditor->haveWidget("payee"));
1442           if (payeeEdit && !d->m_lastPayeeEnteredId.isEmpty()) {
1443             // in case we entered a new transaction before and used a payee,
1444             // we reuse it here. Save the text to the edit widget, select it
1445             // so that hitting any character will start entering another payee.
1446             payeeEdit->setSelectedItem(d->m_lastPayeeEnteredId);
1447             payeeEdit->lineEdit()->selectAll();
1448           }
1449           if (d->m_transactionEditor) {
1450             connect(d->m_transactionEditor.data(), &TransactionEditor::statusProgress, this, &KGlobalLedgerView::slotStatusProgress);
1451             connect(d->m_transactionEditor.data(), &TransactionEditor::statusMsg, this, &KGlobalLedgerView::slotStatusMsg);
1452             connect(d->m_transactionEditor.data(), &TransactionEditor::scheduleTransaction, this, &KGlobalLedgerView::slotNewSchedule);
1453           }
1454           updateLedgerActionsInternal();
1455 //          emit transactionsSelected(d->m_selectedTransactions);
1456         }
1457       }
1458     }
1459   }
1460 }
1461 
slotNewTransaction()1462 void KGlobalLedgerView::slotNewTransaction()
1463 {
1464   // in case the view is not visible ...
1465   if (!isVisible()) {
1466     // we switch to it
1467     pActions[Action::ShowLedgersView]->activate(QAction::ActionEvent::Trigger);
1468     QString tooltip;
1469     if (!canCreateTransactions(tooltip)) {
1470       // and inform the user via a dialog about the reason
1471       // why a transaction cannot be created
1472       KMessageBox::sorry(this, tooltip);
1473       return;
1474     }
1475   }
1476   slotNewTransactionForm(eWidgets::eRegister::Action::None);
1477 }
1478 
slotEditTransaction()1479 void KGlobalLedgerView::slotEditTransaction()
1480 {
1481   Q_D(KGlobalLedgerView);
1482   // qDebug("KMyMoneyApp::slotTransactionsEdit()");
1483   // since we jump here via code, we have to make sure to react only
1484   // if the action is enabled
1485   if (pActions[Action::EditTransaction]->isEnabled()) {
1486     // as soon as we edit a transaction, we don't remember the last payee entered
1487     d->m_lastPayeeEnteredId.clear();
1488     d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1489     KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1490     updateLedgerActionsInternal();
1491   }
1492 }
1493 
slotDeleteTransaction()1494 void KGlobalLedgerView::slotDeleteTransaction()
1495 {
1496   Q_D(KGlobalLedgerView);
1497   // since we may jump here via code, we have to make sure to react only
1498   // if the action is enabled
1499   if (!pActions[Action::DeleteTransaction]->isEnabled())
1500     return;
1501   if (d->m_selectedTransactions.isEmpty())
1502     return;
1503   if (d->m_selectedTransactions.warnLevel() == 1) {
1504     if (KMessageBox::warningContinueCancel(this,
1505                                            i18n("At least one split of the selected transactions has been reconciled. "
1506                                                 "Do you wish to delete the transactions anyway?"),
1507                                            i18n("Transaction already reconciled")) == KMessageBox::Cancel)
1508       return;
1509   }
1510   auto msg =
1511       i18np("Do you really want to delete the selected transaction?",
1512             "Do you really want to delete all %1 selected transactions?",
1513             d->m_selectedTransactions.count());
1514 
1515   if (KMessageBox::questionYesNo(this, msg, i18n("Delete transaction")) == KMessageBox::Yes) {
1516     //KMSTATUS(i18n("Deleting transactions"));
1517     d->doDeleteTransactions();
1518   }
1519 }
1520 
slotDuplicateTransaction(bool reverse)1521 void KGlobalLedgerView::slotDuplicateTransaction(bool reverse)
1522 {
1523   Q_D(KGlobalLedgerView);
1524   // since we may jump here via code, we have to make sure to react only
1525   // if the action is enabled
1526   if (pActions[Action::DuplicateTransaction]->isEnabled()) {
1527     KMyMoneyRegister::SelectedTransactions selectionList = d->m_selectedTransactions;
1528     KMyMoneyRegister::SelectedTransactions::iterator it_t;
1529 
1530     int i = 0;
1531     int cnt = d->m_selectedTransactions.count();
1532     //    KMSTATUS(i18n("Duplicating transactions"));
1533     emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress);
1534     MyMoneyFileTransaction ft;
1535     MyMoneyTransaction lt;
1536     try {
1537       foreach (const auto selection, selectionList) {
1538         auto t = selection.transaction();
1539         // wipe out any reconciliation information
1540         for (auto& split : t.splits()) {
1541           split.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1542           split.setReconcileDate(QDate());
1543           split.setBankID(QString());
1544         }
1545         // clear invalid data
1546         t.setEntryDate(QDate());
1547         t.clearId();
1548 
1549         if (reverse)
1550             // reverse transaction
1551             t.reverse();
1552         else
1553             // set the post date to today
1554             t.setPostDate(QDate::currentDate());
1555 
1556         MyMoneyFile::instance()->addTransaction(t);
1557         lt = t;
1558         emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress);
1559       }
1560       ft.commit();
1561 
1562       // select the new transaction in the ledger
1563       if (!d->m_currentAccount.id().isEmpty())
1564         slotLedgerSelected(d->m_currentAccount.id(), lt.id());
1565     } catch (const MyMoneyException &e) {
1566       KMessageBox::detailedSorry(this, i18n("Unable to duplicate transaction(s)"), QString::fromLatin1(e.what()));
1567     }
1568     // switch off the progress bar
1569     emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress);
1570   }
1571 }
1572 
slotEnterTransaction()1573 void KGlobalLedgerView::slotEnterTransaction()
1574 {
1575   Q_D(KGlobalLedgerView);
1576   // since we jump here via code, we have to make sure to react only
1577   // if the action is enabled
1578   if (pActions[Action::EnterTransaction]->isEnabled()) {
1579     // disable the action while we process it to make sure it's processed only once since
1580     // d->m_transactionEditor->enterTransactions(newId) will run QCoreApplication::processEvents
1581     // we could end up here twice which will cause a crash slotUpdateActions() will enable the action again
1582     pActions[Action::EnterTransaction]->setEnabled(false);
1583     if (d->m_transactionEditor) {
1584       QString accountId = d->m_currentAccount.id();
1585       QString newId;
1586       connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage);
1587       if (d->m_transactionEditor->enterTransactions(newId)) {
1588         KMyMoneyPayeeCombo* payeeEdit = dynamic_cast<KMyMoneyPayeeCombo*>(d->m_transactionEditor->haveWidget("payee"));
1589         if (payeeEdit && !newId.isEmpty()) {
1590           d->m_lastPayeeEnteredId = payeeEdit->selectedItem();
1591         }
1592         d->deleteTransactionEditor();
1593       }
1594       if (!newId.isEmpty()) {
1595         slotLedgerSelected(accountId, newId);
1596       }
1597     }
1598     updateLedgerActionsInternal();
1599   }
1600 }
1601 
slotAcceptTransaction()1602 void KGlobalLedgerView::slotAcceptTransaction()
1603 {
1604   Q_D(KGlobalLedgerView);
1605   KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions;
1606   KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
1607   int cnt = list.count();
1608   int i = 0;
1609   emit selectByVariant(QVariantList {QVariant(0), QVariant(cnt)}, eView::Intent::ReportProgress);
1610   MyMoneyFileTransaction ft;
1611   try {
1612     for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) {
1613       // reload transaction in case it got changed during the course of this loop
1614       MyMoneyTransaction t = MyMoneyFile::instance()->transaction((*it_t).transaction().id());
1615       if (t.isImported()) {
1616         t.setImported(false);
1617         if (!d->m_currentAccount.id().isEmpty()) {
1618           foreach (const auto split, t.splits()) {
1619             if (split.accountId() == d->m_currentAccount.id()) {
1620               if (split.reconcileFlag() == eMyMoney::Split::State::NotReconciled) {
1621                 MyMoneySplit s = split;
1622                 s.setReconcileFlag(eMyMoney::Split::State::Cleared);
1623                 t.modifySplit(s);
1624               }
1625             }
1626           }
1627         }
1628         MyMoneyFile::instance()->modifyTransaction(t);
1629       }
1630       if ((*it_t).split().isMatched()) {
1631         // reload split in case it got changed during the course of this loop
1632         MyMoneySplit s = t.splitById((*it_t).split().id());
1633         TransactionMatcher matcher(d->m_currentAccount);
1634         matcher.accept(t, s);
1635       }
1636       emit selectByVariant(QVariantList {QVariant(i++), QVariant(0)}, eView::Intent::ReportProgress);
1637     }
1638     emit selectByVariant(QVariantList {QVariant(-1), QVariant(-1)}, eView::Intent::ReportProgress);
1639     ft.commit();
1640   } catch (const MyMoneyException &e) {
1641     KMessageBox::detailedSorry(this, i18n("Unable to accept transaction"), QString::fromLatin1(e.what()));
1642   }
1643 }
1644 
slotCancelTransaction()1645 void KGlobalLedgerView::slotCancelTransaction()
1646 {
1647   Q_D(KGlobalLedgerView);
1648   // since we jump here via code, we have to make sure to react only
1649   // if the action is enabled
1650   if (pActions[Action::CancelTransaction]->isEnabled()) {
1651     // make sure, we block the enter function
1652     pActions[Action::EnterTransaction]->setEnabled(false);
1653     // qDebug("KMyMoneyApp::slotTransactionsCancel");
1654     d->deleteTransactionEditor();
1655     updateLedgerActions(d->m_selectedTransactions);
1656     emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1657   }
1658 }
1659 
slotEditSplits()1660 void KGlobalLedgerView::slotEditSplits()
1661 {
1662   Q_D(KGlobalLedgerView);
1663   // since we jump here via code, we have to make sure to react only
1664   // if the action is enabled
1665   if (pActions[Action::EditSplits]->isEnabled()) {
1666     // as soon as we edit a transaction, we don't remember the last payee entered
1667     d->m_lastPayeeEnteredId.clear();
1668     d->m_transactionEditor = d->startEdit(d->m_selectedTransactions);
1669     updateLedgerActions(d->m_selectedTransactions);
1670     emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1671 
1672     if (d->m_transactionEditor) {
1673       KMyMoneyMVCCombo::setSubstringSearchForChildren(this/*d->m_myMoneyView*/, !KMyMoneySettings::stringMatchFromStart());
1674       if (d->m_transactionEditor->slotEditSplits() == QDialog::Accepted) {
1675         MyMoneyFileTransaction ft;
1676         try {
1677           QString id;
1678           connect(d->m_transactionEditor.data(), &TransactionEditor::balanceWarning, d->m_balanceWarning.data(), &KBalanceWarning::slotShowMessage);
1679           d->m_transactionEditor->enterTransactions(id);
1680           ft.commit();
1681         } catch (const MyMoneyException &e) {
1682           KMessageBox::detailedSorry(this, i18n("Unable to modify transaction"), QString::fromLatin1(e.what()));
1683         }
1684       }
1685     }
1686     d->deleteTransactionEditor();
1687     updateLedgerActions(d->m_selectedTransactions);
1688     emit selectByVariant(QVariantList {QVariant::fromValue(d->m_selectedTransactions)}, eView::Intent::SelectRegisterTransactions);
1689   }
1690 }
1691 
slotCopyTransactionToClipboard()1692 void KGlobalLedgerView::slotCopyTransactionToClipboard()
1693 {
1694   Q_D(KGlobalLedgerView);
1695 
1696   // suppress copy transactions if view not visible
1697   // or in edit mode
1698   if (!isVisible() || d->m_inEditMode)
1699     return;
1700 
1701   // format transactions into text
1702   QString txt;
1703   const auto file = MyMoneyFile::instance();
1704   const auto acc = file->account(d->m_lastSelectedAccountID);
1705   const auto currency = file->currency(acc.currencyId());
1706 
1707   foreach (const auto& st, d->m_selectedTransactions) {
1708     if (!txt.isEmpty() || (d->m_selectedTransactions.count() > 1)) {
1709       txt += QStringLiteral("----------------------------\n");
1710     }
1711     try {
1712       const auto& s = st.split();
1713       // Date
1714       txt += i18n("Date: %1", st.transaction().postDate().toString(Qt::DefaultLocaleShortDate));
1715       txt += QStringLiteral("\n");
1716       // Payee
1717       QString payee = i18nc("Name for unknown payee", "Unknown");
1718       if (!s.payeeId().isEmpty()) {
1719         payee = file->payee(s.payeeId()).name();
1720       }
1721       txt += i18n("Payee: %1", payee);
1722       txt += QStringLiteral("\n");
1723       // Amount
1724       txt += i18n("Amount: %1", s.value().formatMoney(currency.tradingSymbol(),  MyMoneyMoney::denomToPrec(acc.fraction(currency))));
1725       txt += QStringLiteral("\n");
1726       // Memo
1727       txt += i18n("Memo: %1", s.memo());
1728       txt += QStringLiteral("\n");
1729 
1730     } catch (MyMoneyException &) {
1731       qDebug() << "Cannot copy transaction" << st.transaction().id() << "to clipboard";
1732     }
1733   }
1734   if (d->m_selectedTransactions.count() > 1) {
1735     txt += QStringLiteral("----------------------------\n");
1736   }
1737 
1738   if (!txt.isEmpty()) {
1739     QClipboard *clipboard = QGuiApplication::clipboard();
1740     clipboard->setText(txt);
1741   }
1742 }
1743 
slotCopySplits()1744 void KGlobalLedgerView::slotCopySplits()
1745 {
1746   Q_D(KGlobalLedgerView);
1747   const auto file = MyMoneyFile::instance();
1748 
1749   if (d->m_selectedTransactions.count() >= 2) {
1750     int singleSplitTransactions = 0;
1751     int multipleSplitTransactions = 0;
1752     KMyMoneyRegister::SelectedTransaction selectedSourceTransaction;
1753     foreach (const auto& st, d->m_selectedTransactions) {
1754       switch (st.transaction().splitCount()) {
1755         case 0:
1756           break;
1757         case 1:
1758           singleSplitTransactions++;
1759           break;
1760         default:
1761           selectedSourceTransaction = st;
1762           multipleSplitTransactions++;
1763           break;
1764       }
1765     }
1766     if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) {
1767       MyMoneyFileTransaction ft;
1768       try {
1769         const auto& sourceTransaction = selectedSourceTransaction.transaction();
1770         const auto& sourceSplit = selectedSourceTransaction.split();
1771         foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) {
1772           auto t = st.transaction();
1773 
1774           // don't process the source transaction
1775           if (sourceTransaction.id() == t.id()) {
1776             continue;
1777           }
1778 
1779           const auto& baseSplit = st.split();
1780 
1781           if (t.splitCount() == 1) {
1782             foreach (const auto& split, sourceTransaction.splits()) {
1783               // Don't copy the source split, as we already have that
1784               // as part of the destination transaction
1785               if (split.id() == sourceSplit.id()) {
1786                 continue;
1787               }
1788 
1789               MyMoneySplit sp(split);
1790               // clear the ID and reconciliation state
1791               sp.clearId();
1792               sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1793               sp.setReconcileDate(QDate());
1794 
1795               // in case it is a simple transaction consisting of two splits,
1796               // we can adjust the share and value part of the second split we
1797               // just created. We need to keep a possible price in mind in case
1798               // of different currencies
1799               if (sourceTransaction.splitCount() == 2) {
1800                 sp.setValue(-baseSplit.value());
1801                 sp.setShares(-(baseSplit.shares() * baseSplit.price()));
1802               }
1803               t.addSplit(sp);
1804             }
1805             // check if we need to add/update a VAT assignment
1806             file->updateVAT(t);
1807 
1808             // and store the modified transaction
1809             file->modifyTransaction(t);
1810           }
1811         }
1812         ft.commit();
1813       } catch (const MyMoneyException &) {
1814         qDebug() << "transactionCopySplits() failed";
1815       }
1816     }
1817   }
1818 }
1819 
slotGoToPayee()1820 void KGlobalLedgerView::slotGoToPayee()
1821 {
1822   Q_D(KGlobalLedgerView);
1823   if (!d->m_payeeGoto.isEmpty()) {
1824     try {
1825       QString transactionId;
1826       if (d->m_selectedTransactions.count() == 1) {
1827         transactionId = d->m_selectedTransactions[0].transaction().id();
1828       }
1829       // make sure to pass copies, as d->myMoneyView->slotPayeeSelected() overrides
1830       // d->m_payeeGoto and d->m_currentAccount while calling slotUpdateActions()
1831       QString payeeId = d->m_payeeGoto;
1832       QString accountId = d->m_currentAccount.id();
1833       emit selectByVariant(QVariantList {QVariant(payeeId), QVariant(accountId), QVariant(transactionId)}, eView::Intent::ShowPayee);
1834 //      emit openPayeeRequested(payeeId, accountId, transactionId);
1835     } catch (const MyMoneyException &) {
1836     }
1837   }
1838 }
1839 
slotGoToAccount()1840 void KGlobalLedgerView::slotGoToAccount()
1841 {
1842   Q_D(KGlobalLedgerView);
1843   if (!d->m_accountGoto.isEmpty()) {
1844     try {
1845       QString transactionId;
1846       if (d->m_selectedTransactions.count() == 1) {
1847         transactionId = d->m_selectedTransactions[0].transaction().id();
1848       }
1849       // make sure to pass a copy, as d->myMoneyView->slotLedgerSelected() overrides
1850       // d->m_accountGoto while calling slotUpdateActions()
1851       slotLedgerSelected(d->m_accountGoto, transactionId);
1852     } catch (const MyMoneyException &) {
1853     }
1854   }
1855 }
1856 
slotMatchTransactions()1857 void KGlobalLedgerView::slotMatchTransactions()
1858 {
1859   Q_D(KGlobalLedgerView);
1860   // if the menu action is retrieved it can contain an '&' character for the accelerator causing the comparison to fail if not removed
1861   QString transactionActionText = pActions[Action::MatchTransaction]->text();
1862   transactionActionText.remove('&');
1863   if (transactionActionText == i18nc("Button text for match transaction", "Match"))
1864     d->transactionMatch();
1865   else
1866     d->transactionUnmatch();
1867 }
1868 
slotCombineTransactions()1869 void KGlobalLedgerView::slotCombineTransactions()
1870 {
1871   qDebug("slotTransactionCombine() not implemented yet");
1872 }
1873 
slotToggleReconciliationFlag()1874 void KGlobalLedgerView::slotToggleReconciliationFlag()
1875 {
1876   Q_D(KGlobalLedgerView);
1877   d->markTransaction(eMyMoney::Split::State::Unknown);
1878 }
1879 
slotMarkCleared()1880 void KGlobalLedgerView::slotMarkCleared()
1881 {
1882   Q_D(KGlobalLedgerView);
1883   d->markTransaction(eMyMoney::Split::State::Cleared);
1884 }
1885 
slotMarkReconciled()1886 void KGlobalLedgerView::slotMarkReconciled()
1887 {
1888   Q_D(KGlobalLedgerView);
1889   d->markTransaction(eMyMoney::Split::State::Reconciled);
1890 }
1891 
slotMarkNotReconciled()1892 void KGlobalLedgerView::slotMarkNotReconciled()
1893 {
1894   Q_D(KGlobalLedgerView);
1895   d->markTransaction(eMyMoney::Split::State::NotReconciled);
1896 }
1897 
slotSelectAllTransactions()1898 void KGlobalLedgerView::slotSelectAllTransactions()
1899 {
1900   Q_D(KGlobalLedgerView);
1901   if(d->m_needLoad)
1902     d->init();
1903 
1904   d->m_register->clearSelection();
1905   KMyMoneyRegister::RegisterItem* p = d->m_register->firstItem();
1906   while (p) {
1907     KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
1908     if (t) {
1909       if (t->isVisible() && t->isSelectable() && !t->isScheduled() && !t->id().isEmpty()) {
1910         t->setSelected(true);
1911       }
1912     }
1913     p = p->nextItem();
1914   }
1915   // 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
1916   d->m_register->selectAll();
1917 
1918   // inform everyone else about the selected items
1919   KMyMoneyRegister::SelectedTransactions list(d->m_register);
1920   updateLedgerActions(list);
1921   emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
1922 }
1923 
slotCreateScheduledTransaction()1924 void KGlobalLedgerView::slotCreateScheduledTransaction()
1925 {
1926    Q_D(KGlobalLedgerView);
1927   if (d->m_selectedTransactions.count() == 1) {
1928     // make sure to have the current selected split as first split in the schedule
1929     MyMoneyTransaction t = d->m_selectedTransactions[0].transaction();
1930     MyMoneySplit s = d->m_selectedTransactions[0].split();
1931     QString splitId = s.id();
1932     s.clearId();
1933     s.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1934     s.setReconcileDate(QDate());
1935     t.removeSplits();
1936     t.addSplit(s);
1937     foreach (const auto split, d->m_selectedTransactions[0].transaction().splits()) {
1938       if (split.id() != splitId) {
1939         MyMoneySplit s0 = split;
1940         s0.clearId();
1941         s0.setReconcileFlag(eMyMoney::Split::State::NotReconciled);
1942         s0.setReconcileDate(QDate());
1943         t.addSplit(s0);
1944       }
1945     }
1946     KEditScheduleDlg::newSchedule(t, eMyMoney::Schedule::Occurrence::Monthly);
1947   }
1948 }
1949 
slotAssignNumber()1950 void KGlobalLedgerView::slotAssignNumber()
1951 {
1952   Q_D(KGlobalLedgerView);
1953   if (d->m_transactionEditor)
1954     d->m_transactionEditor->assignNextNumber();
1955 }
1956 
slotStartReconciliation()1957 void KGlobalLedgerView::slotStartReconciliation()
1958 {
1959   Q_D(KGlobalLedgerView);
1960 
1961   // we cannot reconcile standard accounts
1962   if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id())) {
1963     emit selectByObject(d->m_currentAccount, eView::Intent::SynchronizeAccountInLedgersView);
1964     emit selectByObject(d->m_currentAccount, eView::Intent::StartEnteringOverdueScheduledTransactions);
1965     // asynchronous call to KScheduledView::slotEnterOverdueSchedules is made here
1966     // after that all activity should be continued in KGlobalLedgerView::slotContinueReconciliation()
1967   }
1968 }
1969 
slotFinishReconciliation()1970 void KGlobalLedgerView::slotFinishReconciliation()
1971 {
1972   Q_D(KGlobalLedgerView);
1973   const auto file = MyMoneyFile::instance();
1974 
1975   if (!d->m_reconciliationAccount.id().isEmpty()) {
1976     // retrieve list of all transactions that are not reconciled or cleared
1977     QList<QPair<MyMoneyTransaction, MyMoneySplit> > transactionList;
1978     MyMoneyTransactionFilter filter(d->m_reconciliationAccount.id());
1979     filter.addState((int)eMyMoney::TransactionFilter::State::Cleared);
1980     filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled);
1981     filter.setDateFilter(QDate(), d->m_endingBalanceDlg->statementDate());
1982     filter.setConsiderCategory(false);
1983     filter.setReportAllSplits(true);
1984     file->transactionList(transactionList, filter);
1985 
1986     auto balance = MyMoneyFile::instance()->balance(d->m_reconciliationAccount.id(), d->m_endingBalanceDlg->statementDate());
1987     MyMoneyMoney actBalance, clearedBalance;
1988     actBalance = clearedBalance = balance;
1989 
1990     // walk the list of transactions to figure out the balance(s)
1991     for (auto it = transactionList.constBegin(); it != transactionList.constEnd(); ++it) {
1992       if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) {
1993         clearedBalance -= (*it).second.shares();
1994       }
1995     }
1996 
1997     if (d->m_endingBalanceDlg->endingBalance() != clearedBalance) {
1998       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"
1999                              "Are you sure you want to finish the reconciliation?");
2000       if (KMessageBox::questionYesNo(this, message, i18n("Confirm end of reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::No)
2001         return;
2002     }
2003 
2004     MyMoneyFileTransaction ft;
2005 
2006     // refresh object
2007     d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2008 
2009     // Turn off reconciliation mode
2010 //    Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2011 //    slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2012 //    d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount);
2013 
2014     // only update the last statement balance here, if we haven't a newer one due
2015     // to download of online statements.
2016     if (d->m_reconciliationAccount.value("lastImportedTransactionDate").isEmpty()
2017         || QDate::fromString(d->m_reconciliationAccount.value("lastImportedTransactionDate"), Qt::ISODate) < d->m_endingBalanceDlg->statementDate()) {
2018       d->m_reconciliationAccount.setValue("lastStatementBalance", d->m_endingBalanceDlg->endingBalance().toString());
2019       // in case we override the last statement balance here, we have to make sure
2020       // that we don't show the online balance anymore, as it might be different
2021       d->m_reconciliationAccount.deletePair("lastImportedTransactionDate");
2022     }
2023     d->m_reconciliationAccount.setLastReconciliationDate(d->m_endingBalanceDlg->statementDate());
2024 
2025     // keep a record of this reconciliation
2026     d->m_reconciliationAccount.addReconciliation(d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance());
2027 
2028     d->m_reconciliationAccount.deletePair("lastReconciledBalance");
2029     d->m_reconciliationAccount.deletePair("statementBalance");
2030     d->m_reconciliationAccount.deletePair("statementDate");
2031 
2032     try {
2033       // update the account data
2034       file->modifyAccount(d->m_reconciliationAccount);
2035 
2036       /*
2037       // collect the list of cleared splits for this account
2038       filter.clear();
2039       filter.addAccount(d->m_reconciliationAccount.id());
2040       filter.addState(eMyMoney::TransactionFilter::Cleared);
2041       filter.setConsiderCategory(false);
2042       filter.setReportAllSplits(true);
2043       file->transactionList(transactionList, filter);
2044       */
2045 
2046       // walk the list of transactions/splits and mark the cleared ones as reconciled
2047 
2048       for (auto it = transactionList.begin(); it != transactionList.end(); ++it) {
2049         MyMoneySplit sp = (*it).second;
2050         // skip the ones that are not marked cleared
2051         if (sp.reconcileFlag() != eMyMoney::Split::State::Cleared)
2052           continue;
2053 
2054         // always retrieve a fresh copy of the transaction because we
2055         // might have changed it already with another split
2056         MyMoneyTransaction t = file->transaction((*it).first.id());
2057         sp.setReconcileFlag(eMyMoney::Split::State::Reconciled);
2058         sp.setReconcileDate(d->m_endingBalanceDlg->statementDate());
2059         t.modifySplit(sp);
2060 
2061         // update the engine ...
2062         file->modifyTransaction(t);
2063 
2064         // ... and the list
2065         (*it) = qMakePair(t, sp);
2066       }
2067       ft.commit();
2068 
2069       // reload account data from engine as the data might have changed in the meantime
2070       d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2071 
2072       /**
2073         * This signal is emitted when an account has been successfully reconciled
2074         * and all transactions are updated in the engine. It can be used by plugins
2075         * to create reconciliation reports.
2076         *
2077         * @param account the account data
2078         * @param date the reconciliation date as provided through the dialog
2079         * @param startingBalance the starting balance as provided through the dialog
2080         * @param endingBalance the ending balance as provided through the dialog
2081         * @param transactionList reference to QList of QPair containing all
2082         *        transaction/split pairs processed by the reconciliation.
2083         */
2084       emit selectByVariant(QVariantList {
2085                              QVariant::fromValue(d->m_reconciliationAccount),
2086                              QVariant::fromValue(d->m_endingBalanceDlg->statementDate()),
2087                              QVariant::fromValue(d->m_endingBalanceDlg->previousBalance()),
2088                              QVariant::fromValue(d->m_endingBalanceDlg->endingBalance()),
2089                              QVariant::fromValue(transactionList)
2090                            }, eView::Intent::AccountReconciled);
2091 
2092     } catch (const MyMoneyException &) {
2093       qDebug("Unexpected exception when setting cleared to reconcile");
2094     }
2095     // Turn off reconciliation mode
2096     Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2097     slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2098   }
2099   // Turn off reconciliation mode
2100   d->m_reconciliationAccount = MyMoneyAccount();
2101   updateActions(d->m_currentAccount);
2102   updateLedgerActionsInternal();
2103   d->loadView();
2104 //  slotUpdateActions();
2105 }
2106 
slotPostponeReconciliation()2107 void KGlobalLedgerView::slotPostponeReconciliation()
2108 {
2109   Q_D(KGlobalLedgerView);
2110   MyMoneyFileTransaction ft;
2111   const auto file = MyMoneyFile::instance();
2112 
2113   if (!d->m_reconciliationAccount.id().isEmpty()) {
2114     // refresh object
2115     d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2116 
2117     // Turn off reconciliation mode
2118 //    Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2119 //    slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2120 //    d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount);
2121 
2122     d->m_reconciliationAccount.setValue("lastReconciledBalance", d->m_endingBalanceDlg->previousBalance().toString());
2123     d->m_reconciliationAccount.setValue("statementBalance", d->m_endingBalanceDlg->endingBalance().toString());
2124     d->m_reconciliationAccount.setValue("statementDate", d->m_endingBalanceDlg->statementDate().toString(Qt::ISODate));
2125 
2126     try {
2127       file->modifyAccount(d->m_reconciliationAccount);
2128       ft.commit();
2129       d->m_reconciliationAccount = MyMoneyAccount();
2130       updateActions(d->m_currentAccount);
2131       updateLedgerActionsInternal();
2132 //      slotUpdateActions();
2133     } catch (const MyMoneyException &) {
2134       qDebug("Unexpected exception when setting last reconcile info into account");
2135       ft.rollback();
2136       d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id());
2137     }
2138     // Turn off reconciliation mode
2139     Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2140     slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
2141     d->loadView();
2142   }
2143 }
2144 
slotOpenAccount()2145 void KGlobalLedgerView::slotOpenAccount()
2146 {
2147   Q_D(KGlobalLedgerView);
2148   if (!MyMoneyFile::instance()->isStandardAccount(d->m_currentAccount.id()))
2149     slotLedgerSelected(d->m_currentAccount.id(), QString());
2150 }
2151 
slotFindTransaction()2152 void KGlobalLedgerView::slotFindTransaction()
2153 {
2154   Q_D(KGlobalLedgerView);
2155   if (!d->m_searchDlg) {
2156     d->m_searchDlg = new KFindTransactionDlg(this);
2157     connect(d->m_searchDlg, &QObject::destroyed, this, &KGlobalLedgerView::slotCloseSearchDialog);
2158     connect(d->m_searchDlg, &KFindTransactionDlg::transactionSelected,
2159             this, &KGlobalLedgerView::slotLedgerSelected);
2160   }
2161   d->m_searchDlg->show();
2162   d->m_searchDlg->raise();
2163   d->m_searchDlg->activateWindow();
2164 }
2165 
slotCloseSearchDialog()2166 void KGlobalLedgerView::slotCloseSearchDialog()
2167 {
2168   Q_D(KGlobalLedgerView);
2169   if (d->m_searchDlg)
2170     d->m_searchDlg->deleteLater();
2171   d->m_searchDlg = nullptr;
2172 }
2173 
slotStatusMsg(const QString & txt)2174 void KGlobalLedgerView::slotStatusMsg(const QString& txt)
2175 {
2176   emit selectByVariant(QVariantList {QVariant(txt)}, eView::Intent::ReportProgressMessage);
2177 }
2178 
slotStatusProgress(int cnt,int base)2179 void KGlobalLedgerView::slotStatusProgress(int cnt, int base)
2180 {
2181   emit selectByVariant(QVariantList {QVariant(cnt), QVariant(base)}, eView::Intent::ReportProgress);
2182 }
2183 
slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions & list)2184 void KGlobalLedgerView::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list)
2185 {
2186   updateLedgerActions(list);
2187   emit selectByVariant(QVariantList {QVariant::fromValue(list)}, eView::Intent::SelectRegisterTransactions);
2188 }
2189