File indexing completed on 2024-05-12 16:42:05

0001 /*
0002     SPDX-FileCopyrightText: 2007-2012 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 "kenterscheduledlg.h"
0008 
0009 // ----------------------------------------------------------------------------
0010 // QT Includes
0011 
0012 #include <QTimer>
0013 #include <QWidget>
0014 #include <QLabel>
0015 #include <QPushButton>
0016 #include <QIcon>
0017 #include <QWindow>
0018 
0019 // ----------------------------------------------------------------------------
0020 // KDE Includes
0021 
0022 #include <KMessageBox>
0023 #include <KHelpClient>
0024 #include <KLocalizedString>
0025 #include <KGuiItem>
0026 #include <KStandardGuiItem>
0027 #include <KSharedConfig>
0028 #include <KWindowConfig>
0029 #include <KConfigGroup>
0030 
0031 // ----------------------------------------------------------------------------
0032 // Project Includes
0033 
0034 #include "ui_kenterscheduledlg.h"
0035 
0036 #include "tabbar.h"
0037 #include "mymoneysplit.h"
0038 #include "mymoneytransaction.h"
0039 #include "mymoneyfile.h"
0040 #include "mymoneyaccount.h"
0041 #include "mymoneymoney.h"
0042 #include "mymoneyschedule.h"
0043 #include "register.h"
0044 #include "transactionform.h"
0045 #include "transaction.h"
0046 #include "selectedtransactions.h"
0047 #include "transactioneditor.h"
0048 #include "kmymoneyutils.h"
0049 #include "kmymoneylineedit.h"
0050 #include "kmymoneydateinput.h"
0051 #include "knewaccountdlg.h"
0052 #include "knewinvestmentwizard.h"
0053 #include "mymoneyexception.h"
0054 #include "icons/icons.h"
0055 #include "mymoneyenums.h"
0056 #include "dialogenums.h"
0057 #include "widgetenums.h"
0058 
0059 using namespace Icons;
0060 
0061 class KEnterScheduleDlgPrivate
0062 {
0063     Q_DISABLE_COPY(KEnterScheduleDlgPrivate)
0064 
0065 public:
0066     KEnterScheduleDlgPrivate() :
0067         ui(new Ui::KEnterScheduleDlg),
0068         m_item(nullptr),
0069         m_showWarningOnce(true),
0070         m_extendedReturnCode(eDialogs::ScheduleResultCode::Cancel)
0071     {
0072     }
0073 
0074     ~KEnterScheduleDlgPrivate()
0075     {
0076         delete ui;
0077     }
0078 
0079     Ui::KEnterScheduleDlg         *ui;
0080     MyMoneySchedule                m_schedule;
0081     KMyMoneyRegister::Transaction* m_item;
0082     QWidgetList                    m_tabOrderWidgets;
0083     bool                           m_showWarningOnce;
0084     eDialogs::ScheduleResultCode m_extendedReturnCode;
0085 };
0086 
0087 KEnterScheduleDlg::KEnterScheduleDlg(QWidget *parent, const MyMoneySchedule& schedule) :
0088     QDialog(parent),
0089     d_ptr(new KEnterScheduleDlgPrivate)
0090 {
0091     Q_D(KEnterScheduleDlg);
0092 
0093     // restore the last used dialog size
0094     KConfigGroup grp = KSharedConfig::openConfig()->group("KEnterScheduleDlg");
0095     if (grp.isValid()) {
0096         KWindowConfig::restoreWindowSize(windowHandle(), grp);
0097     }
0098     // let the minimum size be 780x410
0099     resize(QSize(780, 410).expandedTo(windowHandle() ? windowHandle()->size() : QSize()));
0100 
0101     // position the dialog centered on the application (for some reason without
0102     // a call to winId() the dialog is positioned in the upper left corner of
0103     // the screen, but winId() crashes on MS-Windows ...
0104     if (parent)
0105         move(parent->pos() + QPoint(parent->width()/2, parent->height()/2) - QPoint(width()/2, height()/2));
0106 
0107     d->ui->setupUi(this);
0108     d->m_schedule = schedule;
0109     d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Enter;
0110     d->ui->buttonOk->setIcon(Icons::get(Icon::KeyEnter));
0111     d->ui->buttonSkip->setIcon(Icons::get(Icon::SeekForward));
0112     KGuiItem::assign(d->ui->buttonCancel, KStandardGuiItem::cancel());
0113     KGuiItem::assign(d->ui->buttonHelp, KStandardGuiItem::help());
0114     d->ui->buttonIgnore->setHidden(true);
0115     d->ui->buttonSkip->setHidden(true);
0116 
0117     // make sure, we have a tabbar with the form
0118     KMyMoneyTransactionForm::TabBar* tabbar = d->ui->m_form->getTabBar(d->ui->m_form->parentWidget());
0119 
0120     // we never need to see the register
0121     d->ui->m_register->hide();
0122 
0123     // ... setup the form ...
0124     d->ui->m_form->setupForm(d->m_schedule.account());
0125 
0126     // ... and the register ...
0127     d->ui->m_register->clear();
0128 
0129     // ... now add the transaction to register and form ...
0130     MyMoneyTransaction t = transaction();
0131     d->m_item = KMyMoneyRegister::Register::transactionFactory(d->ui->m_register, t,
0132                 d->m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : d->m_schedule.transaction().splits().front(), 0);
0133     d->ui->m_register->selectItem(d->m_item);
0134     // show the account row
0135     d->m_item->setShowRowInForm(0, true);
0136 
0137     d->ui->m_form->slotSetTransaction(d->m_item);
0138 
0139     // no need to see the tabbar
0140     tabbar->hide();
0141 
0142     // setup name and type
0143     d->ui->m_scheduleName->setText(d->m_schedule.name());
0144     d->ui->m_type->setText(KMyMoneyUtils::scheduleTypeToString(d->m_schedule.type()));
0145 
0146     connect(d->ui->buttonHelp, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotShowHelp);
0147     connect(d->ui->buttonIgnore, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotIgnore);
0148     connect(d->ui->buttonSkip, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotSkip);
0149 }
0150 
0151 KEnterScheduleDlg::~KEnterScheduleDlg()
0152 {
0153     Q_D(KEnterScheduleDlg);
0154 
0155     // store the last used dialog size
0156     KConfigGroup grp = KSharedConfig::openConfig()->group("KEnterScheduleDlg");
0157     if (grp.isValid()) {
0158         KWindowConfig::saveWindowSize(windowHandle(), grp);
0159     }
0160 
0161     delete d;
0162 }
0163 
0164 eDialogs::ScheduleResultCode KEnterScheduleDlg::resultCode() const
0165 {
0166     Q_D(const KEnterScheduleDlg);
0167     if (result() == QDialog::Accepted)
0168         return d->m_extendedReturnCode;
0169     return eDialogs::ScheduleResultCode::Cancel;
0170 }
0171 
0172 void KEnterScheduleDlg::showExtendedKeys(bool visible)
0173 {
0174     Q_D(KEnterScheduleDlg);
0175     d->ui->buttonIgnore->setVisible(visible);
0176     d->ui->buttonSkip->setVisible(visible);
0177 }
0178 
0179 void KEnterScheduleDlg::slotIgnore()
0180 {
0181     Q_D(KEnterScheduleDlg);
0182     d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Ignore;
0183     accept();
0184 }
0185 
0186 void KEnterScheduleDlg::slotSkip()
0187 {
0188     Q_D(KEnterScheduleDlg);
0189     d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Skip;
0190     accept();
0191 }
0192 
0193 MyMoneyTransaction KEnterScheduleDlg::transaction()
0194 {
0195     Q_D(KEnterScheduleDlg);
0196     auto t = d->m_schedule.transaction();
0197 
0198     try {
0199         if (d->m_schedule.type() == eMyMoney::Schedule::Type::LoanPayment) {
0200             KMyMoneyUtils::calculateAutoLoan(d->m_schedule, t, QMap<QString, MyMoneyMoney>());
0201         }
0202     } catch (const MyMoneyException &e) {
0203         KMessageBox::detailedError(this, i18n("Unable to load schedule details"), QString::fromLatin1(e.what()));
0204     }
0205 
0206     t.clearId();
0207     t.setEntryDate(QDate());
0208     return t;
0209 }
0210 
0211 QDate KEnterScheduleDlg::date(const QDate& _date) const
0212 {
0213     Q_D(const KEnterScheduleDlg);
0214     auto date(_date);
0215     return d->m_schedule.adjustedDate(date, d->m_schedule.weekendOption());
0216 }
0217 
0218 void KEnterScheduleDlg::resizeEvent(QResizeEvent* ev)
0219 {
0220     Q_UNUSED(ev)
0221     Q_D(KEnterScheduleDlg);
0222     d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail);
0223     d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1);
0224     QDialog::resizeEvent(ev);
0225 }
0226 
0227 void KEnterScheduleDlg::slotSetupSize()
0228 {
0229     resize(width(), minimumSizeHint().height());
0230 }
0231 
0232 int KEnterScheduleDlg::exec()
0233 {
0234     Q_D(KEnterScheduleDlg);
0235     if (d->m_showWarningOnce) {
0236         d->m_showWarningOnce = false;
0237         KMessageBox::information(parentWidget(), QString("<qt>") + i18n("<p>Please check that all the details in the following dialog are correct and press OK.</p><p>Editable data can be changed and can either be applied to just this occurrence or for all subsequent occurrences for this schedule.  (You will be asked what you intend after pressing OK in the following dialog)</p>") + QString("</qt>"), i18n("Enter scheduled transaction"), "EnterScheduleDlgInfo");
0238     }
0239 
0240     // force the initial height to be as small as possible
0241     QTimer::singleShot(0, this, SLOT(slotSetupSize()));
0242     return QDialog::exec();
0243 }
0244 
0245 TransactionEditor* KEnterScheduleDlg::startEdit()
0246 {
0247     Q_D(KEnterScheduleDlg);
0248     KMyMoneyRegister::SelectedTransactions list(d->ui->m_register);
0249     auto editor = d->m_item->createEditor(d->ui->m_form, list, QDate());
0250     if (editor) {
0251         editor->setScheduleInfo(d->m_schedule.name());
0252         editor->setPaymentMethod(d->m_schedule.paymentType());
0253     }
0254 
0255     // check that we use the same transaction commodity in all selected transactions
0256     // if not, we need to update this in the editor's list. The user can also bail out
0257     // of this operation which means that we have to stop editing here.
0258     if (editor) {
0259         if (!editor->fixTransactionCommodity(d->m_schedule.account())) {
0260             // if the user wants to quit, we need to destroy the editor
0261             // and bail out
0262             delete editor;
0263             editor = 0;
0264         }
0265     }
0266 
0267     if (editor) {
0268         connect(editor, &TransactionEditor::transactionDataSufficient, d->ui->buttonOk, &QWidget::setEnabled);
0269         connect(editor, &TransactionEditor::escapePressed, d->ui->buttonCancel, &QAbstractButton::animateClick);
0270         connect(editor, &TransactionEditor::returnPressed, d->ui->buttonOk, &QAbstractButton::animateClick);
0271 
0272         connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets);
0273         // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions)));
0274 
0275         // create the widgets, place them in the parent and load them with data
0276         // setup tab order
0277         d->m_tabOrderWidgets.clear();
0278         eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal;
0279         switch (d->m_schedule.type()) {
0280         case eMyMoney::Schedule::Type::Transfer:
0281             action = eWidgets::eRegister::Action::Transfer;
0282             break;
0283         case eMyMoney::Schedule::Type::Deposit:
0284             action = eWidgets::eRegister::Action::Deposit;
0285             break;
0286         case eMyMoney::Schedule::Type::LoanPayment:
0287             switch (d->m_schedule.paymentType()) {
0288             case eMyMoney::Schedule::PaymentType::DirectDeposit:
0289             case eMyMoney::Schedule::PaymentType::ManualDeposit:
0290                 action = eWidgets::eRegister::Action::Deposit;
0291                 break;
0292             default:
0293                 break;
0294             }
0295             break;
0296         default:
0297             break;
0298         }
0299         editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action);
0300 
0301         MyMoneyTransaction t = d->m_schedule.transaction();
0302         QString num = t.splits().first().number();
0303         QWidget* w = editor->haveWidget("number");
0304         if (d->m_schedule.paymentType() == eMyMoney::Schedule::PaymentType::WriteChecque) {
0305             num = KMyMoneyUtils::nextFreeCheckNumber(d->m_schedule.account());
0306             d->m_schedule.account().setValue("lastNumberUsed", num);
0307             if (w)
0308                 if (auto numberWidget = dynamic_cast<KMyMoneyLineEdit*>(w))
0309                     numberWidget->loadText(num);
0310         }
0311 
0312         Q_ASSERT(!d->m_tabOrderWidgets.isEmpty());
0313 
0314         // editor->setup() leaves the tabbar as the last widget in the stack, but we
0315         // need it as first here. So we move it around.
0316         w = editor->haveWidget("tabbar");
0317         if (w) {
0318             int idx = d->m_tabOrderWidgets.indexOf(w);
0319             if (idx != -1) {
0320                 d->m_tabOrderWidgets.removeAt(idx);
0321                 d->m_tabOrderWidgets.push_front(w);
0322             }
0323         }
0324 
0325         // don't forget our three buttons
0326         d->m_tabOrderWidgets.append(d->ui->buttonOk);
0327         d->m_tabOrderWidgets.append(d->ui->buttonCancel);
0328         d->m_tabOrderWidgets.append(d->ui->buttonHelp);
0329 
0330         for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) {
0331             w = d->m_tabOrderWidgets.at(i);
0332             if (w) {
0333                 w->installEventFilter(this);
0334                 w->installEventFilter(editor);
0335             }
0336         }
0337         // Check if the editor has some preference on where to set the focus
0338         // If not, set the focus to the first widget in the tab order
0339         QWidget* focusWidget = editor->firstWidget();
0340         if (!focusWidget)
0341             focusWidget = d->m_tabOrderWidgets.first();
0342         focusWidget->setFocus();
0343 
0344         // Make sure, we use the adjusted date
0345         if (auto dateEdit = dynamic_cast<KMyMoneyDateInput*>(editor->haveWidget("postdate")))
0346             dateEdit->setDate(d->m_schedule.adjustedNextDueDate());
0347     }
0348 
0349     return editor;
0350 }
0351 
0352 bool KEnterScheduleDlg::focusNextPrevChild(bool next)
0353 {
0354     Q_D(KEnterScheduleDlg);
0355     auto rc = false;
0356 
0357     auto w = qApp->focusWidget();
0358     int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
0359     while (w && currentWidgetIndex == -1) {
0360         // qDebug("'%s' not in list, use parent", w->className());
0361         w = w->parentWidget();
0362         currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w);
0363     }
0364 
0365     if (currentWidgetIndex != -1) {
0366         // if(w) qDebug("tab order is at '%s'", w->className());
0367         currentWidgetIndex += next ? 1 : -1;
0368         if (currentWidgetIndex < 0)
0369             currentWidgetIndex = d->m_tabOrderWidgets.size() - 1;
0370         else if (currentWidgetIndex >= d->m_tabOrderWidgets.size())
0371             currentWidgetIndex = 0;
0372 
0373         w = d->m_tabOrderWidgets[currentWidgetIndex];
0374         // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w);
0375 
0376         if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) {
0377             // qDebug("Selecting '%s' as focus", w->className());
0378             w->setFocus(next ? Qt::TabFocusReason: Qt::BacktabFocusReason);
0379             rc = true;
0380         }
0381     }
0382     return rc;
0383 }
0384 
0385 void KEnterScheduleDlg::slotShowHelp()
0386 {
0387     KHelpClient::invokeHelp("details.schedules.entering");
0388 }