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