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 }