File indexing completed on 2024-05-12 05:07:43
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 <QAction> 0022 #include <QScopedPointer> 0023 #include <QDebug> 0024 0025 // ---------------------------------------------------------------------------- 0026 // KDE Includes 0027 0028 #include <KLocalizedString> 0029 #include <KConfig> 0030 #include <KMessageBox> 0031 #include <KSharedConfig> 0032 0033 // ---------------------------------------------------------------------------- 0034 // Project Includes 0035 0036 #include "ui_kscheduledview.h" 0037 #include "kmymoneyviewbase_p.h" 0038 #include "kmymoneysettings.h" 0039 #include "kenterscheduledlg.h" 0040 #include "kbalancewarning.h" 0041 #include "kconfirmmanualenterdlg.h" 0042 #include "kmymoneymvccombo.h" 0043 #include "kmymoneyutils.h" 0044 #include "kmymoneysettings.h" 0045 #include "mymoneyexception.h" 0046 #include "mymoneyutils.h" 0047 #include "mymoneyaccount.h" 0048 #include "mymoneymoney.h" 0049 #include "mymoneysecurity.h" 0050 #include "mymoneyschedule.h" 0051 #include "mymoneyfile.h" 0052 #include "mymoneypayee.h" 0053 #include "mymoneysplit.h" 0054 #include "mymoneytransaction.h" 0055 #include "mymoneyenums.h" 0056 #include "menuenums.h" 0057 #include "dialogenums.h" 0058 #include "schedulesmodel.h" 0059 #include "scheduleproxymodel.h" 0060 0061 class KScheduledViewPrivate : public KMyMoneyViewBasePrivate 0062 { 0063 Q_DECLARE_PUBLIC(KScheduledView) 0064 0065 public: 0066 explicit KScheduledViewPrivate(KScheduledView* qq) 0067 : KMyMoneyViewBasePrivate(qq) 0068 , ui(new Ui::KScheduledView) 0069 , m_filterModel(nullptr) 0070 , m_needLoad(true) 0071 , m_editingCanceled(false) 0072 , m_balanceWarning(nullptr) 0073 { 0074 } 0075 0076 ~KScheduledViewPrivate() 0077 { 0078 if(!m_needLoad) 0079 writeConfig(); 0080 delete ui; 0081 } 0082 0083 void init() 0084 { 0085 Q_Q(KScheduledView); 0086 m_needLoad = false; 0087 ui->setupUi(q); 0088 0089 //enable custom context menu 0090 ui->m_scheduleTree->setContextMenuPolicy(Qt::CustomContextMenu); 0091 ui->m_scheduleTree->setSelectionMode(QAbstractItemView::SingleSelection); 0092 0093 m_filterModel = new ScheduleProxyModel(q); 0094 m_filterModel->setSourceModel(MyMoneyFile::instance()->schedulesModel()); 0095 m_filterModel->setSortLocaleAware(true); 0096 m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0097 m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); 0098 m_filterModel->setFilterKeyColumn(-1); 0099 0100 ui->m_scheduleTree->setModel(m_filterModel); 0101 0102 readConfig(); 0103 0104 q->connect(ui->m_qbuttonNew, &QAbstractButton::clicked, pActions[eMenu::Action::NewSchedule], &QAction::trigger); 0105 q->connect(ui->m_searchWidget, &QLineEdit::textChanged, m_filterModel, &QSortFilterProxyModel::setFilterFixedString); 0106 0107 KGuiItem::assign(ui->m_qbuttonNew, KMyMoneyUtils::scheduleNewGuiItem()); 0108 0109 q->connect(ui->m_scheduleTree->selectionModel(), &QItemSelectionModel::selectionChanged, q, &KScheduledView::slotSetSelectedItem); 0110 0111 q->connect(ui->m_scheduleTree, &QTreeView::expanded, q, &KScheduledView::slotListViewExpanded); 0112 q->connect(ui->m_scheduleTree, &QTreeView::collapsed, q, &KScheduledView::slotListViewCollapsed); 0113 0114 // update settings 0115 settingsChanged(); 0116 0117 // Setup collapsed and expanded groups 0118 const auto model = ui->m_scheduleTree->model(); 0119 const auto rows = model->rowCount(); 0120 for (int row = 0; row < rows; ++row) { 0121 const auto idx = model->index(row, 0); 0122 const auto groupType = static_cast<eMyMoney::Schedule::Type>(idx.data(eMyMoney::Model::ScheduleTypeRole).toInt()); 0123 if (m_expandedGroups.contains(groupType)) { 0124 ui->m_scheduleTree->setExpanded(idx, m_expandedGroups[groupType]); 0125 } 0126 } 0127 0128 m_focusWidget = ui->m_searchWidget; 0129 } 0130 0131 void settingsChanged() 0132 { 0133 if (m_filterModel) { 0134 m_filterModel->setHideFinishedSchedules(KMyMoneySettings::hideFinishedSchedules()); 0135 m_filterModel->invalidate(); 0136 } 0137 } 0138 0139 void readConfig() 0140 { 0141 KSharedConfigPtr config = KSharedConfig::openConfig(); 0142 KConfigGroup grp = config->group("Last Use Settings"); 0143 m_expandedGroups[eMyMoney::Schedule::Type::Bill] = grp.readEntry("KScheduleView_openBills", true); 0144 m_expandedGroups[eMyMoney::Schedule::Type::Deposit] = grp.readEntry("KScheduleView_openDeposits", true); 0145 m_expandedGroups[eMyMoney::Schedule::Type::Transfer] = grp.readEntry("KScheduleView_openTransfers", true); 0146 m_expandedGroups[eMyMoney::Schedule::Type::LoanPayment] = grp.readEntry("KScheduleView_openLoans", true); 0147 QByteArray columns; 0148 columns = grp.readEntry("KScheduleView_treeState", columns); 0149 ui->m_scheduleTree->header()->restoreState(columns); 0150 ui->m_scheduleTree->header()->setFont(KMyMoneySettings::listHeaderFontEx()); 0151 int sortCol = grp.readEntry<int>("KScheduleView_sortColumn", SchedulesModel::Column::Name); 0152 auto sortOrder = static_cast<Qt::SortOrder>(grp.readEntry<int>("KScheduleView_sortOrder", Qt::AscendingOrder)); 0153 m_filterModel->sort(sortCol, sortOrder); 0154 ui->m_scheduleTree->setSortingEnabled(true); 0155 ui->m_scheduleTree->setAllColumnsShowFocus(true); 0156 ui->m_scheduleTree->setAlternatingRowColors(true); 0157 ui->m_scheduleTree->header()->setSortIndicatorShown(true); 0158 } 0159 0160 void writeConfig() 0161 { 0162 KSharedConfigPtr config = KSharedConfig::openConfig(); 0163 KConfigGroup grp = config->group("Last Use Settings"); 0164 grp.writeEntry("KScheduleView_openBills", m_expandedGroups[eMyMoney::Schedule::Type::Bill]); 0165 grp.writeEntry("KScheduleView_openDeposits", m_expandedGroups[eMyMoney::Schedule::Type::Deposit]); 0166 grp.writeEntry("KScheduleView_openTransfers", m_expandedGroups[eMyMoney::Schedule::Type::Transfer]); 0167 grp.writeEntry("KScheduleView_openLoans", m_expandedGroups[eMyMoney::Schedule::Type::LoanPayment]); 0168 QByteArray columns = ui->m_scheduleTree->header()->saveState(); 0169 grp.writeEntry("KScheduleView_treeState", columns); 0170 grp.writeEntry("KScheduleView_sortColumn", m_filterModel->sortColumn()); 0171 grp.writeEntry("KScheduleView_sortOrder", static_cast<int>(m_filterModel->sortOrder())); 0172 config->sync(); 0173 } 0174 0175 /** 0176 * This method allows to enter the next scheduled transaction of 0177 * the given schedule @a s. In case @a extendedKeys is @a true, 0178 * the given schedule can also be skipped or ignored. 0179 * If @a autoEnter is @a true and the schedule does not contain 0180 * an estimated value, the schedule is entered as is without further 0181 * interaction with the user. In all other cases, the user will 0182 * be presented a dialog and allowed to adjust the values for this 0183 * instance of the schedule. 0184 * 0185 * The transaction will be created and entered into the ledger 0186 * and the schedule updated. 0187 */ 0188 eDialogs::ScheduleResultCode enterSchedule(MyMoneySchedule& schedule, bool autoEnter = false, bool extendedKeys = false) 0189 { 0190 Q_Q(KScheduledView); 0191 auto rc = eDialogs::ScheduleResultCode::Cancel; 0192 if (!schedule.id().isEmpty()) { 0193 try { 0194 schedule = MyMoneyFile::instance()->schedule(schedule.id()); 0195 } catch (const MyMoneyException &e) { 0196 KMessageBox::detailedError(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); 0197 return rc; 0198 } 0199 0200 QWidget* parent = QApplication::activeWindow(); 0201 QPointer<KEnterScheduleDlg> dlg = new KEnterScheduleDlg(parent, schedule); 0202 0203 try { 0204 QDate origDueDate = schedule.nextDueDate(); 0205 0206 dlg->setShowExtendedKeys(extendedKeys); 0207 KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); 0208 0209 auto torig = dlg->transaction(); 0210 MyMoneyTransaction taccepted(torig); 0211 // force actions to be available no matter what (will be updated according to the state during 0212 // slotTransactionsEnter or cancelEditing) 0213 pActions[eMenu::Action::CancelTransaction]->setEnabled(true); 0214 pActions[eMenu::Action::EnterTransaction]->setEnabled(true); 0215 0216 KConfirmManualEnterDlg::Action action = KConfirmManualEnterDlg::ModifyOnce; 0217 if (!autoEnter || !schedule.isFixed()) { 0218 while (dlg != nullptr) { 0219 rc = eDialogs::ScheduleResultCode::Cancel; 0220 if ((dlg->exec() == QDialog::Accepted) && (dlg != nullptr)) { 0221 rc = dlg->resultCode(); 0222 if (rc == eDialogs::ScheduleResultCode::Enter) { 0223 taccepted = dlg->transaction(); 0224 // make sure to suppress comparison of some data: postDate 0225 torig.setPostDate(taccepted.postDate()); 0226 0227 // for loans we only have the option to enter, so we don't ask 0228 if (schedule.type() != eMyMoney::Schedule::Type::LoanPayment) { 0229 if (torig != taccepted) { 0230 QPointer<KConfirmManualEnterDlg> cdlg = new KConfirmManualEnterDlg(schedule, q); 0231 cdlg->loadTransactions(torig, taccepted); 0232 if ((cdlg->exec() == QDialog::Accepted) && (cdlg != nullptr)) { 0233 action = cdlg->action(); 0234 break; 0235 } 0236 // the user has chosen 'cancel' during confirmation, 0237 // we go back to the editor 0238 continue; 0239 } 0240 } 0241 break; 0242 0243 } else if (rc == eDialogs::ScheduleResultCode::Skip) { 0244 skipSchedule(schedule); 0245 } 0246 } else { 0247 if (autoEnter) { 0248 if (KMessageBox::warningTwoActions( 0249 q, 0250 i18n("Are you sure you wish to stop this scheduled transaction from being entered into the register?\n\nKMyMoney will " 0251 "prompt you again next time it starts unless you manually enter it later."), 0252 i18nc("@title:window", "Stop entering schedule"), 0253 KMMYesNo::yes(), 0254 KMMYesNo::no()) 0255 == KMessageBox::SecondaryAction) { 0256 // the user has chosen 'No' for the above question, 0257 // we go back to the editor 0258 continue; 0259 } 0260 } 0261 } 0262 cancelEditing(); 0263 break; 0264 } 0265 } 0266 0267 // if we still have the editor around here, the user did not cancel 0268 if ((dlg != nullptr) && (!m_editingCanceled)) { 0269 MyMoneyFileTransaction ft; 0270 try { 0271 MyMoneyTransaction t(taccepted); 0272 // add the new transaction 0273 switch (action) { 0274 case KConfirmManualEnterDlg::UseOriginal: 0275 // setup widgets with original transaction data 0276 t = torig; 0277 break; 0278 0279 case KConfirmManualEnterDlg::ModifyAlways: 0280 torig = taccepted; 0281 torig.setPostDate(origDueDate); 0282 schedule.setTransaction(torig); 0283 break; 0284 0285 case KConfirmManualEnterDlg::ModifyOnce: 0286 break; 0287 } 0288 0289 MyMoneyFile::instance()->addTransaction(t); 0290 0291 // we should not need this because addTransaction() does 0292 // update the data, but we want to stay on the safe side 0293 if (!t.id().isEmpty()) { 0294 t = MyMoneyFile::instance()->transaction(t.id()); 0295 schedule.setLastPayment(t.postDate()); 0296 } 0297 0298 // in case the next due date is invalid, the schedule is finished 0299 // we mark it as such by setting the next due date to one day past the end 0300 QDate nextDueDate = schedule.nextPayment(origDueDate); 0301 if (!nextDueDate.isValid()) { 0302 schedule.setNextDueDate(schedule.endDate().addDays(1)); 0303 } else { 0304 schedule.setNextDueDate(nextDueDate); 0305 } 0306 MyMoneyFile::instance()->modifySchedule(schedule); 0307 rc = eDialogs::ScheduleResultCode::Enter; 0308 0309 ft.commit(); 0310 } catch (const MyMoneyException& e) { 0311 KMessageBox::detailedError(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); 0312 } 0313 } 0314 } catch (const MyMoneyException &e) { 0315 KMessageBox::detailedError(q, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); 0316 } 0317 delete dlg; 0318 } 0319 return rc; 0320 } 0321 0322 void cancelEditing() 0323 { 0324 // since we jump here via code, we have to make sure to react only 0325 // if the action is enabled 0326 if (pActions[eMenu::Action::CancelTransaction]->isEnabled()) { 0327 // make sure, we block the enter function 0328 pActions[eMenu::Action::EnterTransaction]->setEnabled(false); 0329 m_editingCanceled = true; 0330 } 0331 } 0332 0333 /** 0334 * This method allows to skip the next scheduled transaction of 0335 * the given schedule @a s. 0336 * 0337 */ 0338 void skipSchedule(MyMoneySchedule& schedule) 0339 { 0340 Q_Q(KScheduledView); 0341 const auto parentWidget = QApplication::activeWindow(); 0342 0343 if (!schedule.id().isEmpty()) { 0344 try { 0345 schedule = MyMoneyFile::instance()->schedule(schedule.id()); 0346 if (!schedule.isFinished()) { 0347 if (schedule.occurrence() != eMyMoney::Schedule::Occurrence::Once) { 0348 QDate next = schedule.nextDueDate(); 0349 if (!schedule.isFinished() 0350 && (KMessageBox::questionTwoActions(parentWidget, 0351 i18n("<qt>Do you really want to skip the <b>%1</b> transaction scheduled for <b>%2</b>?</qt>", 0352 schedule.name(), 0353 MyMoneyUtils::formatDate(next)), 0354 i18nc("@title:window", "Skip scheduled transaction"), 0355 KMMYesNo::yes(), 0356 KMMYesNo::no())) 0357 == KMessageBox::PrimaryAction) { 0358 MyMoneyFileTransaction ft; 0359 schedule.setLastPayment(next); 0360 schedule.setNextDueDate(schedule.nextPayment(next)); 0361 MyMoneyFile::instance()->modifySchedule(schedule); 0362 ft.commit(); 0363 } 0364 } 0365 } 0366 } catch (const MyMoneyException &e) { 0367 KMessageBox::detailedError(q, i18n("<qt>Unable to skip scheduled transaction <b>%1</b>.</qt>", schedule.name()), e.what()); 0368 } 0369 } 0370 } 0371 0372 Ui::KScheduledView* ui; 0373 ScheduleProxyModel* m_filterModel; 0374 QHash<eMyMoney::Schedule::Type, bool> m_expandedGroups; 0375 0376 /** 0377 * This member holds the initial load state of the view 0378 */ 0379 bool m_needLoad; 0380 bool m_editingCanceled; 0381 MyMoneySchedule m_currentSchedule; 0382 0383 QScopedPointer<KBalanceWarning> m_balanceWarning; 0384 }; 0385 0386 #endif