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 #include "kscheduledview_p.h" 0013 0014 // ---------------------------------------------------------------------------- 0015 // QT Includes 0016 0017 #include <QMenu> 0018 0019 // ---------------------------------------------------------------------------- 0020 // KDE Includes 0021 0022 // ---------------------------------------------------------------------------- 0023 // Project Includes 0024 0025 #include "keditloanwizard.h" 0026 #include "kmymoneyutils.h" 0027 #include "kmymoneysettings.h" 0028 #include "mymoneyexception.h" 0029 #include "keditscheduledlg.h" 0030 0031 KScheduledView::KScheduledView(QWidget *parent) : 0032 KMyMoneyViewBase(*new KScheduledViewPrivate(this), parent) 0033 { 0034 typedef void(KScheduledView::*KScheduledViewFunc)(); 0035 const QHash<eMenu::Action, KScheduledViewFunc> actionConnections { 0036 {eMenu::Action::NewSchedule, &KScheduledView::slotNewSchedule}, 0037 {eMenu::Action::EditSchedule, &KScheduledView::slotEditSchedule}, 0038 {eMenu::Action::DeleteSchedule, &KScheduledView::slotDeleteSchedule}, 0039 {eMenu::Action::DuplicateSchedule, &KScheduledView::slotDuplicateSchedule}, 0040 {eMenu::Action::EnterSchedule, &KScheduledView::slotEnterSchedule}, 0041 {eMenu::Action::SkipSchedule, &KScheduledView::slotSkipSchedule}, 0042 }; 0043 0044 for (auto a = actionConnections.cbegin(); a != actionConnections.cend(); ++a) 0045 connect(pActions[a.key()], &QAction::triggered, this, a.value()); 0046 0047 Q_D(KScheduledView); 0048 d->m_balanceWarning.reset(new KBalanceWarning(this)); 0049 0050 d->m_sharedToolbarActions.insert(eMenu::Action::FileNew, pActions[eMenu::Action::NewSchedule]); 0051 } 0052 0053 KScheduledView::~KScheduledView() 0054 { 0055 } 0056 0057 void KScheduledView::slotSettingsChanged() 0058 { 0059 Q_D(KScheduledView); 0060 d->settingsChanged(); 0061 } 0062 0063 void KScheduledView::showEvent(QShowEvent* event) 0064 { 0065 Q_D(KScheduledView); 0066 if (d->m_needLoad) { 0067 d->init(); 0068 connect(d->ui->m_scheduleTree, &QWidget::customContextMenuRequested, this, [&](const QPoint& pos) { 0069 Q_D(KScheduledView); 0070 Q_EMIT requestCustomContextMenu(eMenu::Menu::Schedule, d->ui->m_scheduleTree->viewport()->mapToGlobal(pos)); 0071 }); 0072 connect(d->ui->m_scheduleTree, &KMyMoneyTreeView::startEdit, this, &KScheduledView::slotEditSchedule); 0073 0074 connect(d->ui->m_scheduleTree->header(), &QHeaderView::sortIndicatorChanged, this, [&](int logicalIndex, Qt::SortOrder order) { 0075 Q_D(KScheduledView); 0076 d->m_filterModel->sort(logicalIndex, order); 0077 }); 0078 connect(MyMoneyFile::instance()->schedulesModel(), &SchedulesModel::dataChanged, this, [&](const QModelIndex& from, const QModelIndex& to) { 0079 Q_UNUSED(from) 0080 Q_UNUSED(to) 0081 Q_D(KScheduledView); 0082 d->m_filterModel->invalidate(); 0083 }); 0084 } 0085 QWidget::showEvent(event); 0086 } 0087 0088 void KScheduledView::updateActions(const SelectedObjects& selections) 0089 { 0090 Q_D(KScheduledView); 0091 0092 const QVector<eMenu::Action> actionsToBeDisabled { 0093 eMenu::Action::EditSchedule, eMenu::Action::DuplicateSchedule, eMenu::Action::DeleteSchedule, 0094 eMenu::Action::EnterSchedule, eMenu::Action::SkipSchedule, 0095 }; 0096 0097 for (const auto& a : actionsToBeDisabled) 0098 pActions[a]->setEnabled(false); 0099 0100 MyMoneySchedule sch; 0101 if (!selections.selection(SelectedObjects::Schedule).isEmpty()) { 0102 sch = MyMoneyFile::instance()->schedulesModel()->itemById(selections.selection(SelectedObjects::Schedule).at(0)); 0103 if (!sch.id().isEmpty()) { 0104 pActions[eMenu::Action::EditSchedule]->setEnabled(true); 0105 pActions[eMenu::Action::DuplicateSchedule]->setEnabled(true); 0106 pActions[eMenu::Action::DeleteSchedule]->setEnabled(!MyMoneyFile::instance()->isReferenced(sch)); 0107 if (!sch.isFinished()) { 0108 pActions[eMenu::Action::EnterSchedule]->setEnabled(true); 0109 // a schedule with a single occurrence cannot be skipped 0110 if (sch.occurrence() != eMyMoney::Schedule::Occurrence::Once) { 0111 pActions[eMenu::Action::SkipSchedule]->setEnabled(true); 0112 } 0113 } 0114 } 0115 } 0116 d->m_currentSchedule = sch; 0117 } 0118 0119 eDialogs::ScheduleResultCode KScheduledView::enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys) 0120 { 0121 Q_D(KScheduledView); 0122 return d->enterSchedule(schedule, autoEnter, extendedKeys); 0123 } 0124 0125 void KScheduledView::slotEnterOverdueSchedules(const MyMoneyAccount& acc) 0126 { 0127 Q_D(KScheduledView); 0128 const auto file = MyMoneyFile::instance(); 0129 auto schedules = file->scheduleList(acc.id(), eMyMoney::Schedule::Type::Any, eMyMoney::Schedule::Occurrence::Any, eMyMoney::Schedule::PaymentType::Any, QDate(), QDate(), true); 0130 if (!schedules.isEmpty()) { 0131 if (KMessageBox::questionTwoActions( 0132 this, 0133 i18n("KMyMoney has detected some overdue scheduled transactions for this account. Do you want to enter those scheduled transactions now?"), 0134 i18n("Scheduled transactions found"), 0135 KMMYesNo::yes(), 0136 KMMYesNo::no()) 0137 == KMessageBox::PrimaryAction) { 0138 QMap<QString, bool> skipMap; 0139 bool processedOne; 0140 auto rc = eDialogs::ScheduleResultCode::Enter; 0141 do { 0142 processedOne = false; 0143 QList<MyMoneySchedule>::const_iterator it_sch; 0144 for (it_sch = schedules.constBegin(); (rc != eDialogs::ScheduleResultCode::Cancel) && (it_sch != schedules.constEnd()); ++it_sch) { 0145 MyMoneySchedule sch(*(it_sch)); 0146 0147 // and enter it if it is not on the skip list 0148 if (skipMap.find((*it_sch).id()) == skipMap.end()) { 0149 rc = d->enterSchedule(sch, false, true); 0150 if (rc == eDialogs::ScheduleResultCode::Ignore) { 0151 skipMap[(*it_sch).id()] = true; 0152 } 0153 } 0154 } 0155 0156 // reload list (maybe this schedule needs to be added again) 0157 schedules = file->scheduleList(acc.id(), eMyMoney::Schedule::Type::Any, eMyMoney::Schedule::Occurrence::Any, eMyMoney::Schedule::PaymentType::Any, QDate(), QDate(), true); 0158 } while (processedOne); 0159 } 0160 } 0161 } 0162 0163 void KScheduledView::slotListViewExpanded(const QModelIndex& idx) 0164 { 0165 Q_D(KScheduledView); 0166 // we only need to check the top level items 0167 if (!idx.parent().isValid()) { 0168 const auto groupType = static_cast<eMyMoney::Schedule::Type>(idx.data(eMyMoney::Model::ScheduleTypeRole).toInt()); 0169 d->m_expandedGroups[groupType] = true; 0170 } 0171 } 0172 0173 void KScheduledView::slotListViewCollapsed(const QModelIndex& idx) 0174 { 0175 Q_D(KScheduledView); 0176 // we only need to check the top level items 0177 if (!idx.parent().isValid()) { 0178 const auto groupType = static_cast<eMyMoney::Schedule::Type>(idx.data(eMyMoney::Model::ScheduleTypeRole).toInt()); 0179 d->m_expandedGroups[groupType] = false; 0180 } 0181 } 0182 0183 void KScheduledView::slotSetSelectedItem(const QItemSelection& selected, const QItemSelection& deselected ) 0184 { 0185 Q_UNUSED(deselected) 0186 SelectedObjects selections; 0187 0188 if (!selected.isEmpty()) { 0189 QModelIndexList idxList = selected.indexes(); 0190 if (!idxList.isEmpty()) { 0191 const auto objId = selected.indexes().front().data(eMyMoney::Model::IdRole).toString(); 0192 selections.addSelection(SelectedObjects::Schedule, objId); 0193 } 0194 } 0195 Q_EMIT requestSelectionChange(selections); 0196 } 0197 0198 void KScheduledView::slotNewSchedule() 0199 { 0200 KEditScheduleDlg::newSchedule(MyMoneyTransaction(), eMyMoney::Schedule::Occurrence::Monthly); 0201 } 0202 0203 void KScheduledView::slotEditSchedule() 0204 { 0205 Q_D(KScheduledView); 0206 auto scheduleId = d->m_currentSchedule.id(); 0207 if (scheduleId.isEmpty()) { 0208 scheduleId = pActions[eMenu::Action::EditSchedule]->data().toString(); 0209 } 0210 try { 0211 auto schedule = MyMoneyFile::instance()->schedule(scheduleId); 0212 KEditScheduleDlg::editSchedule(schedule); 0213 } catch (const MyMoneyException &e) { 0214 KMessageBox::detailedError(this, i18n("Unknown scheduled transaction '%1'", d->m_currentSchedule.name()), QString::fromLatin1(e.what())); 0215 } 0216 } 0217 0218 void KScheduledView::slotDeleteSchedule() 0219 { 0220 Q_D(KScheduledView); 0221 if (!d->m_currentSchedule.id().isEmpty()) { 0222 MyMoneyFileTransaction ft; 0223 try { 0224 MyMoneySchedule sched = MyMoneyFile::instance()->schedule(d->m_currentSchedule.id()); 0225 QString msg = i18n("<p>Are you sure you want to delete the scheduled transaction <b>%1</b>?</p>", d->m_currentSchedule.name()); 0226 if (sched.type() == eMyMoney::Schedule::Type::LoanPayment) { 0227 msg += QString(" "); 0228 msg += i18n("In case of loan payments it is currently not possible to recreate the scheduled transaction."); 0229 } 0230 if (KMessageBox::questionTwoActions(parentWidget(), msg, i18nc("@title:window", "Delete schedule"), KMMYesNo::yes(), KMMYesNo::no()) 0231 == KMessageBox::SecondaryAction) 0232 return; 0233 0234 MyMoneyFile::instance()->removeSchedule(sched); 0235 ft.commit(); 0236 0237 } catch (const MyMoneyException &e) { 0238 KMessageBox::detailedError(this, i18n("Unable to remove scheduled transaction '%1'", d->m_currentSchedule.name()), QString::fromLatin1(e.what())); 0239 } 0240 } 0241 } 0242 0243 void KScheduledView::slotDuplicateSchedule() 0244 { 0245 Q_D(KScheduledView); 0246 // since we may jump here via code, we have to make sure to react only 0247 // if the action is enabled 0248 if (pActions[eMenu::Action::DuplicateSchedule]->isEnabled()) { 0249 MyMoneySchedule sch = d->m_currentSchedule; 0250 sch.clearId(); 0251 sch.setLastPayment(QDate()); 0252 sch.setName(i18nc("Copy of scheduled transaction name", "Copy of %1", sch.name())); 0253 // make sure that we set a valid next due date if the original next due date is invalid 0254 if (!sch.nextDueDate().isValid()) 0255 sch.setNextDueDate(QDate::currentDate()); 0256 0257 MyMoneyFileTransaction ft; 0258 try { 0259 MyMoneyFile::instance()->addSchedule(sch); 0260 ft.commit(); 0261 0262 // select the new schedule in the view 0263 const auto indexes = d->m_filterModel->match(d->m_filterModel->index(0, 0), 0264 eMyMoney::Model::IdRole, 0265 QVariant(sch.id()), 0266 1, 0267 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); 0268 if (!indexes.isEmpty()) { 0269 d->ui->m_scheduleTree->setCurrentIndex(indexes.at(0)); 0270 } 0271 0272 } catch (const MyMoneyException &e) { 0273 KMessageBox::detailedError(this, i18n("Unable to duplicate scheduled transaction: '%1'", d->m_currentSchedule.name()), QString::fromLatin1(e.what())); 0274 } 0275 } 0276 } 0277 0278 void KScheduledView::slotEnterSchedule() 0279 { 0280 Q_D(KScheduledView); 0281 auto scheduleId = d->m_currentSchedule.id(); 0282 if (scheduleId.isEmpty()) { 0283 scheduleId = pActions[eMenu::Action::EnterSchedule]->data().toString(); 0284 } 0285 if (!scheduleId.isEmpty()) { 0286 try { 0287 auto schedule = MyMoneyFile::instance()->schedule(scheduleId); 0288 d->enterSchedule(schedule); 0289 } catch (const MyMoneyException &e) { 0290 KMessageBox::detailedError(this, i18n("Unknown scheduled transaction '%1'", d->m_currentSchedule.name()), QString::fromLatin1(e.what())); 0291 } 0292 } 0293 } 0294 0295 void KScheduledView::slotSkipSchedule() 0296 { 0297 Q_D(KScheduledView); 0298 auto scheduleId = d->m_currentSchedule.id(); 0299 if (scheduleId.isEmpty()) { 0300 scheduleId = pActions[eMenu::Action::SkipSchedule]->data().toString(); 0301 } 0302 if (!scheduleId.isEmpty()) { 0303 try { 0304 auto schedule = MyMoneyFile::instance()->schedule(scheduleId); 0305 d->skipSchedule(schedule); 0306 } catch (const MyMoneyException &e) { 0307 KMessageBox::detailedError(this, i18n("Unknown scheduled transaction '%1'", d->m_currentSchedule.name()), QString::fromLatin1(e.what())); 0308 } 0309 } 0310 }