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

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000-2002 Javier Campos Morales <javi_c@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2000-2002 Felix Rodriguez <frodriguez@users.sourceforge.net>
0005     SPDX-FileCopyrightText: 2000-2002 John C <thetacoturtle@users.sourceforge.net>
0006     SPDX-FileCopyrightText: 2000-2002 Thomas Baumgart <ipwizard@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2000-2002 Kevin Tambascio <ktambascio@users.sourceforge.net>
0008     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #ifndef KSCHEDULEDVIEW_P_H
0013 #define KSCHEDULEDVIEW_P_H
0014 
0015 #include "kscheduledview.h"
0016 
0017 // ----------------------------------------------------------------------------
0018 // QT Includes
0019 
0020 #include <QList>
0021 #include <QTimer>
0022 #include <QPushButton>
0023 #include <QMenu>
0024 #include <QIcon>
0025 #include <QScopedPointer>
0026 #include <QDebug>
0027 
0028 // ----------------------------------------------------------------------------
0029 // KDE Includes
0030 
0031 #include <KLocalizedString>
0032 #include <KConfig>
0033 #include <KMessageBox>
0034 #include <KSharedConfig>
0035 #include <KTreeWidgetSearchLine>
0036 #include <KTreeWidgetSearchLineWidget>
0037 
0038 // ----------------------------------------------------------------------------
0039 // Project Includes
0040 
0041 #include "ui_kscheduledview.h"
0042 #include "kmymoneyviewbase_p.h"
0043 #include "kenterscheduledlg.h"
0044 #include "kbalancewarning.h"
0045 #include "transactioneditor.h"
0046 #include "kconfirmmanualenterdlg.h"
0047 #include "kmymoneymvccombo.h"
0048 #include "kmymoneyutils.h"
0049 #include "kmymoneysettings.h"
0050 #include "mymoneyexception.h"
0051 #include "kscheduletreeitem.h"
0052 #include "ktreewidgetfilterlinewidget.h"
0053 #include "icons/icons.h"
0054 #include "mymoneyutils.h"
0055 #include "mymoneyaccount.h"
0056 #include "mymoneymoney.h"
0057 #include "mymoneysecurity.h"
0058 #include "mymoneyschedule.h"
0059 #include "mymoneyfile.h"
0060 #include "mymoneypayee.h"
0061 #include "mymoneysplit.h"
0062 #include "mymoneytransaction.h"
0063 #include "mymoneyenums.h"
0064 #include "menuenums.h"
0065 #include "dialogenums.h"
0066 
0067 using namespace Icons;
0068 
0069 class KScheduledViewPrivate : public KMyMoneyViewBasePrivate
0070 {
0071     Q_DECLARE_PUBLIC(KScheduledView)
0072 
0073 public:
0074     explicit KScheduledViewPrivate(KScheduledView *qq) :
0075         KMyMoneyViewBasePrivate(),
0076         q_ptr(qq),
0077         ui(new Ui::KScheduledView),
0078         m_kaccPopup(nullptr),
0079         m_openBills(true),
0080         m_openDeposits(true),
0081         m_openTransfers(true),
0082         m_openLoans(true),
0083         m_needLoad(true),
0084         m_searchWidget(nullptr),
0085         m_balanceWarning(nullptr)
0086     {
0087     }
0088 
0089     ~KScheduledViewPrivate()
0090     {
0091         if(!m_needLoad)
0092             writeConfig();
0093         delete ui;
0094     }
0095 
0096     void init()
0097     {
0098         Q_Q(KScheduledView);
0099         m_needLoad = false;
0100         ui->setupUi(q);
0101 
0102         // create the searchline widget
0103         // and insert it into the existing layout
0104         m_searchWidget = new KTreeWidgetFilterLineWidget(q, ui->m_scheduleTree);
0105         ui->vboxLayout->insertWidget(1, m_searchWidget);
0106 
0107         //enable custom context menu
0108         ui->m_scheduleTree->setContextMenuPolicy(Qt::CustomContextMenu);
0109         ui->m_scheduleTree->setSelectionMode(QAbstractItemView::SingleSelection);
0110 
0111         readConfig();
0112 
0113         q->connect(ui->m_qbuttonNew, &QAbstractButton::clicked, pActions[eMenu::Action::NewSchedule], &QAction::trigger);
0114 
0115         // attach popup to 'Filter...' button
0116         m_kaccPopup = new QMenu(q);
0117         ui->m_accountsCombo->setMenu(m_kaccPopup);
0118         q->connect(m_kaccPopup, &QMenu::triggered, q, &KScheduledView::slotAccountActivated);
0119 
0120         KGuiItem::assign(ui->m_qbuttonNew, KMyMoneyUtils::scheduleNewGuiItem());
0121         KGuiItem::assign(ui->m_accountsCombo, KMyMoneyUtils::accountsFilterGuiItem());
0122 
0123         q->connect(ui->m_scheduleTree, &QWidget::customContextMenuRequested, q, &KScheduledView::customContextMenuRequested);
0124         q->connect(ui->m_scheduleTree, &QTreeWidget::itemSelectionChanged,
0125                    q, &KScheduledView::slotSetSelectedItem);
0126 
0127         q->connect(ui->m_scheduleTree, &QTreeWidget::itemDoubleClicked,
0128                    q, &KScheduledView::slotListItemExecuted);
0129         q->connect(ui->m_scheduleTree, &QTreeWidget::itemExpanded,
0130                    q, &KScheduledView::slotListViewExpanded);
0131         q->connect(ui->m_scheduleTree, &QTreeWidget::itemCollapsed,
0132                    q, &KScheduledView::slotListViewCollapsed);
0133 
0134         q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KScheduledView::refresh);
0135     }
0136 
0137     static bool accountNameLessThan(const MyMoneyAccount& acc1, const MyMoneyAccount& acc2)
0138     {
0139         return acc1.name().toLower() < acc2.name().toLower();
0140     }
0141 
0142     void refreshSchedule(bool full, const QString& schedId)
0143     {
0144         Q_Q(KScheduledView);
0145         ui->m_scheduleTree->header()->setFont(KMyMoneySettings::listHeaderFontEx());
0146 
0147         ui->m_scheduleTree->clear();
0148 
0149         try {
0150             if (full) {
0151                 try {
0152                     m_kaccPopup->clear();
0153 
0154                     MyMoneyFile* file = MyMoneyFile::instance();
0155 
0156                     // extract a list of all accounts under the asset group
0157                     // and sort them by name
0158                     QList<MyMoneyAccount> list;
0159                     QStringList accountList = file->asset().accountList();
0160                     accountList.append(file->liability().accountList());
0161                     file->accountList(list, accountList, true);
0162                     qStableSort(list.begin(), list.end(), accountNameLessThan);
0163 
0164                     QList<MyMoneyAccount>::ConstIterator it_a;
0165                     for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) {
0166                         if (!(*it_a).isClosed()) {
0167                             QAction* act;
0168                             act = m_kaccPopup->addAction((*it_a).name());
0169                             act->setCheckable(true);
0170                             act->setChecked(true);
0171                         }
0172                     }
0173 
0174                 } catch (const MyMoneyException &e) {
0175                     KMessageBox::detailedError(q, i18n("Unable to load accounts: "), e.what());
0176                 }
0177             }
0178 
0179             MyMoneyFile *file = MyMoneyFile::instance();
0180             QList<MyMoneySchedule> scheduledItems = file->scheduleList();
0181 
0182             if (scheduledItems.count() == 0)
0183                 return;
0184 
0185             //disable sorting for performance
0186             ui->m_scheduleTree->setSortingEnabled(false);
0187 
0188             KScheduleTreeItem *itemBills = new KScheduleTreeItem(ui->m_scheduleTree);
0189             itemBills->setIcon(0, Icons::get(Icon::Expense));
0190             itemBills->setText(0, i18n("Bills"));
0191             itemBills->setData(0, KScheduleTreeItem::OrderRole, QVariant("0"));
0192             itemBills->setFirstColumnSpanned(true);
0193             itemBills->setFlags(Qt::ItemIsEnabled);
0194             QFont bold = itemBills->font(0);
0195             bold.setBold(true);
0196             itemBills->setFont(0, bold);
0197             KScheduleTreeItem *itemDeposits = new KScheduleTreeItem(ui->m_scheduleTree);
0198             itemDeposits->setIcon(0, Icons::get(Icon::Income));
0199             itemDeposits->setText(0, i18n("Deposits"));
0200             itemDeposits->setData(0, KScheduleTreeItem::OrderRole, QVariant("1"));
0201             itemDeposits->setFirstColumnSpanned(true);
0202             itemDeposits->setFlags(Qt::ItemIsEnabled);
0203             itemDeposits->setFont(0, bold);
0204             KScheduleTreeItem *itemLoans = new KScheduleTreeItem(ui->m_scheduleTree);
0205             itemLoans->setIcon(0, Icons::get(Icon::Loan));
0206             itemLoans->setText(0, i18n("Loans"));
0207             itemLoans->setData(0, KScheduleTreeItem::OrderRole, QVariant("2"));
0208             itemLoans->setFirstColumnSpanned(true);
0209             itemLoans->setFlags(Qt::ItemIsEnabled);
0210             itemLoans->setFont(0, bold);
0211             KScheduleTreeItem *itemTransfers = new KScheduleTreeItem(ui->m_scheduleTree);
0212             itemTransfers->setIcon(0, Icons::get(Icon::Transaction));
0213             itemTransfers->setText(0, i18n("Transfers"));
0214             itemTransfers->setData(0, KScheduleTreeItem::OrderRole, QVariant("3"));
0215             itemTransfers->setFirstColumnSpanned(true);
0216             itemTransfers->setFlags(Qt::ItemIsEnabled);
0217             itemTransfers->setFont(0, bold);
0218 
0219             QList<MyMoneySchedule>::Iterator it;
0220 
0221             QTreeWidgetItem *openItem = 0;
0222 
0223             for (it = scheduledItems.begin(); it != scheduledItems.end(); ++it) {
0224                 MyMoneySchedule schedData = (*it);
0225                 QTreeWidgetItem* item = 0;
0226 
0227                 bool bContinue = true;
0228                 QStringList::iterator accIt;
0229                 for (accIt = m_filterAccounts.begin(); accIt != m_filterAccounts.end(); ++accIt) {
0230                     if (*accIt == schedData.account().id()) {
0231                         bContinue = false; // Filter it out
0232                         break;
0233                     }
0234                 }
0235 
0236                 if (!bContinue)
0237                     continue;
0238 
0239                 QTreeWidgetItem* parent = 0;
0240                 switch (schedData.type()) {
0241                 case eMyMoney::Schedule::Type::Any:
0242                 // Should we display an error ?
0243                 // We just sort it as bill and fall through here
0244 
0245                 case eMyMoney::Schedule::Type::Bill:
0246                     parent = itemBills;
0247                     break;
0248 
0249                 case eMyMoney::Schedule::Type::Deposit:
0250                     parent = itemDeposits;
0251                     break;
0252 
0253                 case eMyMoney::Schedule::Type::Transfer:
0254                     parent = itemTransfers;
0255                     break;
0256 
0257                 case eMyMoney::Schedule::Type::LoanPayment:
0258                     parent = itemLoans;
0259                     break;
0260 
0261                 }
0262                 if (parent) {
0263                     if (!KMyMoneySettings::hideFinishedSchedules() || !schedData.isFinished()) {
0264                         item = addScheduleItem(parent, schedData);
0265                         if (schedData.id() == schedId)
0266                             openItem = item;
0267                     }
0268                 }
0269             }
0270 
0271             if (openItem) {
0272                 ui->m_scheduleTree->setCurrentItem(openItem);
0273             }
0274             // using a timeout is the only way, I got the 'ensureTransactionVisible'
0275             // working when coming from hidden form to visible form. I assume, this
0276             // has something to do with the delayed update of the display somehow.
0277             q->resize(q->width(), q->height() - 1);
0278             QTimer::singleShot(10, q, SLOT(slotTimerDone()));
0279             ui->m_scheduleTree->update();
0280 
0281             // force repaint in case the filter is set
0282             m_searchWidget->searchLine()->updateSearch(QString());
0283 
0284             if (m_openBills)
0285                 itemBills->setExpanded(true);
0286 
0287             if (m_openDeposits)
0288                 itemDeposits->setExpanded(true);
0289 
0290             if (m_openTransfers)
0291                 itemTransfers->setExpanded(true);
0292 
0293             if (m_openLoans)
0294                 itemLoans->setExpanded(true);
0295 
0296         } catch (const MyMoneyException &e) {
0297             KMessageBox::error(q, e.what());
0298         }
0299 
0300         for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
0301             ui->m_scheduleTree->resizeColumnToContents(i);
0302         }
0303 
0304         //reenable sorting after loading items
0305         ui->m_scheduleTree->setSortingEnabled(true);
0306     }
0307 
0308     void readConfig()
0309     {
0310         KSharedConfigPtr config = KSharedConfig::openConfig();
0311         KConfigGroup grp = config->group("Last Use Settings");
0312         m_openBills = grp.readEntry("KScheduleView_openBills", true);
0313         m_openDeposits = grp.readEntry("KScheduleView_openDeposits", true);
0314         m_openTransfers = grp.readEntry("KScheduleView_openTransfers", true);
0315         m_openLoans = grp.readEntry("KScheduleView_openLoans", true);
0316         QByteArray columns;
0317         columns = grp.readEntry("KScheduleView_treeState", columns);
0318         ui->m_scheduleTree->header()->restoreState(columns);
0319         ui->m_scheduleTree->header()->setFont(KMyMoneySettings::listHeaderFontEx());
0320     }
0321 
0322     void writeConfig()
0323     {
0324         KSharedConfigPtr config = KSharedConfig::openConfig();
0325         KConfigGroup grp = config->group("Last Use Settings");
0326         grp.writeEntry("KScheduleView_openBills", m_openBills);
0327         grp.writeEntry("KScheduleView_openDeposits", m_openDeposits);
0328         grp.writeEntry("KScheduleView_openTransfers", m_openTransfers);
0329         grp.writeEntry("KScheduleView_openLoans", m_openLoans);
0330         QByteArray columns = ui->m_scheduleTree->header()->saveState();
0331         grp.writeEntry("KScheduleView_treeState", columns);
0332 
0333         config->sync();
0334     }
0335 
0336     QTreeWidgetItem* addScheduleItem(QTreeWidgetItem* parent, MyMoneySchedule& schedule)
0337     {
0338         KScheduleTreeItem* item = new KScheduleTreeItem(parent);
0339         item->setData(0, Qt::UserRole, QVariant::fromValue(schedule));
0340         item->setData(0, KScheduleTreeItem::OrderRole, schedule.name());
0341         if (!schedule.isFinished()) {
0342             if (schedule.isOverdue()) {
0343                 item->setIcon(0, Icons::get(Icon::UpcomingEvents));
0344                 QBrush brush = item->foreground(0);
0345                 brush.setColor(Qt::red);
0346                 for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
0347                     item->setForeground(i, brush);
0348                 }
0349             } else {
0350                 item->setIcon(0, Icons::get(Icon::CalendarDay));
0351             }
0352         } else {
0353             item->setIcon(0, Icons::get(Icon::DialogClose));
0354             QBrush brush = item->foreground(0);
0355             brush.setColor(Qt::darkGreen);
0356             for (int i = 0; i < ui->m_scheduleTree->columnCount(); ++i) {
0357                 item->setForeground(i, brush);
0358             }
0359         }
0360 
0361         try {
0362             MyMoneyTransaction transaction = schedule.transaction();
0363             MyMoneySplit s1 = (transaction.splits().size() < 1) ? MyMoneySplit() : transaction.splits()[0];
0364             MyMoneySplit s2 = (transaction.splits().size() < 2) ? MyMoneySplit() : transaction.splits()[1];
0365             MyMoneySplit split;
0366             MyMoneyAccount acc;
0367 
0368             switch (schedule.type()) {
0369             case eMyMoney::Schedule::Type::Deposit:
0370                 if (s1.value().isNegative())
0371                     split = s2;
0372                 else
0373                     split = s1;
0374                 break;
0375 
0376             case eMyMoney::Schedule::Type::LoanPayment:
0377             {
0378                 auto found = false;
0379                 foreach (const auto it_split, transaction.splits()) {
0380                     acc = MyMoneyFile::instance()->account(it_split.accountId());
0381                     if (acc.accountGroup() == eMyMoney::Account::Type::Asset
0382                             || acc.accountGroup() == eMyMoney::Account::Type::Liability) {
0383                         if (acc.accountType() != eMyMoney::Account::Type::Loan
0384                                 && acc.accountType() != eMyMoney::Account::Type::AssetLoan) {
0385                             split = it_split;
0386                             found = true;
0387                             break;
0388                         }
0389                     }
0390                 }
0391                 if (!found) {
0392                     qWarning("Split for payment account not found in %s:%d.", __FILE__, __LINE__);
0393                 }
0394                 break;
0395             }
0396             default:
0397                 if (!s1.value().isPositive())
0398                     split = s1;
0399                 else
0400                     split = s2;
0401                 break;
0402             }
0403             acc = MyMoneyFile::instance()->account(split.accountId());
0404 
0405             item->setText(0, schedule.name());
0406             MyMoneySecurity currency = MyMoneyFile::instance()->currency(acc.currencyId());
0407 
0408             QString accName =  acc.name();
0409             if (!accName.isEmpty()) {
0410                 item->setText(1, accName);
0411             } else {
0412                 item->setText(1, "---");
0413             }
0414             item->setData(1, KScheduleTreeItem::OrderRole, QVariant(accName));
0415 
0416             QString payeeName;
0417             if (!s1.payeeId().isEmpty()) {
0418                 payeeName = MyMoneyFile::instance()->payee(s1.payeeId()).name();
0419                 item->setText(2, payeeName);
0420             } else {
0421                 item->setText(2, "---");
0422             }
0423             item->setData(2, KScheduleTreeItem::OrderRole, QVariant(payeeName));
0424 
0425             MyMoneyMoney amount = split.shares().abs();
0426             item->setData(3, Qt::UserRole, QVariant::fromValue(amount));
0427             if (!accName.isEmpty()) {
0428                 item->setText(3, QString("%1  ").arg(MyMoneyUtils::formatMoney(amount, acc, currency)));
0429             } else {
0430                 //there are some cases where the schedule does not have an account
0431                 //in those cases the account will not have a fraction
0432                 //use base currency instead
0433                 item->setText(3, QString("%1  ").arg(MyMoneyUtils::formatMoney(amount, MyMoneyFile::instance()->baseCurrency())));
0434             }
0435             item->setTextAlignment(3, Qt::AlignRight | Qt::AlignVCenter);
0436             item->setData(3, KScheduleTreeItem::OrderRole, QVariant::fromValue(amount));
0437 
0438             // Do the real next payment like ms-money etc
0439             QDate nextDueDate;
0440             if (schedule.isFinished()) {
0441                 item->setText(4, i18nc("Finished schedule", "Finished"));
0442             } else {
0443                 nextDueDate = schedule.adjustedNextDueDate();
0444                 item->setText(4, QLocale().toString(schedule.adjustedNextDueDate(), QLocale::ShortFormat));
0445             }
0446             item->setData(4, KScheduleTreeItem::OrderRole, QVariant(nextDueDate));
0447             item->setText(5, i18nc("Frequency of schedule", schedule.occurrenceToString().toLatin1()));
0448             item->setText(6, KMyMoneyUtils::paymentMethodToString(schedule.paymentType()));
0449         } catch (const MyMoneyException &e) {
0450             item->setText(0, "Error:");
0451             item->setText(1, e.what());
0452         }
0453         return item;
0454     }
0455 
0456     /**
0457       * This method allows to enter the next scheduled transaction of
0458       * the given schedule @a s. In case @a extendedKeys is @a true,
0459       * the given schedule can also be skipped or ignored.
0460       * If @a autoEnter is @a true and the schedule does not contain
0461       * an estimated value, the schedule is entered as is without further
0462       * interaction with the user. In all other cases, the user will
0463       * be presented a dialog and allowed to adjust the values for this
0464       * instance of the schedule.
0465       *
0466       * The transaction will be created and entered into the ledger
0467       * and the schedule updated.
0468       */
0469     eDialogs::ScheduleResultCode enterSchedule(MyMoneySchedule& schedule, bool autoEnter = false, bool extendedKeys = false)
0470     {
0471         Q_Q(KScheduledView);
0472         auto rc = eDialogs::ScheduleResultCode::Cancel;
0473         if (!schedule.id().isEmpty()) {
0474             try {
0475                 schedule = MyMoneyFile::instance()->schedule(schedule.id());
0476             } catch (const MyMoneyException &e) {
0477                 KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
0478                 return rc;
0479             }
0480 
0481             QWidget* parent = QApplication::activeWindow();
0482             QPointer<KEnterScheduleDlg> dlg = new KEnterScheduleDlg(parent, schedule);
0483 
0484             try {
0485                 QDate origDueDate = schedule.nextDueDate();
0486 
0487                 dlg->showExtendedKeys(extendedKeys);
0488 
0489                 QPointer<TransactionEditor> transactionEditor = dlg->startEdit();
0490                 if (transactionEditor) {
0491                     KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart());
0492                     MyMoneyTransaction torig, taccepted;
0493                     transactionEditor->createTransaction(torig, dlg->transaction(),
0494                                                          schedule.transaction().splits().isEmpty() ? MyMoneySplit() : schedule.transaction().splits().front(), true);
0495                     // force actions to be available no matter what (will be updated according to the state during
0496                     // slotTransactionsEnter or slotTransactionsCancel)
0497 
0498                     KConfirmManualEnterDlg::Action action = KConfirmManualEnterDlg::ModifyOnce;
0499                     if (!autoEnter || !schedule.isFixed()) {
0500                         for (; dlg != 0;) {
0501                             rc = eDialogs::ScheduleResultCode::Cancel;
0502                             if (dlg->exec() == QDialog::Accepted && dlg != 0) {
0503                                 pActions[eMenu::Action::CancelTransaction]->setEnabled(true);
0504                                 pActions[eMenu::Action::EnterTransaction]->setEnabled(true);
0505                                 rc = dlg->resultCode();
0506                                 if (rc == eDialogs::ScheduleResultCode::Enter) {
0507                                     transactionEditor->createTransaction(taccepted, torig, torig.splits().isEmpty() ? MyMoneySplit() : torig.splits().front(), true);
0508                                     // make sure to suppress comparison of some data: postDate
0509                                     torig.setPostDate(taccepted.postDate());
0510                                     if (torig != taccepted) {
0511                                         QPointer<KConfirmManualEnterDlg> cdlg =
0512                                             new KConfirmManualEnterDlg(schedule, q);
0513                                         cdlg->loadTransactions(torig, taccepted);
0514                                         if (cdlg->exec() == QDialog::Accepted) {
0515                                             action = cdlg->action();
0516                                             delete cdlg;
0517                                             break;
0518                                         }
0519                                         delete cdlg;
0520                                         // the user has chosen 'cancel' during confirmation,
0521                                         // we go back to the editor
0522                                         continue;
0523                                     }
0524                                 } else if (rc == eDialogs::ScheduleResultCode::Skip) {
0525                                     slotTransactionsCancel(transactionEditor, schedule);
0526                                     skipSchedule(schedule);
0527                                 } else {
0528                                     slotTransactionsCancel(transactionEditor, schedule);
0529                                 }
0530                             } else {
0531                                 pActions[eMenu::Action::CancelTransaction]->setEnabled(true);
0532                                 pActions[eMenu::Action::EnterTransaction]->setEnabled(true);
0533                                 if (autoEnter) {
0534                                     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) {
0535                                         // the user has chosen 'No' for the above question,
0536                                         // we go back to the editor
0537                                         continue;
0538                                     }
0539                                 }
0540                                 slotTransactionsCancel(transactionEditor, schedule);
0541                             }
0542                             break;
0543                         }
0544                     }
0545 
0546                     // if we still have the editor around here, the user did not cancel
0547                     if ((transactionEditor != 0) && (dlg != 0)) {
0548                         MyMoneyFileTransaction ft;
0549                         try {
0550                             MyMoneyTransaction t;
0551                             // add the new transaction
0552                             switch (action) {
0553                             case KConfirmManualEnterDlg::UseOriginal:
0554                                 // setup widgets with original transaction data
0555                                 transactionEditor->setTransaction(dlg->transaction(), dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front());
0556                                 // and create a transaction based on that data
0557                                 taccepted = MyMoneyTransaction();
0558                                 transactionEditor->createTransaction(taccepted, dlg->transaction(),
0559                                                                      dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front(), true);
0560                                 break;
0561 
0562                             case KConfirmManualEnterDlg::ModifyAlways:
0563                                 torig = taccepted;
0564                                 torig.setPostDate(origDueDate);
0565                                 schedule.setTransaction(torig);
0566                                 break;
0567 
0568                             case KConfirmManualEnterDlg::ModifyOnce:
0569                                 break;
0570                             }
0571 
0572                             QString newId;
0573                             q->connect(transactionEditor, SIGNAL(balanceWarning(QWidget*,MyMoneyAccount,QString)), m_balanceWarning.data(), SLOT(slotShowMessage(QWidget*,MyMoneyAccount,QString)));
0574                             if (transactionEditor->enterTransactions(newId, false)) {
0575                                 if (!newId.isEmpty()) {
0576                                     t = MyMoneyFile::instance()->transaction(newId);
0577                                     schedule.setLastPayment(t.postDate());
0578                                 }
0579                                 // in case the next due date is invalid, the schedule is finished
0580                                 // we mark it as such by setting the next due date to one day past the end
0581                                 QDate nextDueDate = schedule.nextPayment(origDueDate);
0582                                 if (!nextDueDate.isValid()) {
0583                                     schedule.setNextDueDate(schedule.endDate().addDays(1));
0584                                 } else {
0585                                     schedule.setNextDueDate(nextDueDate);
0586                                 }
0587                                 MyMoneyFile::instance()->modifySchedule(schedule);
0588                                 rc = eDialogs::ScheduleResultCode::Enter;
0589 
0590                                 // delete the editor before we emit the dataChanged() signal from the
0591                                 // engine. Calling this twice in a row does not hurt.
0592                                 delete transactionEditor;
0593                                 ft.commit();
0594                             }
0595                         } catch (const MyMoneyException &e) {
0596                             KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
0597                         }
0598                         delete transactionEditor;
0599                     }
0600                 }
0601             } catch (const MyMoneyException &e) {
0602                 KMessageBox::detailedSorry(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what());
0603             }
0604             delete dlg;
0605         }
0606         return rc;
0607     }
0608 
0609     void slotTransactionsCancel(TransactionEditor* editor, const MyMoneySchedule& schedule)
0610     {
0611         Q_Q(KScheduledView);
0612         // since we jump here via code, we have to make sure to react only
0613         // if the action is enabled
0614         if (pActions[eMenu::Action::CancelTransaction]->isEnabled()) {
0615             // make sure, we block the enter function
0616             pActions[eMenu::Action::EnterTransaction]->setEnabled(false);
0617             // qDebug("KMyMoneyApp::slotTransactionsCancel");
0618             delete editor;
0619             emit q->selectByObject(schedule, eView::Intent::None);
0620         }
0621     }
0622 
0623     /**
0624       * This method allows to skip the next scheduled transaction of
0625       * the given schedule @a s.
0626       *
0627       */
0628     void skipSchedule(MyMoneySchedule& schedule)
0629     {
0630         const auto parentWidget = QApplication::activeWindow();
0631 
0632         if (!schedule.id().isEmpty()) {
0633             try {
0634                 schedule = MyMoneyFile::instance()->schedule(schedule.id());
0635                 if (!schedule.isFinished()) {
0636                     if (schedule.occurrence() != eMyMoney::Schedule::Occurrence::Once) {
0637                         QDate next = schedule.nextDueDate();
0638                         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) {
0639                             MyMoneyFileTransaction ft;
0640                             schedule.setLastPayment(next);
0641                             schedule.setNextDueDate(schedule.nextPayment(next));
0642                             MyMoneyFile::instance()->modifySchedule(schedule);
0643                             ft.commit();
0644                         }
0645                     }
0646                 }
0647             } catch (const MyMoneyException &e) {
0648                 KMessageBox::detailedSorry(parentWidget, i18n("<qt>Unable to skip scheduled transaction <b>%1</b>.</qt>", schedule.name()), e.what());
0649             }
0650         }
0651     }
0652 
0653     KScheduledView      *q_ptr;
0654     Ui::KScheduledView  *ui;
0655     /// The selected schedule id in the list view.
0656     QMenu *m_kaccPopup;
0657     QStringList m_filterAccounts;
0658     bool m_openBills;
0659     bool m_openDeposits;
0660     bool m_openTransfers;
0661     bool m_openLoans;
0662 
0663     /**
0664       * This member holds the load state of page
0665       */
0666     bool m_needLoad;
0667 
0668     /**
0669      * Search widget for the list
0670      */
0671     KTreeWidgetSearchLineWidget*  m_searchWidget;
0672     MyMoneySchedule m_currentSchedule;
0673 
0674     QScopedPointer<KBalanceWarning> m_balanceWarning;
0675 };
0676 
0677 #endif