File indexing completed on 2024-05-12 16:42:05
0001 /* 0002 SPDX-FileCopyrightText: 2007-2018 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "keditscheduledlg.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QTimer> 0013 #include <QCheckBox> 0014 #include <QLabel> 0015 #include <QList> 0016 #include <QPushButton> 0017 0018 // ---------------------------------------------------------------------------- 0019 // KDE Includes 0020 0021 #include <KStandardGuiItem> 0022 #include <KLineEdit> 0023 #include <KHelpClient> 0024 #include <KMessageBox> 0025 #include <KLocalizedString> 0026 0027 // ---------------------------------------------------------------------------- 0028 // Project Includes 0029 0030 #include "ui_keditscheduledlg.h" 0031 0032 #include "tabbar.h" 0033 #include "mymoneyexception.h" 0034 #include "mymoneyfile.h" 0035 #include "mymoneyaccount.h" 0036 #include "mymoneymoney.h" 0037 #include "mymoneyschedule.h" 0038 #include "mymoneysplit.h" 0039 #include "mymoneytransaction.h" 0040 #include "register.h" 0041 #include "transactionform.h" 0042 #include "transaction.h" 0043 #include "selectedtransactions.h" 0044 #include "transactioneditor.h" 0045 #include "kmymoneylineedit.h" 0046 #include "kmymoneydateinput.h" 0047 #include "kmymoneymvccombo.h" 0048 #include "kguiutils.h" 0049 #include "kmymoneyutils.h" 0050 #include "knewaccountdlg.h" 0051 #include "knewinvestmentwizard.h" 0052 #include "keditloanwizard.h" 0053 #include "kmymoneysettings.h" 0054 #include "mymoneyenums.h" 0055 #include "widgetenums.h" 0056 0057 using namespace eMyMoney; 0058 0059 class KEditScheduleDlgPrivate 0060 { 0061 Q_DISABLE_COPY(KEditScheduleDlgPrivate) 0062 Q_DECLARE_PUBLIC(KEditScheduleDlg) 0063 0064 public: 0065 explicit KEditScheduleDlgPrivate(KEditScheduleDlg *qq) : 0066 q_ptr(qq), 0067 ui(new Ui::KEditScheduleDlg), 0068 m_item(nullptr), 0069 m_editor(nullptr), 0070 m_requiredFields(nullptr) 0071 { 0072 } 0073 0074 ~KEditScheduleDlgPrivate() 0075 { 0076 delete ui; 0077 } 0078 0079 void init() 0080 { 0081 Q_Q(KEditScheduleDlg); 0082 ui->setupUi(q); 0083 0084 m_requiredFields = new KMandatoryFieldGroup(q); 0085 m_requiredFields->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present 0086 0087 // make sure, we have a tabbar with the form 0088 // insert it after the horizontal line 0089 ui->m_paymentInformationLayout->insertWidget(2, ui->m_form->getTabBar(ui->m_form->parentWidget())); 0090 0091 // we never need to see the register 0092 ui->m_register->hide(); 0093 0094 // ... setup the form ... 0095 ui->m_form->setupForm(m_schedule.account()); 0096 0097 // ... and the register ... 0098 ui->m_register->clear(); 0099 0100 // ... now add the transaction to register and form ... 0101 auto t = transaction(); 0102 if (m_schedule.transaction().splits().isEmpty()) 0103 m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, MyMoneySplit(), 0); 0104 else 0105 m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, 0106 m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), 0); 0107 ui->m_register->selectItem(m_item); 0108 // show the account row 0109 m_item->setShowRowInForm(0, true); 0110 0111 ui->m_form->slotSetTransaction(m_item); 0112 0113 // setup widget contents 0114 ui->m_nameEdit->setText(m_schedule.name()); 0115 0116 ui->m_frequencyEdit->setCurrentItem((int)m_schedule.occurrence()); 0117 if (ui->m_frequencyEdit->currentItem() == Schedule::Occurrence::Any) 0118 ui->m_frequencyEdit->setCurrentItem((int)Schedule::Occurrence::Monthly); 0119 q->slotFrequencyChanged((int)ui->m_frequencyEdit->currentItem()); 0120 ui->m_frequencyNoEdit->setValue(m_schedule.occurrenceMultiplier()); 0121 0122 // load option widgets 0123 ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); 0124 ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); 0125 ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); 0126 ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); 0127 ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); 0128 ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); 0129 ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); 0130 0131 auto method = m_schedule.paymentType(); 0132 if (method == Schedule::PaymentType::Any) 0133 method = Schedule::PaymentType::Other; 0134 ui->m_paymentMethodEdit->setCurrentItem((int)method); 0135 0136 switch (m_schedule.weekendOption()) { 0137 case Schedule::WeekendOption::MoveNothing: 0138 ui->m_weekendOptionEdit->setCurrentIndex(0); 0139 break; 0140 case Schedule::WeekendOption::MoveBefore: 0141 ui->m_weekendOptionEdit->setCurrentIndex(1); 0142 break; 0143 case Schedule::WeekendOption::MoveAfter: 0144 ui->m_weekendOptionEdit->setCurrentIndex(2); 0145 break; 0146 } 0147 ui->m_estimateEdit->setChecked(!m_schedule.isFixed()); 0148 ui->m_lastDayInMonthEdit->setChecked(m_schedule.lastDayInMonth()); 0149 ui->m_autoEnterEdit->setChecked(m_schedule.autoEnter()); 0150 ui->m_endSeriesEdit->setChecked(m_schedule.willEnd()); 0151 0152 ui->m_endOptionsFrame->setEnabled(m_schedule.willEnd()); 0153 if (m_schedule.willEnd()) { 0154 ui->m_RemainingEdit->setValue(m_schedule.transactionsRemaining()); 0155 ui->m_FinalPaymentEdit->setDate(m_schedule.endDate()); 0156 } 0157 0158 q->connect(ui->m_RemainingEdit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0159 q, &KEditScheduleDlg::slotRemainingChanged); 0160 q->connect(ui->m_FinalPaymentEdit, &KMyMoneyDateInput::dateChanged, 0161 q, &KEditScheduleDlg::slotEndDateChanged); 0162 q->connect(ui->m_frequencyEdit, &KMyMoneyGeneralCombo::itemSelected, 0163 q, &KEditScheduleDlg::slotFrequencyChanged); 0164 q->connect(ui->m_frequencyNoEdit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0165 q, &KEditScheduleDlg::slotOccurrenceMultiplierChanged); 0166 q->connect(ui->buttonBox, &QDialogButtonBox::helpRequested, q, &KEditScheduleDlg::slotShowHelp); 0167 0168 q->setModal(true); 0169 // force the initial height to be as small as possible 0170 QTimer::singleShot(0, q, SLOT(slotSetupSize())); 0171 0172 // we just hide the variation field for now and enable the logic 0173 // once we have a respective member in the MyMoneySchedule object 0174 ui->m_variation->hide(); 0175 } 0176 0177 /** 0178 * Helper method to recalculate and update Transactions Remaining 0179 * when other values are changed 0180 */ 0181 void updateTransactionsRemaining() 0182 { 0183 auto remain = m_schedule.transactionsRemaining(); 0184 if (remain != ui->m_RemainingEdit->value()) { 0185 QSignalBlocker blocker(ui->m_RemainingEdit); 0186 ui->m_RemainingEdit->setValue(remain); 0187 } 0188 } 0189 0190 MyMoneyTransaction transaction() const 0191 { 0192 auto t = m_schedule.transaction(); 0193 0194 if (m_editor) { 0195 m_editor->createTransaction(t, m_schedule.transaction(), m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), false); 0196 } 0197 0198 // make sure all splits in the scheduled transaction 0199 // reference the same payee 0200 if (t.splitCount() > 1) { 0201 const auto payeeId = t.splits().first().payeeId(); 0202 for (auto split : t.splits()) { 0203 if (split.payeeId() != payeeId) { 0204 split.setPayeeId(payeeId); 0205 t.modifySplit(split); 0206 } 0207 } 0208 } 0209 t.clearId(); 0210 t.setEntryDate(QDate()); 0211 return t; 0212 } 0213 0214 KEditScheduleDlg *q_ptr; 0215 Ui::KEditScheduleDlg *ui; 0216 MyMoneySchedule m_schedule; 0217 KMyMoneyRegister::Transaction* m_item; 0218 QWidgetList m_tabOrderWidgets; 0219 TransactionEditor* m_editor; 0220 KMandatoryFieldGroup* m_requiredFields; 0221 }; 0222 0223 KEditScheduleDlg::KEditScheduleDlg(const MyMoneySchedule& schedule, QWidget *parent) : 0224 QDialog(parent), 0225 d_ptr(new KEditScheduleDlgPrivate(this)) 0226 { 0227 Q_D(KEditScheduleDlg); 0228 d->m_schedule = schedule; 0229 d->m_editor = 0; 0230 d->init(); 0231 } 0232 0233 KEditScheduleDlg::~KEditScheduleDlg() 0234 { 0235 Q_D(KEditScheduleDlg); 0236 delete d; 0237 } 0238 0239 void KEditScheduleDlg::slotSetupSize() 0240 { 0241 resize(width(), minimumSizeHint().height()); 0242 } 0243 0244 TransactionEditor* KEditScheduleDlg::startEdit() 0245 { 0246 Q_D(KEditScheduleDlg); 0247 KMyMoneyRegister::SelectedTransactions list(d->ui->m_register); 0248 TransactionEditor* editor = d->m_item->createEditor(d->ui->m_form, list, QDate()); 0249 0250 // check that we use the same transaction commodity in all selected transactions 0251 // if not, we need to update this in the editor's list. The user can also bail out 0252 // of this operation which means that we have to stop editing here. 0253 if (editor && !d->m_schedule.account().id().isEmpty()) { 0254 if (!editor->fixTransactionCommodity(d->m_schedule.account())) { 0255 // if the user wants to quit, we need to destroy the editor 0256 // and bail out 0257 delete editor; 0258 editor = 0; 0259 } 0260 } 0261 0262 if (editor) { 0263 editor->setScheduleInfo(d->ui->m_nameEdit->text()); 0264 connect(editor, &TransactionEditor::transactionDataSufficient, d->m_requiredFields, &KMandatoryFieldGroup::setExternalMandatoryState); 0265 connect(editor, &TransactionEditor::escapePressed, d->ui->buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::animateClick); 0266 connect(editor, &TransactionEditor::returnPressed, d->ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::animateClick); 0267 0268 connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); 0269 // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); 0270 connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); 0271 0272 // create the widgets, place them in the parent and load them with data 0273 // setup tab order 0274 d->m_tabOrderWidgets.clear(); 0275 eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal; 0276 switch (d->m_schedule.type()) { 0277 case Schedule::Type::Deposit: 0278 action = eWidgets::eRegister::Action::Deposit; 0279 break; 0280 case Schedule::Type::Bill: 0281 action = eWidgets::eRegister::Action::Withdrawal; 0282 editor->setPaymentMethod(d->m_schedule.paymentType()); 0283 break; 0284 case Schedule::Type::Transfer: 0285 action = eWidgets::eRegister::Action::Transfer; 0286 break; 0287 default: 0288 // if we end up here, we don't have a known schedule type (yet). in this case, we just glimpse 0289 // into the transaction and determine the type. in case we don't have a transaction with splits 0290 // we stick with the default action already set up 0291 if (d->m_schedule.transaction().splits().count() > 0) { 0292 auto isDeposit = false; 0293 auto isTransfer = false; 0294 auto splits = d->m_schedule.transaction().splits(); 0295 foreach (const auto split, splits) { 0296 if (split.accountId() == d->m_schedule.account().id()) { 0297 isDeposit = !(split.shares().isNegative()); 0298 } else { 0299 auto acc = MyMoneyFile::instance()->account(split.accountId()); 0300 if (acc.isAssetLiability() && d->m_schedule.transaction().splits().count() == 2) { 0301 isTransfer = true; 0302 } 0303 } 0304 } 0305 0306 if (isTransfer) 0307 action = eWidgets::eRegister::Action::Transfer; 0308 else if (isDeposit) 0309 action = eWidgets::eRegister::Action::Deposit; 0310 } 0311 break; 0312 } 0313 editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); 0314 0315 Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); 0316 0317 d->m_tabOrderWidgets.push_front(d->ui->m_paymentMethodEdit); 0318 0319 // editor->setup() leaves the tabbar as the last widget in the stack, but we 0320 // need it as first here. So we move it around. 0321 QWidget* w = editor->haveWidget("tabbar"); 0322 if (w) { 0323 int idx = d->m_tabOrderWidgets.indexOf(w); 0324 if (idx != -1) { 0325 d->m_tabOrderWidgets.removeAt(idx); 0326 d->m_tabOrderWidgets.push_front(w); 0327 } 0328 } 0329 0330 // don't forget our three buttons and additional widgets 0331 // make sure to use the correct order 0332 d->m_tabOrderWidgets.push_front(d->ui->m_frequencyEdit); 0333 d->m_tabOrderWidgets.push_front(d->ui->m_frequencyNoEdit); 0334 d->m_tabOrderWidgets.push_front(d->ui->m_nameEdit); 0335 0336 d->m_tabOrderWidgets.append(d->ui->m_weekendOptionEdit); 0337 d->m_tabOrderWidgets.append(d->ui->m_estimateEdit); 0338 d->m_tabOrderWidgets.append(d->ui->m_variation); 0339 d->m_tabOrderWidgets.append(d->ui->m_lastDayInMonthEdit); 0340 d->m_tabOrderWidgets.append(d->ui->m_autoEnterEdit); 0341 d->m_tabOrderWidgets.append(d->ui->m_endSeriesEdit); 0342 d->m_tabOrderWidgets.append(d->ui->m_RemainingEdit); 0343 d->m_tabOrderWidgets.append(d->ui->m_FinalPaymentEdit); 0344 0345 d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Ok)); 0346 d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Cancel)); 0347 d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Help)); 0348 for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) { 0349 w = d->m_tabOrderWidgets.at(i); 0350 if (w) { 0351 w->installEventFilter(this); 0352 w->installEventFilter(editor); 0353 } 0354 } 0355 0356 // connect the postdate modification signal to our update routine 0357 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(editor->haveWidget("postdate"))) { 0358 connect(dateEdit, &KMyMoneyDateInput::dateChanged, this, &KEditScheduleDlg::slotPostDateChanged); 0359 connect(d->ui->m_lastDayInMonthEdit, &QCheckBox::stateChanged, dateEdit, &KMyMoneyDateInput::setDisabled); 0360 // update initial state 0361 dateEdit->setDisabled(d->ui->m_lastDayInMonthEdit->isChecked()); 0362 } 0363 0364 d->ui->m_nameEdit->setFocus(); 0365 0366 // add the required fields to the mandatory group 0367 d->m_requiredFields->add(d->ui->m_nameEdit); 0368 d->m_requiredFields->add(editor->haveWidget("account")); 0369 d->m_requiredFields->add(editor->haveWidget("category")); 0370 0371 // fix labels 0372 if (auto label = dynamic_cast<QLabel*>(editor->haveWidget("date-label"))) 0373 label->setText(i18n("Next due date")); 0374 0375 d->m_editor = editor; 0376 connect(editor, &TransactionEditor::operationTypeChanged, this, &KEditScheduleDlg::slotFilterPaymentType); 0377 } 0378 0379 return editor; 0380 } 0381 0382 void KEditScheduleDlg::accept() 0383 { 0384 Q_D(KEditScheduleDlg); 0385 // Force the focus to be on the OK button. This will trigger creation 0386 // of any unknown objects (payees, categories etc.) 0387 d->ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); 0388 0389 // only accept if the button is really still enabled. We could end 0390 // up here, if the user filled all fields, the focus is on the category 0391 // field, but the category is not yet existent. When the user presses the 0392 // OK button in this context, he will be asked if he wants to create 0393 // the category or not. In case he decides no, we end up here with no 0394 // category filled in, so we don't run through the final acceptance. 0395 if (d->ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) 0396 QDialog::accept(); 0397 } 0398 0399 const MyMoneySchedule& KEditScheduleDlg::schedule() 0400 { 0401 Q_D(KEditScheduleDlg); 0402 if (d->m_editor) { 0403 auto t = d->transaction(); 0404 if (d->m_schedule.nextDueDate() != t.postDate()) { 0405 d->m_schedule.setNextDueDate(t.postDate()); 0406 d->m_schedule.setStartDate(t.postDate()); 0407 } 0408 d->m_schedule.setTransaction(t); 0409 d->m_schedule.setName(d->ui->m_nameEdit->text()); 0410 d->m_schedule.setFixed(!d->ui->m_estimateEdit->isChecked()); 0411 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(d->ui->m_frequencyEdit->currentItem())); 0412 d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); 0413 0414 switch (d->ui->m_weekendOptionEdit->currentIndex()) { 0415 case 0: 0416 d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveNothing); 0417 break; 0418 case 1: 0419 d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveBefore); 0420 break; 0421 case 2: 0422 d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveAfter); 0423 break; 0424 } 0425 0426 d->m_schedule.setType(Schedule::Type::Bill); 0427 0428 if (auto tabbar = dynamic_cast<KMyMoneyTransactionForm::TabBar*>(d->m_editor->haveWidget("tabbar"))) { 0429 switch (static_cast<eWidgets::eRegister::Action>(tabbar->currentIndex())) { 0430 case eWidgets::eRegister::Action::Deposit: 0431 d->m_schedule.setType(Schedule::Type::Deposit); 0432 break; 0433 default: 0434 case eWidgets::eRegister::Action::Withdrawal: 0435 d->m_schedule.setType(Schedule::Type::Bill); 0436 break; 0437 case eWidgets::eRegister::Action::Transfer: 0438 d->m_schedule.setType(Schedule::Type::Transfer); 0439 break; 0440 } 0441 } else { 0442 qDebug("No tabbar found in KEditScheduleDlg::schedule(). Defaulting type to BILL"); 0443 } 0444 0445 if(d->ui->m_lastDayInMonthEdit->isEnabled()) 0446 d->m_schedule.setLastDayInMonth(d->ui->m_lastDayInMonthEdit->isChecked()); 0447 else 0448 d->m_schedule.setLastDayInMonth(false); 0449 d->m_schedule.setAutoEnter(d->ui->m_autoEnterEdit->isChecked()); 0450 d->m_schedule.setPaymentType(static_cast<Schedule::PaymentType>(d->ui->m_paymentMethodEdit->currentItem())); 0451 if (d->ui->m_endSeriesEdit->isEnabled() && d->ui->m_endSeriesEdit->isChecked()) { 0452 d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); 0453 } else { 0454 d->m_schedule.setEndDate(QDate()); 0455 } 0456 } 0457 return d->m_schedule; 0458 } 0459 0460 void KEditScheduleDlg::newSchedule(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence) 0461 { 0462 MyMoneySchedule schedule; 0463 schedule.setOccurrence(occurrence); 0464 0465 // if the schedule is based on an existing transaction, 0466 // we take the post date and project it to the next 0467 // schedule in a month. 0468 if (_t != MyMoneyTransaction()) { 0469 MyMoneyTransaction t(_t); 0470 schedule.setTransaction(t); 0471 if (occurrence != eMyMoney::Schedule::Occurrence::Once) 0472 schedule.setNextDueDate(schedule.nextPayment(t.postDate())); 0473 } 0474 0475 bool committed; 0476 do { 0477 committed = true; 0478 QPointer<KEditScheduleDlg> dlg = new KEditScheduleDlg(schedule, nullptr); 0479 QPointer<TransactionEditor> transactionEditor = dlg->startEdit(); 0480 KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); 0481 if (dlg->exec() == QDialog::Accepted && dlg != 0) { 0482 MyMoneyFileTransaction ft; 0483 try { 0484 schedule = dlg->schedule(); 0485 MyMoneyFile::instance()->addSchedule(schedule); 0486 ft.commit(); 0487 0488 } catch (const MyMoneyException &e) { 0489 KMessageBox::error(nullptr, i18n("Unable to add scheduled transaction: %1", QString::fromLatin1(e.what())), i18n("Add scheduled transaction")); 0490 committed = false; 0491 } 0492 } 0493 delete transactionEditor; 0494 delete dlg; 0495 } while(!committed); 0496 } 0497 0498 void KEditScheduleDlg::editSchedule(const MyMoneySchedule& inputSchedule) 0499 { 0500 try { 0501 auto schedule = MyMoneyFile::instance()->schedule(inputSchedule.id()); 0502 0503 KEditScheduleDlg* sched_dlg = nullptr; 0504 KEditLoanWizard* loan_wiz = nullptr; 0505 0506 switch (schedule.type()) { 0507 case eMyMoney::Schedule::Type::Bill: 0508 case eMyMoney::Schedule::Type::Deposit: 0509 case eMyMoney::Schedule::Type::Transfer: 0510 { 0511 sched_dlg = new KEditScheduleDlg(schedule, nullptr); 0512 QPointer<TransactionEditor> transactionEditor = sched_dlg->startEdit(); 0513 if (transactionEditor) { 0514 KMyMoneyMVCCombo::setSubstringSearchForChildren(sched_dlg, !KMyMoneySettings::stringMatchFromStart()); 0515 if (sched_dlg->exec() == QDialog::Accepted) { 0516 MyMoneyFileTransaction ft; 0517 try { 0518 MyMoneySchedule sched = sched_dlg->schedule(); 0519 // Check whether the new Schedule Date 0520 // is at or before the lastPaymentDate 0521 // If it is, ask the user whether to clear the 0522 // lastPaymentDate 0523 const auto& next = sched.nextDueDate(); 0524 const auto& last = sched.lastPayment(); 0525 if (next.isValid() && last.isValid() && next <= last) { 0526 // Entered a date effectively no later 0527 // than previous payment. Date would be 0528 // updated automatically so we probably 0529 // want to clear it. Let's ask the user. 0530 QString questionText = i18n("<qt>You have entered a scheduled transaction date of <b>%1</b>. Because the scheduled transaction was last paid on <b>%2</b>, KMyMoney will automatically adjust the scheduled transaction date to the next date unless the last payment date is reset. Do you want to reset the last payment date?</qt>", QLocale().toString(next, QLocale::ShortFormat), QLocale().toString(last, QLocale::ShortFormat)); 0531 if (KMessageBox::questionYesNo(nullptr, 0532 questionText, 0533 i18n("Reset Last Payment Date"), 0534 KStandardGuiItem::yes(), 0535 KStandardGuiItem::no()) 0536 == KMessageBox::Yes) { 0537 sched.setLastPayment(QDate()); 0538 } 0539 } 0540 MyMoneyFile::instance()->modifySchedule(sched); 0541 // delete the editor before we emit the dataChanged() signal from the 0542 // engine. Calling this twice in a row does not hurt. 0543 delete transactionEditor; 0544 ft.commit(); 0545 } catch (const MyMoneyException &e) { 0546 KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); 0547 } 0548 } 0549 delete transactionEditor; 0550 } 0551 delete sched_dlg; 0552 break; 0553 } 0554 case eMyMoney::Schedule::Type::LoanPayment: 0555 { 0556 loan_wiz = new KEditLoanWizard(schedule.account(2)); 0557 if (loan_wiz->exec() == QDialog::Accepted) { 0558 MyMoneyFileTransaction ft; 0559 try { 0560 MyMoneyFile::instance()->modifySchedule(loan_wiz->schedule()); 0561 MyMoneyFile::instance()->modifyAccount(loan_wiz->account()); 0562 ft.commit(); 0563 } catch (const MyMoneyException &e) { 0564 KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); 0565 } 0566 } 0567 delete loan_wiz; 0568 break; 0569 } 0570 case eMyMoney::Schedule::Type::Any: 0571 break; 0572 } 0573 0574 } catch (const MyMoneyException &e) { 0575 KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); 0576 } 0577 } 0578 0579 bool KEditScheduleDlg::focusNextPrevChild(bool next) 0580 { 0581 Q_D(KEditScheduleDlg); 0582 auto rc = false; 0583 0584 auto w = qApp->focusWidget(); 0585 auto currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); 0586 while (w && currentWidgetIndex == -1) { 0587 // qDebug("'%s' not in list, use parent", qPrintable(w->objectName())); 0588 w = w->parentWidget(); 0589 currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); 0590 } 0591 0592 if (currentWidgetIndex != -1) { 0593 do { 0594 // if(w) qDebug("tab order is at '%s (%d/%d)'", qPrintable(w->objectName()), currentWidgetIndex, d->m_tabOrderWidgets.size()); 0595 currentWidgetIndex += next ? 1 : -1; 0596 if (currentWidgetIndex < 0) 0597 currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; 0598 else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) 0599 currentWidgetIndex = 0; 0600 0601 w = d->m_tabOrderWidgets[currentWidgetIndex]; 0602 // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); 0603 0604 if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { 0605 // qDebug("Selecting '%s' as focus", qPrintable(w->objectName())); 0606 w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason); 0607 rc = true; 0608 } 0609 } while (rc == false); 0610 } 0611 return rc; 0612 } 0613 0614 void KEditScheduleDlg::resizeEvent(QResizeEvent* ev) 0615 { 0616 Q_D(KEditScheduleDlg); 0617 d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail); 0618 d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); 0619 QDialog::resizeEvent(ev); 0620 } 0621 0622 0623 void KEditScheduleDlg::slotRemainingChanged(int value) 0624 { 0625 Q_D(KEditScheduleDlg); 0626 // Make sure the required fields are set 0627 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"))) 0628 d->m_schedule.setNextDueDate(dateEdit->date()); 0629 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(d->ui->m_frequencyEdit->currentItem())); 0630 d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); 0631 0632 QSignalBlocker blocker(d->ui->m_FinalPaymentEdit); 0633 d->ui->m_FinalPaymentEdit->setDate(d->m_schedule.dateAfter(value)); 0634 } 0635 0636 void KEditScheduleDlg::slotEndDateChanged(const QDate& date) 0637 { 0638 Q_D(KEditScheduleDlg); 0639 // Make sure the required fields are set 0640 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"))) 0641 d->m_schedule.setNextDueDate(dateEdit->date()); 0642 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(d->ui->m_frequencyEdit->currentItem())); 0643 d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); 0644 0645 d->m_schedule.setEndDate(date); 0646 d->updateTransactionsRemaining(); 0647 } 0648 0649 void KEditScheduleDlg::slotPostDateChanged(const QDate& date) 0650 { 0651 Q_D(KEditScheduleDlg); 0652 if (d->m_schedule.nextDueDate() != date) { 0653 if (d->ui->m_endOptionsFrame->isEnabled()) { 0654 d->m_schedule.setNextDueDate(date); 0655 d->m_schedule.setStartDate(date); 0656 d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); 0657 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(d->ui->m_frequencyEdit->currentItem())); 0658 d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); 0659 d->updateTransactionsRemaining(); 0660 } 0661 } 0662 } 0663 0664 void KEditScheduleDlg::slotFrequencyChanged(int item) 0665 { 0666 Q_D(KEditScheduleDlg); 0667 d->ui->m_endSeriesEdit->setEnabled(item != (int)Schedule::Occurrence::Once); 0668 bool isEndSeries = d->ui->m_endSeriesEdit->isChecked(); 0669 if (isEndSeries) 0670 d->ui->m_endOptionsFrame->setEnabled(item != (int)Schedule::Occurrence::Once); 0671 switch (item) { 0672 case (int)Schedule::Occurrence::Daily: 0673 case (int)Schedule::Occurrence::Weekly: 0674 d->ui->m_frequencyNoEdit->setEnabled(true); 0675 d->ui->m_lastDayInMonthEdit->setEnabled(false); 0676 break; 0677 0678 case (int)Schedule::Occurrence::EveryHalfMonth: 0679 case (int)Schedule::Occurrence::Monthly: 0680 case (int)Schedule::Occurrence::Yearly: 0681 // Supports Frequency Number 0682 d->ui->m_frequencyNoEdit->setEnabled(true); 0683 d->ui->m_lastDayInMonthEdit->setEnabled(true); 0684 break; 0685 0686 default: 0687 // Multiplier is always 1 0688 d->ui->m_frequencyNoEdit->setEnabled(false); 0689 d->ui->m_frequencyNoEdit->setValue(1); 0690 d->ui->m_lastDayInMonthEdit->setEnabled(true); 0691 break; 0692 } 0693 if (isEndSeries && (item != (int)Schedule::Occurrence::Once)) { 0694 // Changing the frequency changes the number 0695 // of remaining transactions 0696 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"))) 0697 d->m_schedule.setNextDueDate(dateEdit->date()); 0698 d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); 0699 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(item)); 0700 d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); 0701 d->updateTransactionsRemaining(); 0702 } 0703 } 0704 0705 void KEditScheduleDlg::slotOccurrenceMultiplierChanged(int multiplier) 0706 { 0707 Q_D(KEditScheduleDlg); 0708 // Make sure the required fields are set 0709 auto oldOccurrenceMultiplier = d->m_schedule.occurrenceMultiplier(); 0710 if (multiplier != oldOccurrenceMultiplier) { 0711 if (d->ui->m_endOptionsFrame->isEnabled()) { 0712 if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"))) 0713 d->m_schedule.setNextDueDate(dateEdit->date()); 0714 d->m_schedule.setOccurrenceMultiplier(multiplier); 0715 d->m_schedule.setOccurrencePeriod(static_cast<Schedule::Occurrence>(d->ui->m_frequencyEdit->currentItem())); 0716 d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); 0717 d->updateTransactionsRemaining(); 0718 } 0719 } 0720 } 0721 0722 void KEditScheduleDlg::slotShowHelp() 0723 { 0724 KHelpClient::invokeHelp("details.schedules.intro"); 0725 } 0726 0727 void KEditScheduleDlg::slotFilterPaymentType(int index) 0728 { 0729 Q_D(KEditScheduleDlg); 0730 //save selected item to reload if possible 0731 auto selectedId = d->ui->m_paymentMethodEdit->itemData(d->ui->m_paymentMethodEdit->currentIndex(), Qt::UserRole).toInt(); 0732 0733 //clear and reload the widget with the correct items 0734 d->ui->m_paymentMethodEdit->clear(); 0735 0736 // load option widgets 0737 eWidgets::eRegister::Action action = static_cast<eWidgets::eRegister::Action>(index); 0738 if (action != eWidgets::eRegister::Action::Withdrawal) { 0739 d->ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); 0740 d->ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); 0741 } 0742 if (action != eWidgets::eRegister::Action::Deposit) { 0743 d->ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); 0744 d->ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); 0745 } 0746 d->ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); 0747 d->ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); 0748 d->ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); 0749 0750 auto newIndex = d->ui->m_paymentMethodEdit->findData(QVariant(selectedId), Qt::UserRole, Qt::MatchExactly); 0751 if (newIndex > -1) { 0752 d->ui->m_paymentMethodEdit->setCurrentIndex(newIndex); 0753 } else { 0754 d->ui->m_paymentMethodEdit->setCurrentIndex(0); 0755 } 0756 0757 }