1 /***************************************************************************
2                           kscheduledview_p.h  -  description
3                              -------------------
4     begin                : Sun Jan 27 2002
5     copyright            : (C) 2000-2002 by Michael Edwardes
6     email                : mte@users.sourceforge.net
7                            Javier Campos Morales <javi_c@users.sourceforge.net>
8                            Felix Rodriguez <frodriguez@users.sourceforge.net>
9                            John C <thetacoturtle@users.sourceforge.net>
10                            Thomas Baumgart <ipwizard@users.sourceforge.net>
11                            Kevin Tambascio <ktambascio@users.sourceforge.net>
12                            (C) 2017 by Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
13  ***************************************************************************/
14 
15 /***************************************************************************
16  *                                                                         *
17  *   This program is free software; you can redistribute it and/or modify  *
18  *   it under the terms of the GNU General Public License as published by  *
19  *   the Free Software Foundation; either version 2 of the License, or     *
20  *   (at your option) any later version.                                   *
21  *                                                                         *
22  ***************************************************************************/
23 
24 #ifndef KSCHEDULEDVIEW_P_H
25 #define KSCHEDULEDVIEW_P_H
26 
27 #include "kscheduledview.h"
28 
29 // ----------------------------------------------------------------------------
30 // QT Includes
31 
32 #include <QList>
33 #include <QTimer>
34 #include <QPushButton>
35 #include <QMenu>
36 #include <QIcon>
37 #include <QScopedPointer>
38 #include <QDebug>
39 
40 // ----------------------------------------------------------------------------
41 // KDE Includes
42 
43 #include <KLocalizedString>
44 #include <KConfig>
45 #include <KMessageBox>
46 #include <KSharedConfig>
47 #include <KTreeWidgetSearchLine>
48 #include <KTreeWidgetSearchLineWidget>
49 
50 // ----------------------------------------------------------------------------
51 // Project Includes
52 
53 #include "ui_kscheduledview.h"
54 #include "kmymoneyviewbase_p.h"
55 #include "kenterscheduledlg.h"
56 #include "kbalancewarning.h"
57 #include "transactioneditor.h"
58 #include "kconfirmmanualenterdlg.h"
59 #include "kmymoneymvccombo.h"
60 #include "kmymoneyutils.h"
61 #include "kmymoneysettings.h"
62 #include "mymoneyexception.h"
63 #include "kscheduletreeitem.h"
64 #include "ktreewidgetfilterlinewidget.h"
65 #include "icons/icons.h"
66 #include "mymoneyutils.h"
67 #include "mymoneyaccount.h"
68 #include "mymoneymoney.h"
69 #include "mymoneysecurity.h"
70 #include "mymoneyschedule.h"
71 #include "mymoneyfile.h"
72 #include "mymoneypayee.h"
73 #include "mymoneysplit.h"
74 #include "mymoneytransaction.h"
75 #include "mymoneyenums.h"
76 #include "menuenums.h"
77 #include "dialogenums.h"
78 
79 using namespace Icons;
80 
81 class KScheduledViewPrivate : public KMyMoneyViewBasePrivate
82 {
Q_DECLARE_PUBLIC(KScheduledView)83   Q_DECLARE_PUBLIC(KScheduledView)
84 
85 public:
86   explicit KScheduledViewPrivate(KScheduledView *qq) :
87     KMyMoneyViewBasePrivate(),
88     q_ptr(qq),
89     ui(new Ui::KScheduledView),
90     m_kaccPopup(nullptr),
91     m_openBills(true),
92     m_openDeposits(true),
93     m_openTransfers(true),
94     m_openLoans(true),
95     m_needLoad(true),
96     m_searchWidget(nullptr),
97     m_balanceWarning(nullptr)
98   {
99   }
100 
~KScheduledViewPrivate()101   ~KScheduledViewPrivate()
102   {
103     if(!m_needLoad)
104       writeConfig();
105     delete ui;
106   }
107 
init()108   void init()
109   {
110     Q_Q(KScheduledView);
111     m_needLoad = false;
112     ui->setupUi(q);
113 
114     // create the searchline widget
115     // and insert it into the existing layout
116     m_searchWidget = new KTreeWidgetFilterLineWidget(q, ui->m_scheduleTree);
117     ui->vboxLayout->insertWidget(1, m_searchWidget);
118 
119     //enable custom context menu
120     ui->m_scheduleTree->setContextMenuPolicy(Qt::CustomContextMenu);
121     ui->m_scheduleTree->setSelectionMode(QAbstractItemView::SingleSelection);
122 
123     readConfig();
124 
125     q->connect(ui->m_qbuttonNew, &QAbstractButton::clicked, pActions[eMenu::Action::NewSchedule], &QAction::trigger);
126 
127     // attach popup to 'Filter...' button
128     m_kaccPopup = new QMenu(q);
129     ui->m_accountsCombo->setMenu(m_kaccPopup);
130     q->connect(m_kaccPopup, &QMenu::triggered, q, &KScheduledView::slotAccountActivated);
131 
132     KGuiItem::assign(ui->m_qbuttonNew, KMyMoneyUtils::scheduleNewGuiItem());
133     KGuiItem::assign(ui->m_accountsCombo, KMyMoneyUtils::accountsFilterGuiItem());
134 
135     q->connect(ui->m_scheduleTree, &QWidget::customContextMenuRequested, q, &KScheduledView::customContextMenuRequested);
136     q->connect(ui->m_scheduleTree, &QTreeWidget::itemSelectionChanged,
137             q, &KScheduledView::slotSetSelectedItem);
138 
139     q->connect(ui->m_scheduleTree, &QTreeWidget::itemDoubleClicked,
140             q, &KScheduledView::slotListItemExecuted);
141     q->connect(ui->m_scheduleTree, &QTreeWidget::itemExpanded,
142             q, &KScheduledView::slotListViewExpanded);
143     q->connect(ui->m_scheduleTree, &QTreeWidget::itemCollapsed,
144             q, &KScheduledView::slotListViewCollapsed);
145 
146     q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KScheduledView::refresh);
147   }
148 
accountNameLessThan(const MyMoneyAccount & acc1,const MyMoneyAccount & acc2)149   static bool accountNameLessThan(const MyMoneyAccount& acc1, const MyMoneyAccount& acc2)
150   {
151     return acc1.name().toLower() < acc2.name().toLower();
152   }
153 
refreshSchedule(bool full,const QString & schedId)154   void refreshSchedule(bool full, const QString& schedId)
155   {
156     Q_Q(KScheduledView);
157     ui->m_scheduleTree->header()->setFont(KMyMoneySettings::listHeaderFontEx());
158 
159     ui->m_scheduleTree->clear();
160 
161     try {
162       if (full) {
163         try {
164           m_kaccPopup->clear();
165 
166           MyMoneyFile* file = MyMoneyFile::instance();
167 
168           // extract a list of all accounts under the asset group
169           // and sort them by name
170           QList<MyMoneyAccount> list;
171           QStringList accountList = file->asset().accountList();
172           accountList.append(file->liability().accountList());
173           file->accountList(list, accountList, true);
174           qStableSort(list.begin(), list.end(), accountNameLessThan);
175 
176           QList<MyMoneyAccount>::ConstIterator it_a;
177           for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) {
178             if (!(*it_a).isClosed()) {
179               QAction* act;
180               act = m_kaccPopup->addAction((*it_a).name());
181               act->setCheckable(true);
182               act->setChecked(true);
183             }
184           }
185 
186         } catch (const MyMoneyException &e) {
187           KMessageBox::detailedError(q, i18n("Unable to load accounts: "), e.what());
188         }
189       }
190 
191       MyMoneyFile *file = MyMoneyFile::instance();
192       QList<MyMoneySchedule> scheduledItems = file->scheduleList();
193 
194       if (scheduledItems.count() == 0)
195         return;
196 
197       //disable sorting for performance
198       ui->m_scheduleTree->setSortingEnabled(false);
199 
200       KScheduleTreeItem *itemBills = new KScheduleTreeItem(ui->m_scheduleTree);
201       itemBills->setIcon(0, Icons::get(Icon::Expense));
202       itemBills->setText(0, i18n("Bills"));
203       itemBills->setData(0, KScheduleTreeItem::OrderRole, QVariant("0"));
204       itemBills->setFirstColumnSpanned(true);
205       itemBills->setFlags(Qt::ItemIsEnabled);
206       QFont bold = itemBills->font(0);
207       bold.setBold(true);
208       itemBills->setFont(0, bold);
209       KScheduleTreeItem *itemDeposits = new KScheduleTreeItem(ui->m_scheduleTree);
210       itemDeposits->setIcon(0, Icons::get(Icon::Income));
211       itemDeposits->setText(0, i18n("Deposits"));
212       itemDeposits->setData(0, KScheduleTreeItem::OrderRole, QVariant("1"));
213       itemDeposits->setFirstColumnSpanned(true);
214       itemDeposits->setFlags(Qt::ItemIsEnabled);
215       itemDeposits->setFont(0, bold);
216       KScheduleTreeItem *itemLoans = new KScheduleTreeItem(ui->m_scheduleTree);
217       itemLoans->setIcon(0, Icons::get(Icon::Loan));
218       itemLoans->setText(0, i18n("Loans"));
219       itemLoans->setData(0, KScheduleTreeItem::OrderRole, QVariant("2"));
220       itemLoans->setFirstColumnSpanned(true);
221       itemLoans->setFlags(Qt::ItemIsEnabled);
222       itemLoans->setFont(0, bold);
223       KScheduleTreeItem *itemTransfers = new KScheduleTreeItem(ui->m_scheduleTree);
224       itemTransfers->setIcon(0, Icons::get(Icon::Transaction));
225       itemTransfers->setText(0, i18n("Transfers"));
226       itemTransfers->setData(0, KScheduleTreeItem::OrderRole, QVariant("3"));
227       itemTransfers->setFirstColumnSpanned(true);
228       itemTransfers->setFlags(Qt::ItemIsEnabled);
229       itemTransfers->setFont(0, bold);
230 
231       QList<MyMoneySchedule>::Iterator it;
232 
233       QTreeWidgetItem *openItem = 0;
234 
235       for (it = scheduledItems.begin(); it != scheduledItems.end(); ++it) {
236         MyMoneySchedule schedData = (*it);
237         QTreeWidgetItem* item = 0;
238 
239         bool bContinue = true;
240         QStringList::iterator accIt;
241         for (accIt = m_filterAccounts.begin(); accIt != m_filterAccounts.end(); ++accIt) {
242           if (*accIt == schedData.account().id()) {
243             bContinue = false; // Filter it out
244             break;
245           }
246         }
247 
248         if (!bContinue)
249           continue;
250 
251         QTreeWidgetItem* parent = 0;
252         switch (schedData.type()) {
253           case eMyMoney::Schedule::Type::Any:
254             // Should we display an error ?
255             // We just sort it as bill and fall through here
256 
257           case eMyMoney::Schedule::Type::Bill:
258             parent = itemBills;
259             break;
260 
261           case eMyMoney::Schedule::Type::Deposit:
262             parent = itemDeposits;
263             break;
264 
265           case eMyMoney::Schedule::Type::Transfer:
266             parent = itemTransfers;
267             break;
268 
269           case eMyMoney::Schedule::Type::LoanPayment:
270             parent = itemLoans;
271             break;
272 
273         }
274         if (parent) {
275           if (!KMyMoneySettings::hideFinishedSchedules() || !schedData.isFinished()) {
276             item = addScheduleItem(parent, schedData);
277             if (schedData.id() == schedId)
278               openItem = item;
279           }
280         }
281       }
282 
283       if (openItem) {
284         ui->m_scheduleTree->setCurrentItem(openItem);
285       }
286       // using a timeout is the only way, I got the 'ensureTransactionVisible'
287       // working when coming from hidden form to visible form. I assume, this
288       // has something to do with the delayed update of the display somehow.
289       q->resize(q->width(), q->height() - 1);
290       QTimer::singleShot(10, q, SLOT(slotTimerDone()));
291       ui->m_scheduleTree->update();
292 
293       // force repaint in case the filter is set
294       m_searchWidget->searchLine()->updateSearch(QString());
295 
296       if (m_openBills)
297         itemBills->setExpanded(true);
298 
299       if (m_openDeposits)
300         itemDeposits->setExpanded(true);
301 
302       if (m_openTransfers)
303         itemTransfers->setExpanded(true);
304 
305       if (m_openLoans)
306         itemLoans->setExpanded(true);
307 
308     } catch (const MyMoneyException &e) {
309       KMessageBox::error(q, e.what());
310     }
311 
312     for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
313       ui->m_scheduleTree->resizeColumnToContents(i);
314     }
315 
316     //reenable sorting after loading items
317     ui->m_scheduleTree->setSortingEnabled(true);
318   }
319 
readConfig()320   void readConfig()
321   {
322     KSharedConfigPtr config = KSharedConfig::openConfig();
323     KConfigGroup grp = config->group("Last Use Settings");
324     m_openBills = grp.readEntry("KScheduleView_openBills", true);
325     m_openDeposits = grp.readEntry("KScheduleView_openDeposits", true);
326     m_openTransfers = grp.readEntry("KScheduleView_openTransfers", true);
327     m_openLoans = grp.readEntry("KScheduleView_openLoans", true);
328     QByteArray columns;
329     columns = grp.readEntry("KScheduleView_treeState", columns);
330     ui->m_scheduleTree->header()->restoreState(columns);
331     ui->m_scheduleTree->header()->setFont(KMyMoneySettings::listHeaderFontEx());
332   }
333 
writeConfig()334   void writeConfig()
335   {
336     KSharedConfigPtr config = KSharedConfig::openConfig();
337     KConfigGroup grp = config->group("Last Use Settings");
338     grp.writeEntry("KScheduleView_openBills", m_openBills);
339     grp.writeEntry("KScheduleView_openDeposits", m_openDeposits);
340     grp.writeEntry("KScheduleView_openTransfers", m_openTransfers);
341     grp.writeEntry("KScheduleView_openLoans", m_openLoans);
342     QByteArray columns = ui->m_scheduleTree->header()->saveState();
343     grp.writeEntry("KScheduleView_treeState", columns);
344 
345     config->sync();
346   }
347 
addScheduleItem(QTreeWidgetItem * parent,MyMoneySchedule & schedule)348   QTreeWidgetItem* addScheduleItem(QTreeWidgetItem* parent, MyMoneySchedule& schedule)
349   {
350     KScheduleTreeItem* item = new KScheduleTreeItem(parent);
351     item->setData(0, Qt::UserRole, QVariant::fromValue(schedule));
352     item->setData(0, KScheduleTreeItem::OrderRole, schedule.name());
353     if (!schedule.isFinished()) {
354       if (schedule.isOverdue()) {
355         item->setIcon(0, Icons::get(Icon::UpcomingEvents));
356         QBrush brush = item->foreground(0);
357         brush.setColor(Qt::red);
358         for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
359           item->setForeground(i, brush);
360         }
361       } else {
362         item->setIcon(0, Icons::get(Icon::CalendarDay));
363       }
364     } else {
365       item->setIcon(0, Icons::get(Icon::DialogClose));
366       QBrush brush = item->foreground(0);
367       brush.setColor(Qt::darkGreen);
368       for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
369         item->setForeground(i, brush);
370       }
371     }
372 
373     try {
374       MyMoneyTransaction transaction = schedule.transaction();
375       MyMoneySplit s1 = (transaction.splits().size() < 1) ? MyMoneySplit() : transaction.splits()[0];
376       MyMoneySplit s2 = (transaction.splits().size() < 2) ? MyMoneySplit() : transaction.splits()[1];
377       MyMoneySplit split;
378       MyMoneyAccount acc;
379 
380       switch (schedule.type()) {
381       case eMyMoney::Schedule::Type::Deposit:
382         if (s1.value().isNegative())
383           split = s2;
384         else
385           split = s1;
386         break;
387 
388       case eMyMoney::Schedule::Type::LoanPayment:
389       {
390         auto found = false;
391         foreach (const auto it_split, transaction.splits()) {
392           acc = MyMoneyFile::instance()->account(it_split.accountId());
393           if (acc.accountGroup() == eMyMoney::Account::Type::Asset
394               || acc.accountGroup() == eMyMoney::Account::Type::Liability) {
395             if (acc.accountType() != eMyMoney::Account::Type::Loan
396                 && acc.accountType() != eMyMoney::Account::Type::AssetLoan) {
397               split = it_split;
398               found = true;
399               break;
400             }
401           }
402         }
403         if (!found) {
404           qWarning("Split for payment account not found in %s:%d.", __FILE__, __LINE__);
405         }
406         break;
407       }
408       default:
409         if (!s1.value().isPositive())
410           split = s1;
411         else
412           split = s2;
413         break;
414       }
415       acc = MyMoneyFile::instance()->account(split.accountId());
416 
417       item->setText(0, schedule.name());
418       MyMoneySecurity currency = MyMoneyFile::instance()->currency(acc.currencyId());
419 
420       QString accName =  acc.name();
421       if (!accName.isEmpty()) {
422         item->setText(1, accName);
423       } else {
424         item->setText(1, "---");
425       }
426       item->setData(1, KScheduleTreeItem::OrderRole, QVariant(accName));
427 
428       QString payeeName;
429       if (!s1.payeeId().isEmpty()) {
430         payeeName = MyMoneyFile::instance()->payee(s1.payeeId()).name();
431         item->setText(2, payeeName);
432       } else {
433         item->setText(2, "---");
434       }
435       item->setData(2, KScheduleTreeItem::OrderRole, QVariant(payeeName));
436 
437       MyMoneyMoney amount = split.shares().abs();
438       item->setData(3, Qt::UserRole, QVariant::fromValue(amount));
439       if (!accName.isEmpty()) {
440         item->setText(3, QString("%1  ").arg(MyMoneyUtils::formatMoney(amount, acc, currency)));
441       } else {
442         //there are some cases where the schedule does not have an account
443         //in those cases the account will not have a fraction
444         //use base currency instead
445         item->setText(3, QString("%1  ").arg(MyMoneyUtils::formatMoney(amount, MyMoneyFile::instance()->baseCurrency())));
446       }
447       item->setTextAlignment(3, Qt::AlignRight | Qt::AlignVCenter);
448       item->setData(3, KScheduleTreeItem::OrderRole, QVariant::fromValue(amount));
449 
450       // Do the real next payment like ms-money etc
451       QDate nextDueDate;
452       if (schedule.isFinished()) {
453         item->setText(4, i18nc("Finished schedule", "Finished"));
454       } else {
455         nextDueDate = schedule.adjustedNextDueDate();
456         item->setText(4, QLocale().toString(schedule.adjustedNextDueDate(), QLocale::ShortFormat));
457       }
458       item->setData(4, KScheduleTreeItem::OrderRole, QVariant(nextDueDate));
459       item->setText(5, i18nc("Frequency of schedule", schedule.occurrenceToString().toLatin1()));
460       item->setText(6, KMyMoneyUtils::paymentMethodToString(schedule.paymentType()));
461     } catch (const MyMoneyException &e) {
462       item->setText(0, "Error:");
463       item->setText(1, e.what());
464     }
465     return item;
466   }
467 
468   /**
469     * This method allows to enter the next scheduled transaction of
470     * the given schedule @a s. In case @a extendedKeys is @a true,
471     * the given schedule can also be skipped or ignored.
472     * If @a autoEnter is @a true and the schedule does not contain
473     * an estimated value, the schedule is entered as is without further
474     * interaction with the user. In all other cases, the user will
475     * be presented a dialog and allowed to adjust the values for this
476     * instance of the schedule.
477     *
478     * The transaction will be created and entered into the ledger
479     * and the schedule updated.
480     */
481   eDialogs::ScheduleResultCode enterSchedule(MyMoneySchedule& schedule, bool autoEnter = false, bool extendedKeys = false)
482   {
483     Q_Q(KScheduledView);
484     auto rc = eDialogs::ScheduleResultCode::Cancel;
485     if (!schedule.id().isEmpty()) {
486       try {
487         schedule = MyMoneyFile::instance()->schedule(schedule.id());
catch(const MyMoneyException & e)488       } catch (const MyMoneyException &e) {
489         KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
490         return rc;
491       }
492 
493       QWidget* parent = QApplication::activeWindow();
494       QPointer<KEnterScheduleDlg> dlg = new KEnterScheduleDlg(parent, schedule);
495 
496       try {
497         QDate origDueDate = schedule.nextDueDate();
498 
499         dlg->showExtendedKeys(extendedKeys);
500 
501         QPointer<TransactionEditor> transactionEditor = dlg->startEdit();
502         if (transactionEditor) {
503           KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart());
504           MyMoneyTransaction torig, taccepted;
505           transactionEditor->createTransaction(torig, dlg->transaction(),
506               schedule.transaction().splits().isEmpty() ? MyMoneySplit() : schedule.transaction().splits().front(), true);
507           // force actions to be available no matter what (will be updated according to the state during
508           // slotTransactionsEnter or slotTransactionsCancel)
509 
510           KConfirmManualEnterDlg::Action action = KConfirmManualEnterDlg::ModifyOnce;
511           if (!autoEnter || !schedule.isFixed()) {
512             for (; dlg != 0;) {
513               rc = eDialogs::ScheduleResultCode::Cancel;
514               if (dlg->exec() == QDialog::Accepted && dlg != 0) {
515                 pActions[eMenu::Action::CancelTransaction]->setEnabled(true);
516                 pActions[eMenu::Action::EnterTransaction]->setEnabled(true);
517                 rc = dlg->resultCode();
518                 if (rc == eDialogs::ScheduleResultCode::Enter) {
519                   transactionEditor->createTransaction(taccepted, torig, torig.splits().isEmpty() ? MyMoneySplit() : torig.splits().front(), true);
520                   // make sure to suppress comparison of some data: postDate
521                   torig.setPostDate(taccepted.postDate());
522                   if (torig != taccepted) {
523                     QPointer<KConfirmManualEnterDlg> cdlg =
524                       new KConfirmManualEnterDlg(schedule, q);
525                     cdlg->loadTransactions(torig, taccepted);
526                     if (cdlg->exec() == QDialog::Accepted) {
527                       action = cdlg->action();
528                       delete cdlg;
529                       break;
530                     }
531                     delete cdlg;
532                     // the user has chosen 'cancel' during confirmation,
533                     // we go back to the editor
534                     continue;
535                   }
536                 } else if (rc == eDialogs::ScheduleResultCode::Skip) {
537                   slotTransactionsCancel(transactionEditor, schedule);
538                   skipSchedule(schedule);
539                 } else {
540                   slotTransactionsCancel(transactionEditor, schedule);
541                 }
542               } else {
543                 pActions[eMenu::Action::CancelTransaction]->setEnabled(true);
544                 pActions[eMenu::Action::EnterTransaction]->setEnabled(true);
545                 if (autoEnter) {
546                   if (KMessageBox::warningYesNo(q, i18n("Are you sure you wish to stop this scheduled transaction from being entered into the register?\n\nKMyMoney will prompt you again next time it starts unless you manually enter it later.")) == KMessageBox::No) {
547                     // the user has chosen 'No' for the above question,
548                     // we go back to the editor
549                     continue;
550                   }
551                 }
552                 slotTransactionsCancel(transactionEditor, schedule);
553               }
554               break;
555             }
556           }
557 
558           // if we still have the editor around here, the user did not cancel
559           if ((transactionEditor != 0) && (dlg != 0)) {
560             MyMoneyFileTransaction ft;
561             try {
562               MyMoneyTransaction t;
563               // add the new transaction
564               switch (action) {
565                 case KConfirmManualEnterDlg::UseOriginal:
566                   // setup widgets with original transaction data
567                   transactionEditor->setTransaction(dlg->transaction(), dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front());
568                   // and create a transaction based on that data
569                   taccepted = MyMoneyTransaction();
570                   transactionEditor->createTransaction(taccepted, dlg->transaction(),
571                       dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front(), true);
572                   break;
573 
574                 case KConfirmManualEnterDlg::ModifyAlways:
575                   torig = taccepted;
576                   torig.setPostDate(origDueDate);
577                   schedule.setTransaction(torig);
578                   break;
579 
580                 case KConfirmManualEnterDlg::ModifyOnce:
581                   break;
582               }
583 
584               QString newId;
585               q->connect(transactionEditor, SIGNAL(balanceWarning(QWidget*,MyMoneyAccount,QString)), m_balanceWarning.data(), SLOT(slotShowMessage(QWidget*,MyMoneyAccount,QString)));
586               if (transactionEditor->enterTransactions(newId, false)) {
587                 if (!newId.isEmpty()) {
588                   t = MyMoneyFile::instance()->transaction(newId);
589                   schedule.setLastPayment(t.postDate());
590                 }
591                 // in case the next due date is invalid, the schedule is finished
592                 // we mark it as such by setting the next due date to one day past the end
593                 QDate nextDueDate = schedule.nextPayment(origDueDate);
594                 if (!nextDueDate.isValid()) {
595                   schedule.setNextDueDate(schedule.endDate().addDays(1));
596                 } else {
597                   schedule.setNextDueDate(nextDueDate);
598                 }
599                 MyMoneyFile::instance()->modifySchedule(schedule);
600                 rc = eDialogs::ScheduleResultCode::Enter;
601 
602                 // delete the editor before we emit the dataChanged() signal from the
603                 // engine. Calling this twice in a row does not hurt.
604                 delete transactionEditor;
605                 ft.commit();
606               }
catch(const MyMoneyException & e)607             } catch (const MyMoneyException &e) {
608               KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
609             }
610             delete transactionEditor;
611           }
612         }
catch(const MyMoneyException & e)613       } catch (const MyMoneyException &e) {
614         KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
615       }
616       delete dlg;
617     }
618     return rc;
619   }
620 
slotTransactionsCancel(TransactionEditor * editor,const MyMoneySchedule & schedule)621   void slotTransactionsCancel(TransactionEditor* editor, const MyMoneySchedule& schedule)
622   {
623     Q_Q(KScheduledView);
624     // since we jump here via code, we have to make sure to react only
625     // if the action is enabled
626     if (pActions[eMenu::Action::CancelTransaction]->isEnabled()) {
627       // make sure, we block the enter function
628       pActions[eMenu::Action::EnterTransaction]->setEnabled(false);
629       // qDebug("KMyMoneyApp::slotTransactionsCancel");
630       delete editor;
631       emit q->selectByObject(schedule, eView::Intent::None);
632     }
633   }
634 
635   /**
636     * This method allows to skip the next scheduled transaction of
637     * the given schedule @a s.
638     *
639     */
skipSchedule(MyMoneySchedule & schedule)640   void skipSchedule(MyMoneySchedule& schedule)
641   {
642     Q_Q(KScheduledView);
643     const auto parentWidget = QApplication::activeWindow();
644 
645     if (!schedule.id().isEmpty()) {
646       try {
647         schedule = MyMoneyFile::instance()->schedule(schedule.id());
648         if (!schedule.isFinished()) {
649           if (schedule.occurrence() != eMyMoney::Schedule::Occurrence::Once) {
650             QDate next = schedule.nextDueDate();
651             if (!schedule.isFinished() && (KMessageBox::questionYesNo(parentWidget, i18n("<qt>Do you really want to skip the <b>%1</b> transaction scheduled for <b>%2</b>?</qt>", schedule.name(), QLocale().toString(next, QLocale::ShortFormat)))) == KMessageBox::Yes) {
652               MyMoneyFileTransaction ft;
653               schedule.setLastPayment(next);
654               schedule.setNextDueDate(schedule.nextPayment(next));
655               MyMoneyFile::instance()->modifySchedule(schedule);
656               ft.commit();
657             }
658           }
659         }
660       } catch (const MyMoneyException &e) {
661         KMessageBox::detailedSorry(parentWidget, i18n("<qt>Unable to skip scheduled transaction <b>%1</b>.</qt>", schedule.name()), e.what());
662       }
663     }
664   }
665 
666   KScheduledView      *q_ptr;
667   Ui::KScheduledView  *ui;
668   /// The selected schedule id in the list view.
669   QMenu *m_kaccPopup;
670   QStringList m_filterAccounts;
671   bool m_openBills;
672   bool m_openDeposits;
673   bool m_openTransfers;
674   bool m_openLoans;
675 
676   /**
677     * This member holds the load state of page
678     */
679   bool m_needLoad;
680 
681   /**
682    * Search widget for the list
683    */
684   KTreeWidgetSearchLineWidget*  m_searchWidget;
685   MyMoneySchedule m_currentSchedule;
686 
687   QScopedPointer<KBalanceWarning> m_balanceWarning;
688 };
689 
690 #endif
691