File indexing completed on 2024-05-12 16:43:53

0001 /*
0002     SPDX-FileCopyrightText: 2015 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "newtransactioneditor.h"
0007 
0008 // ----------------------------------------------------------------------------
0009 // QT Includes
0010 
0011 #include <QCompleter>
0012 #include <QSortFilterProxyModel>
0013 #include <QStringList>
0014 #include <QDebug>
0015 #include <QGlobalStatic>
0016 #include <QStandardItemModel>
0017 #include <QAbstractItemView>
0018 
0019 // ----------------------------------------------------------------------------
0020 // KDE Includes
0021 
0022 #include <KLocalizedString>
0023 
0024 // ----------------------------------------------------------------------------
0025 // Project Includes
0026 
0027 #include "creditdebithelper.h"
0028 #include "mymoneyfile.h"
0029 #include "mymoneyaccount.h"
0030 #include "mymoneyexception.h"
0031 #include "kmymoneyutils.h"
0032 #include "kmymoneyaccountcombo.h"
0033 #include "models.h"
0034 #include "accountsmodel.h"
0035 #include "costcentermodel.h"
0036 #include "ledgermodel.h"
0037 #include "splitmodel.h"
0038 #include "payeesmodel.h"
0039 #include "mymoneysplit.h"
0040 #include "mymoneytransaction.h"
0041 #include "ui_newtransactioneditor.h"
0042 #include "splitdialog.h"
0043 #include "widgethintframe.h"
0044 #include "icons/icons.h"
0045 #include "modelenums.h"
0046 #include "mymoneyenums.h"
0047 
0048 using namespace Icons;
0049 
0050 Q_GLOBAL_STATIC(QDate, lastUsedPostDate)
0051 
0052 class NewTransactionEditor::Private
0053 {
0054 public:
0055     Private(NewTransactionEditor* parent)
0056         : ui(new Ui_NewTransactionEditor)
0057         , accountsModel(new AccountNamesFilterProxyModel(parent))
0058         , costCenterModel(new QSortFilterProxyModel(parent))
0059         , payeesModel(new QSortFilterProxyModel(parent))
0060         , accepted(false)
0061         , costCenterRequired(false)
0062         , amountHelper(nullptr)
0063     {
0064         accountsModel->setObjectName("NewTransactionEditor::accountsModel");
0065         costCenterModel->setObjectName("SortedCostCenterModel");
0066         payeesModel->setObjectName("SortedPayeesModel");
0067         statusModel.setObjectName("StatusModel");
0068         splitModel.setObjectName("SplitModel");
0069 
0070         costCenterModel->setSortLocaleAware(true);
0071         costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
0072 
0073         payeesModel->setSortLocaleAware(true);
0074         payeesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
0075 
0076         createStatusEntry(eMyMoney::Split::State::NotReconciled);
0077         createStatusEntry(eMyMoney::Split::State::Cleared);
0078         createStatusEntry(eMyMoney::Split::State::Reconciled);
0079         // createStatusEntry(eMyMoney::Split::State::Frozen);
0080     }
0081 
0082     ~Private()
0083     {
0084         delete ui;
0085     }
0086 
0087     void createStatusEntry(eMyMoney::Split::State status);
0088     void updateWidgetState();
0089     bool checkForValidTransaction(bool doUserInteraction = true);
0090     bool isDatePostOpeningDate(const QDate& date, const QString& accountId);
0091 
0092     bool postdateChanged(const QDate& date);
0093     bool costCenterChanged(int costCenterIndex);
0094     bool categoryChanged(const QString& accountId);
0095     bool numberChanged(const QString& newNumber);
0096     bool valueChanged(CreditDebitHelper* valueHelper);
0097 
0098     Ui_NewTransactionEditor*      ui;
0099     AccountNamesFilterProxyModel* accountsModel;
0100     QSortFilterProxyModel*        costCenterModel;
0101     QSortFilterProxyModel*        payeesModel;
0102     bool                          accepted;
0103     bool                          costCenterRequired;
0104     SplitModel                    splitModel;
0105     QStandardItemModel            statusModel;
0106     QString                       transactionSplitId;
0107     MyMoneyAccount                m_account;
0108     MyMoneyTransaction            transaction;
0109     MyMoneySplit                  split;
0110     CreditDebitHelper*            amountHelper;
0111 };
0112 
0113 void NewTransactionEditor::Private::createStatusEntry(eMyMoney::Split::State status)
0114 {
0115     QStandardItem* p = new QStandardItem(KMyMoneyUtils::reconcileStateToString(status, true));
0116     p->setData((int)status);
0117     statusModel.appendRow(p);
0118 }
0119 
0120 void NewTransactionEditor::Private::updateWidgetState()
0121 {
0122     // just in case it is disabled we turn it on
0123     ui->costCenterCombo->setEnabled(true);
0124 
0125     // setup the category/account combo box. If we have a split transaction, we disable the
0126     // combo box altogether. Changes can only be made via the split dialog editor
0127     bool blocked = false;
0128     QModelIndex index;
0129 
0130     // update the category combo box
0131     ui->accountCombo->setEnabled(true);
0132     switch(splitModel.rowCount()) {
0133     case 0:
0134         ui->accountCombo->setSelected(QString());
0135         break;
0136     case 1:
0137         index = splitModel.index(0, 0);
0138         ui->accountCombo->setSelected(splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString());
0139         break;
0140     default:
0141         index = splitModel.index(0, 0);
0142         blocked = ui->accountCombo->lineEdit()->blockSignals(true);
0143         ui->accountCombo->lineEdit()->setText(i18n("Split transaction"));
0144         ui->accountCombo->setDisabled(true);
0145         ui->accountCombo->lineEdit()->blockSignals(blocked);
0146         ui->costCenterCombo->setDisabled(true);
0147         ui->costCenterLabel->setDisabled(true);
0148         break;
0149     }
0150     ui->accountCombo->hidePopup();
0151 
0152     // update the costcenter combo box
0153     if(ui->costCenterCombo->isEnabled()) {
0154         // extract the cost center
0155         index = splitModel.index(0, 0);
0156         QModelIndexList ccList = costCenterModel->match(costCenterModel->index(0, 0), CostCenterModel::CostCenterIdRole,
0157                                  splitModel.data(index, (int)eLedgerModel::Role::CostCenterId),
0158                                  1,
0159                                  Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
0160         if (ccList.count() > 0) {
0161             index = ccList.front();
0162             ui->costCenterCombo->setCurrentIndex(index.row());
0163         }
0164     }
0165 }
0166 
0167 bool NewTransactionEditor::Private::checkForValidTransaction(bool doUserInteraction)
0168 {
0169     QStringList infos;
0170     bool rc = true;
0171     if(!postdateChanged(ui->dateEdit->date())) {
0172         infos << ui->dateEdit->toolTip();
0173         rc = false;
0174     }
0175 
0176     if(!costCenterChanged(ui->costCenterCombo->currentIndex())) {
0177         infos << ui->costCenterCombo->toolTip();
0178         rc = false;
0179     }
0180 
0181     if(doUserInteraction) {
0182         /// @todo add dialog here that shows the @a infos about the problem
0183     }
0184     return rc;
0185 }
0186 
0187 bool NewTransactionEditor::Private::isDatePostOpeningDate(const QDate& date, const QString& accountId)
0188 {
0189     bool rc = true;
0190 
0191     try {
0192         MyMoneyAccount account = MyMoneyFile::instance()->account(accountId);
0193         const bool isIncomeExpense = account.isIncomeExpense();
0194 
0195         // we don't check for categories
0196         if(!isIncomeExpense) {
0197             if(date < account.openingDate())
0198                 rc = false;
0199         }
0200     } catch (MyMoneyException &e) {
0201         qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO;
0202     }
0203     return rc;
0204 }
0205 
0206 bool NewTransactionEditor::Private::postdateChanged(const QDate& date)
0207 {
0208     bool rc = true;
0209     WidgetHintFrame::hide(ui->dateEdit, i18n("The posting date of the transaction."));
0210 
0211     // collect all account ids
0212     QStringList accountIds;
0213     accountIds << m_account.id();
0214     for(int row = 0; row < splitModel.rowCount(); ++row) {
0215         QModelIndex index = splitModel.index(row, 0);
0216         accountIds << splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString();;
0217     }
0218 
0219     Q_FOREACH(QString accountId, accountIds) {
0220         if(!isDatePostOpeningDate(date, accountId)) {
0221             MyMoneyAccount account = MyMoneyFile::instance()->account(accountId);
0222             WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is prior to the opening date of account <b>%1</b>.", account.name()));
0223             rc = false;
0224             break;
0225         }
0226     }
0227     return rc;
0228 }
0229 
0230 
0231 bool NewTransactionEditor::Private::costCenterChanged(int costCenterIndex)
0232 {
0233     bool rc = true;
0234     WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to."));
0235     if(costCenterIndex != -1) {
0236         if(costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) {
0237             WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category."));
0238             rc = false;
0239         }
0240         if(rc == true && splitModel.rowCount() == 1) {
0241             QModelIndex index = costCenterModel->index(costCenterIndex, 0);
0242             QString costCenterId = costCenterModel->data(index, CostCenterModel::CostCenterIdRole).toString();
0243             index = splitModel.index(0, 0);
0244             splitModel.setData(index, costCenterId, (int)eLedgerModel::Role::CostCenterId);
0245         }
0246     }
0247 
0248     return rc;
0249 }
0250 
0251 bool NewTransactionEditor::Private::categoryChanged(const QString& accountId)
0252 {
0253     bool rc = true;
0254     if(!accountId.isEmpty() && splitModel.rowCount() <= 1) {
0255         try {
0256             MyMoneyAccount category = MyMoneyFile::instance()->account(accountId);
0257             const bool isIncomeExpense = category.isIncomeExpense();
0258             ui->costCenterCombo->setEnabled(isIncomeExpense);
0259             ui->costCenterLabel->setEnabled(isIncomeExpense);
0260             costCenterRequired = category.isCostCenterRequired();
0261             rc &= costCenterChanged(ui->costCenterCombo->currentIndex());
0262             rc &= postdateChanged(ui->dateEdit->date());
0263 
0264             // make sure we have a split in the model
0265             bool newSplit = false;
0266             if(splitModel.rowCount() == 0) {
0267                 splitModel.addEmptySplitEntry();
0268                 newSplit = true;
0269             }
0270 
0271             const QModelIndex index = splitModel.index(0, 0);
0272             splitModel.setData(index, accountId, (int)eLedgerModel::Role::AccountId);
0273             if(newSplit) {
0274                 costCenterChanged(ui->costCenterCombo->currentIndex());
0275 
0276                 if(amountHelper->haveValue()) {
0277                     splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue);
0278 
0279                     /// @todo make sure to convert initial value to shares according to price information
0280 
0281                     splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares);
0282                 }
0283             }
0284 
0285             /// @todo we need to make sure to support multiple currencies here
0286 
0287 
0288         } catch (MyMoneyException &e) {
0289             qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO;
0290         }
0291     }
0292     return rc;
0293 }
0294 
0295 bool NewTransactionEditor::Private::numberChanged(const QString& newNumber)
0296 {
0297     bool rc = true;
0298     WidgetHintFrame::hide(ui->numberEdit, i18n("The check number used for this transaction."));
0299     if(!newNumber.isEmpty()) {
0300         const LedgerModel* model = Models::instance()->ledgerModel();
0301         QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::Number,
0302                                             QVariant(newNumber),
0303                                             -1,                         // all splits
0304                                             Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
0305 
0306         foreach(QModelIndex index, list) {
0307             if(model->data(index, (int)eLedgerModel::Role::AccountId) == m_account.id()
0308                     && model->data(index, (int)eLedgerModel::Role::TransactionSplitId) != transactionSplitId) {
0309                 WidgetHintFrame::show(ui->numberEdit, i18n("The check number <b>%1</b> has already been used in this account.", newNumber));
0310                 rc = false;
0311                 break;
0312             }
0313         }
0314     }
0315     return rc;
0316 }
0317 
0318 bool NewTransactionEditor::Private::valueChanged(CreditDebitHelper* valueHelper)
0319 {
0320     bool rc = true;
0321     if(valueHelper->haveValue()  && splitModel.rowCount() <= 1) {
0322         rc = false;
0323         try {
0324             MyMoneyMoney shares;
0325             if(splitModel.rowCount() == 1) {
0326                 const QModelIndex index = splitModel.index(0, 0);
0327                 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue);
0328 
0329                 /// @todo make sure to support multiple currencies
0330 
0331                 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares);
0332             } else {
0333                 /// @todo ask what to do: if the rest of the splits is the same amount we could simply reverse the sign
0334                 /// of all splits, otherwise we could ask if the user wants to start the split editor or anything else.
0335             }
0336             rc = true;
0337 
0338         } catch (MyMoneyException &e) {
0339             qDebug() << "Ooops: something went wrong in" << Q_FUNC_INFO;
0340         }
0341     }
0342     return rc;
0343 }
0344 
0345 
0346 NewTransactionEditor::NewTransactionEditor(QWidget* parent, const QString& accountId)
0347     : QFrame(parent, Qt::FramelessWindowHint /* | Qt::X11BypassWindowManagerHint */)
0348     , d(new Private(this))
0349 {
0350     auto const model = Models::instance()->accountsModel();
0351     // extract account information from model
0352     const auto index = model->accountById(accountId);
0353     d->m_account = model->data(index, (int)eAccountsModel::Role::Account).value<MyMoneyAccount>();
0354 
0355     d->ui->setupUi(this);
0356 
0357     d->accountsModel->addAccountGroup(QVector<eMyMoney::Account::Type> {eMyMoney::Account::Type::Asset, eMyMoney::Account::Type::Liability, eMyMoney::Account::Type::Income, eMyMoney::Account::Type::Expense, eMyMoney::Account::Type::Equity});
0358     d->accountsModel->setHideEquityAccounts(false);
0359     d->accountsModel->setSourceColumns(model->getColumns());
0360     d->accountsModel->setSourceModel(model);
0361     d->accountsModel->sort((int)eAccountsModel::Column::Account);
0362     d->ui->accountCombo->setModel(d->accountsModel);
0363 
0364     d->costCenterModel->setSortRole(Qt::DisplayRole);
0365     d->costCenterModel->setSourceModel(Models::instance()->costCenterModel());
0366     d->costCenterModel->sort(0);
0367 
0368     d->ui->costCenterCombo->setEditable(true);
0369     d->ui->costCenterCombo->setModel(d->costCenterModel);
0370     d->ui->costCenterCombo->setModelColumn(0);
0371     d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains);
0372 
0373     d->payeesModel->setSortRole(Qt::DisplayRole);
0374     d->payeesModel->setSourceModel(Models::instance()->payeesModel());
0375     d->payeesModel->sort(0);
0376 
0377     d->ui->payeeEdit->setEditable(true);
0378     d->ui->payeeEdit->setModel(d->payeesModel);
0379     d->ui->payeeEdit->setModelColumn(0);
0380     d->ui->payeeEdit->completer()->setFilterMode(Qt::MatchContains);
0381 
0382     d->ui->enterButton->setIcon(Icons::get(Icon::DialogOK));
0383     d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel));
0384 
0385     d->ui->statusCombo->setModel(&d->statusModel);
0386 
0387     d->ui->dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat));
0388 
0389     d->ui->amountEditCredit->setAllowEmpty(true);
0390     d->ui->amountEditDebit->setAllowEmpty(true);
0391     d->amountHelper = new CreditDebitHelper(this, d->ui->amountEditCredit, d->ui->amountEditDebit);
0392 
0393     WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this);
0394     frameCollection->addFrame(new WidgetHintFrame(d->ui->dateEdit));
0395     frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo));
0396     frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning));
0397     frameCollection->addWidget(d->ui->enterButton);
0398 
0399     connect(d->ui->numberEdit, SIGNAL(textChanged(QString)), this, SLOT(numberChanged(QString)));
0400     connect(d->ui->costCenterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(costCenterChanged(int)));
0401     connect(d->ui->accountCombo, SIGNAL(accountSelected(QString)), this, SLOT(categoryChanged(QString)));
0402     connect(d->ui->dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(postdateChanged(QDate)));
0403     connect(d->amountHelper, SIGNAL(valueChanged()), this, SLOT(valueChanged()));
0404 
0405     connect(d->ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject()));
0406     connect(d->ui->enterButton, SIGNAL(clicked(bool)), this, SLOT(acceptEdit()));
0407     connect(d->ui->splitEditorButton, SIGNAL(clicked(bool)), this, SLOT(editSplits()));
0408 
0409     // handle some events in certain conditions different from default
0410     d->ui->payeeEdit->installEventFilter(this);
0411     d->ui->costCenterCombo->installEventFilter(this);
0412     d->ui->tagComboBox->installEventFilter(this);
0413     d->ui->statusCombo->installEventFilter(this);
0414 
0415     // setup tooltip
0416 
0417     // setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
0418 }
0419 
0420 NewTransactionEditor::~NewTransactionEditor()
0421 {
0422 }
0423 
0424 bool NewTransactionEditor::accepted() const
0425 {
0426     return d->accepted;
0427 }
0428 
0429 void NewTransactionEditor::acceptEdit()
0430 {
0431     if(d->checkForValidTransaction()) {
0432         d->accepted = true;
0433         emit done();
0434     }
0435 }
0436 
0437 void NewTransactionEditor::reject()
0438 {
0439     emit done();
0440 }
0441 
0442 void NewTransactionEditor::keyPressEvent(QKeyEvent* e)
0443 {
0444     if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
0445         switch (e->key()) {
0446         case Qt::Key_Enter:
0447         case Qt::Key_Return:
0448         {
0449             if(focusWidget() == d->ui->cancelButton) {
0450                 reject();
0451             } else {
0452                 if(d->ui->enterButton->isEnabled()) {
0453                     d->ui->enterButton->click();
0454                 }
0455                 return;
0456             }
0457         }
0458         break;
0459 
0460         case Qt::Key_Escape:
0461             reject();
0462             break;
0463 
0464         default:
0465             e->ignore();
0466             return;
0467         }
0468     } else {
0469         e->ignore();
0470     }
0471 }
0472 
0473 void NewTransactionEditor::loadTransaction(const QString& id)
0474 {
0475     const LedgerModel* model = Models::instance()->ledgerModel();
0476     const QString transactionId = model->transactionIdFromTransactionSplitId(id);
0477 
0478     if(id.isEmpty()) {
0479         d->transactionSplitId.clear();
0480         d->transaction = MyMoneyTransaction();
0481         if(lastUsedPostDate()->isValid()) {
0482             d->ui->dateEdit->setDate(*lastUsedPostDate());
0483         } else {
0484             d->ui->dateEdit->setDate(QDate::currentDate());
0485         }
0486         bool blocked = d->ui->accountCombo->lineEdit()->blockSignals(true);
0487         d->ui->accountCombo->lineEdit()->clear();
0488         d->ui->accountCombo->lineEdit()->blockSignals(blocked);
0489 
0490     } else {
0491         // find which item has this id and set is as the current item
0492         QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::TransactionId,
0493                                             QVariant(transactionId),
0494                                             -1,                         // all splits
0495                                             Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
0496 
0497         Q_FOREACH(QModelIndex index, list) {
0498             // the selected split?
0499             const QString transactionSplitId = model->data(index, (int)eLedgerModel::Role::TransactionSplitId).toString();
0500             if(transactionSplitId == id) {
0501                 d->transactionSplitId = id;
0502                 d->transaction = model->data(index, (int)eLedgerModel::Role::Transaction).value<MyMoneyTransaction>();
0503                 d->split = model->data(index, (int)eLedgerModel::Role::Split).value<MyMoneySplit>();
0504                 d->ui->dateEdit->setDate(model->data(index, (int)eLedgerModel::Role::PostDate).toDate());
0505                 d->ui->payeeEdit->lineEdit()->setText(model->data(index, (int)eLedgerModel::Role::PayeeName).toString());
0506                 d->ui->memoEdit->clear();
0507                 d->ui->memoEdit->insertPlainText(model->data(index, (int)eLedgerModel::Role::Memo).toString());
0508                 d->ui->memoEdit->moveCursor(QTextCursor::Start);
0509                 d->ui->memoEdit->ensureCursorVisible();
0510 
0511                 // The calculator for the amount field can simply be added as an icon to the line edit widget.
0512                 // See https://stackoverflow.com/questions/11381865/how-to-make-an-extra-icon-in-qlineedit-like-this howto do it
0513                 d->ui->amountEditCredit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Payment)).toString());
0514                 d->ui->amountEditDebit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Deposit)).toString());
0515 
0516                 d->ui->numberEdit->setText(model->data(index, (int)eLedgerModel::Role::Number).toString());
0517                 d->ui->statusCombo->setCurrentIndex(model->data(index, (int)eLedgerModel::Role::Number).toInt());
0518 
0519                 QModelIndexList stList = d->statusModel.match(d->statusModel.index(0, 0), Qt::UserRole+1, model->data(index, (int)eLedgerModel::Role::Reconciliation).toInt());
0520                 if(stList.count()) {
0521                     QModelIndex stIndex = stList.front();
0522                     d->ui->statusCombo->setCurrentIndex(stIndex.row());
0523                 }
0524             } else {
0525                 d->splitModel.addSplit(transactionSplitId);
0526             }
0527         }
0528         d->updateWidgetState();
0529     }
0530 
0531     // set focus to date edit once we return to event loop
0532     QMetaObject::invokeMethod(d->ui->dateEdit, "setFocus", Qt::QueuedConnection);
0533 }
0534 
0535 void NewTransactionEditor::numberChanged(const QString& newNumber)
0536 {
0537     d->numberChanged(newNumber);
0538 }
0539 
0540 void NewTransactionEditor::categoryChanged(const QString& accountId)
0541 {
0542     d->categoryChanged(accountId);
0543 }
0544 
0545 void NewTransactionEditor::costCenterChanged(int costCenterIndex)
0546 {
0547     d->costCenterChanged(costCenterIndex);
0548 }
0549 
0550 void NewTransactionEditor::postdateChanged(const QDate& date)
0551 {
0552     d->postdateChanged(date);
0553 }
0554 
0555 void NewTransactionEditor::valueChanged()
0556 {
0557     d->valueChanged(d->amountHelper);
0558 }
0559 
0560 void NewTransactionEditor::editSplits()
0561 {
0562     SplitModel splitModel;
0563 
0564     splitModel.deepCopy(d->splitModel, true);
0565 
0566     // create an empty split at the end
0567     splitModel.addEmptySplitEntry();
0568 
0569     QPointer<SplitDialog> splitDialog = new SplitDialog(d->m_account, transactionAmount(), this);
0570     splitDialog->setModel(&splitModel);
0571 
0572     int rc = splitDialog->exec();
0573 
0574     if(splitDialog && (rc == QDialog::Accepted)) {
0575         // remove that empty split again before we update the splits
0576         splitModel.removeEmptySplitEntry();
0577 
0578         // copy the splits model contents
0579         d->splitModel.deepCopy(splitModel, true);
0580 
0581         // update the transaction amount
0582         d->amountHelper->setValue(splitDialog->transactionAmount());
0583 
0584         d->updateWidgetState();
0585         QWidget *next = d->ui->tagComboBox;
0586         if(d->ui->costCenterCombo->isEnabled()) {
0587             next = d->ui->costCenterCombo;
0588         }
0589         next->setFocus();
0590     }
0591 
0592     if(splitDialog) {
0593         splitDialog->deleteLater();
0594     }
0595 }
0596 
0597 MyMoneyMoney NewTransactionEditor::transactionAmount() const
0598 {
0599     return d->amountHelper->value();
0600 }
0601 
0602 void NewTransactionEditor::saveTransaction()
0603 {
0604     MyMoneyTransaction t;
0605 
0606     if(!d->transactionSplitId.isEmpty()) {
0607         t = d->transaction;
0608     } else {
0609         // we keep the date when adding a new transaction
0610         // for the next new one
0611         *lastUsedPostDate() = d->ui->dateEdit->date();
0612     }
0613 
0614     // first remove the splits that are gone
0615     foreach (const auto split, t.splits()) {
0616         if(split.id() == d->split.id()) {
0617             continue;
0618         }
0619         int row;
0620         for(row = 0; row < d->splitModel.rowCount(); ++row) {
0621             QModelIndex index = d->splitModel.index(row, 0);
0622             if(d->splitModel.data(index, (int)eLedgerModel::Role::SplitId).toString() == split.id()) {
0623                 break;
0624             }
0625         }
0626 
0627         // if the split is not in the model, we get rid of it
0628         if(d->splitModel.rowCount() == row) {
0629             t.removeSplit(split);
0630         }
0631     }
0632 
0633     MyMoneyFileTransaction ft;
0634     try {
0635         // new we update the split we are opened for
0636         MyMoneySplit sp(d->split);
0637         sp.setNumber(d->ui->numberEdit->text());
0638         sp.setMemo(d->ui->memoEdit->toPlainText());
0639         sp.setShares(d->amountHelper->value());
0640         if(t.commodity().isEmpty()) {
0641             t.setCommodity(d->m_account.currencyId());
0642             sp.setValue(d->amountHelper->value());
0643         } else {
0644             /// @todo check that the transactions commodity is the same
0645             /// as the one of the account this split references. If
0646             /// that is not the case, the next statement would create
0647             /// a problem
0648             sp.setValue(d->amountHelper->value());
0649         }
0650 
0651         if(sp.reconcileFlag() != eMyMoney::Split::State::Reconciled
0652                 && !sp.reconcileDate().isValid()
0653                 && d->ui->statusCombo->currentIndex() == (int)eMyMoney::Split::State::Reconciled) {
0654             sp.setReconcileDate(QDate::currentDate());
0655         }
0656         sp.setReconcileFlag(static_cast<eMyMoney::Split::State>(d->ui->statusCombo->currentIndex()));
0657         // sp.setPayeeId(d->ui->payeeEdit->cu)
0658         if(sp.id().isEmpty()) {
0659             t.addSplit(sp);
0660         } else {
0661             t.modifySplit(sp);
0662         }
0663         t.setPostDate(d->ui->dateEdit->date());
0664 
0665         // now update and add what we have in the model
0666         const SplitModel * model = &d->splitModel;
0667         for(int row = 0; row < model->rowCount(); ++row) {
0668             QModelIndex index = model->index(row, 0);
0669             MyMoneySplit s;
0670             const QString splitId = model->data(index, (int)eLedgerModel::Role::SplitId).toString();
0671             if(!SplitModel::isNewSplitId(splitId)) {
0672                 s = t.splitById(splitId);
0673             }
0674             s.setNumber(model->data(index, (int)eLedgerModel::Role::Number).toString());
0675             s.setMemo(model->data(index, (int)eLedgerModel::Role::Memo).toString());
0676             s.setAccountId(model->data(index, (int)eLedgerModel::Role::AccountId).toString());
0677             s.setShares(model->data(index, (int)eLedgerModel::Role::SplitShares).value<MyMoneyMoney>());
0678             s.setValue(model->data(index, (int)eLedgerModel::Role::SplitValue).value<MyMoneyMoney>());
0679             s.setCostCenterId(model->data(index, (int)eLedgerModel::Role::CostCenterId).toString());
0680             s.setPayeeId(model->data(index, (int)eLedgerModel::Role::PayeeId).toString());
0681 
0682             // reconcile flag and date
0683             if(s.id().isEmpty()) {
0684                 t.addSplit(s);
0685             } else {
0686                 t.modifySplit(s);
0687             }
0688         }
0689 
0690         if(t.id().isEmpty()) {
0691             MyMoneyFile::instance()->addTransaction(t);
0692         } else {
0693             MyMoneyFile::instance()->modifyTransaction(t);
0694         }
0695         ft.commit();
0696 
0697     } catch (const MyMoneyException &e) {
0698         qDebug() << Q_FUNC_INFO << "something went wrong" << e.what();
0699     }
0700 
0701 }
0702 
0703 bool NewTransactionEditor::eventFilter(QObject* o, QEvent* e)
0704 {
0705     auto cb = qobject_cast<QComboBox*>(o);
0706     if (o) {
0707         // filter out wheel events for combo boxes if the popup view is not visible
0708         if ((e->type() == QEvent::Wheel) && !cb->view()->isVisible()) {
0709             return true;
0710         }
0711     }
0712     return QFrame::eventFilter(o, e);
0713 }
0714 
0715 // kate: space-indent on; indent-width 2; remove-trailing-space on; remove-trailing-space-save on;