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;