File indexing completed on 2024-05-12 05:07:47
0001 /* 0002 SPDX-FileCopyrightText: 2023 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "multitransactioneditor.h" 0007 0008 // ---------------------------------------------------------------------------- 0009 // QT Includes 0010 0011 #include <QAbstractItemView> 0012 #include <QCompleter> 0013 #include <QDebug> 0014 #include <QGlobalStatic> 0015 #include <QHeaderView> 0016 #include <QSortFilterProxyModel> 0017 #include <QStandardItemModel> 0018 #include <QStringList> 0019 #include <QTableView> 0020 #include <QTimer> 0021 0022 // ---------------------------------------------------------------------------- 0023 // KDE Includes 0024 0025 #include <KLocalizedString> 0026 0027 // ---------------------------------------------------------------------------- 0028 // Project Includes 0029 0030 #include "accountcreator.h" 0031 #include "costcentermodel.h" 0032 #include "icons.h" 0033 #include "idfilter.h" 0034 #include "journalmodel.h" 0035 #include "kcurrencycalculator.h" 0036 #include "kmymoneysettings.h" 0037 #include "knewaccountdlg.h" 0038 #include "mymoneyaccount.h" 0039 #include "mymoneyexception.h" 0040 #include "mymoneyfile.h" 0041 #include "mymoneypayee.h" 0042 #include "mymoneyschedule.h" 0043 #include "mymoneysecurity.h" 0044 #include "mymoneysplit.h" 0045 #include "mymoneytransaction.h" 0046 #include "mymoneyutils.h" 0047 #include "payeesmodel.h" 0048 #include "securitiesmodel.h" 0049 #include "splitdialog.h" 0050 #include "statusmodel.h" 0051 #include "tagsmodel.h" 0052 #include "widgethintframe.h" 0053 0054 #include "ui_newtransactioneditor.h" 0055 0056 using namespace Icons; 0057 0058 class MultiTransactionEditor::Private 0059 { 0060 Q_DISABLE_COPY_MOVE(Private) 0061 0062 public: 0063 enum TaxValueChange { 0064 ValueUnchanged, 0065 ValueChanged, 0066 }; 0067 Private(MultiTransactionEditor* parent) 0068 : q(parent) 0069 , ui(new Ui_NewTransactionEditor) 0070 , tabOrderUi(nullptr) 0071 , accountsModel(new AccountNamesFilterProxyModel(parent)) 0072 , categoriesModel(new AccountNamesFilterProxyModel(parent)) 0073 , costCenterModel(new QSortFilterProxyModel(parent)) 0074 , payeesModel(new QSortFilterProxyModel(parent)) 0075 , frameCollection(nullptr) 0076 , costCenterRequired(false) 0077 { 0078 accountsModel->setObjectName(QLatin1String("MultiTransactionEditor::accountsModel")); 0079 categoriesModel->setObjectName(QLatin1String("MultiTransactionEditor::categoriesModel")); 0080 costCenterModel->setObjectName(QLatin1String("SortedCostCenterModel")); 0081 payeesModel->setObjectName(QLatin1String("SortedPayeesModel")); 0082 0083 costCenterModel->setSortLocaleAware(true); 0084 costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0085 0086 payeesModel->setSortLocaleAware(true); 0087 payeesModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0088 } 0089 0090 ~Private() 0091 { 0092 delete ui; 0093 } 0094 0095 void setupTabOrder(); 0096 bool anyChanges() const; 0097 bool isDatePostOpeningDate(const QDate& date, const QString& accountId); 0098 bool checkForValidTransaction(bool doUserInteraction); 0099 bool postdateChanged(const QDate& date); 0100 bool costCenterChanged(int costCenterIndex); 0101 void payeeChanged(int payeeIndex); 0102 void accountChanged(const QString& id); 0103 bool categoryChanged(const QString& id); 0104 void numberChanged(const QString& newNumber); 0105 void amountChanged(); 0106 bool isIncomeExpense(const QModelIndex& idx) const; 0107 bool isIncomeExpense(const QString& categoryId) const; 0108 void tagsChanged(const QStringList& tagIds); 0109 0110 MultiTransactionEditor* q; 0111 Ui_NewTransactionEditor* ui; 0112 Ui_NewTransactionEditor* tabOrderUi; 0113 AccountNamesFilterProxyModel* accountsModel; 0114 AccountNamesFilterProxyModel* categoriesModel; 0115 QSortFilterProxyModel* costCenterModel; 0116 QSortFilterProxyModel* payeesModel; 0117 QUndoStack undoStack; 0118 WidgetHintFrameCollection* frameCollection; 0119 QString errorMessage; 0120 StatusModel statusModel; 0121 QStringList selectedJournalEntryIds; 0122 bool costCenterRequired; 0123 }; 0124 0125 bool MultiTransactionEditor::Private::anyChanges() const 0126 { 0127 bool rc = false; 0128 rc |= !ui->dateEdit->isNull(); 0129 rc |= ui->creditDebitEdit->haveValue(); 0130 rc |= !ui->payeeEdit->lineEdit()->text().isEmpty(); 0131 rc |= !ui->categoryCombo->getSelected().isEmpty(); 0132 rc |= !ui->costCenterCombo->lineEdit()->text().isEmpty(); 0133 rc |= !ui->tagContainer->selectedTags().isEmpty(); 0134 rc |= !ui->statusCombo->currentText().isEmpty(); 0135 rc |= !ui->memoEdit->toPlainText().isEmpty(); 0136 return rc; 0137 } 0138 0139 bool MultiTransactionEditor::Private::checkForValidTransaction(bool doUserInteraction) 0140 { 0141 QStringList infos; 0142 bool rc = true; 0143 if (!postdateChanged(ui->dateEdit->date())) { 0144 infos << ui->dateEdit->toolTip(); 0145 rc = false; 0146 } 0147 0148 if (!costCenterChanged(ui->costCenterCombo->currentIndex())) { 0149 infos << ui->costCenterCombo->toolTip(); 0150 rc = false; 0151 } 0152 0153 if (q->needCreateCategory(ui->categoryCombo) || q->needCreatePayee(ui->payeeEdit)) { 0154 rc = false; 0155 } 0156 0157 if (doUserInteraction) { 0158 /// @todo add dialog here that shows the @a infos about the problem 0159 } 0160 return rc; 0161 } 0162 0163 bool MultiTransactionEditor::Private::isDatePostOpeningDate(const QDate& date, const QString& accountId) 0164 { 0165 bool rc = true; 0166 0167 try { 0168 MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); 0169 const bool isIncomeExpense = account.isIncomeExpense(); 0170 0171 // we don't check for categories 0172 if (!isIncomeExpense) { 0173 if (date < account.openingDate()) 0174 rc = false; 0175 } 0176 } catch (MyMoneyException&) { 0177 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; 0178 } 0179 return rc; 0180 } 0181 0182 bool MultiTransactionEditor::Private::postdateChanged(const QDate& date) 0183 { 0184 WidgetHintFrame::hide(ui->dateEdit, i18n("The posting date of the transaction.")); 0185 0186 // if the date field is empty, we have a valid date 0187 if (ui->dateEdit->isNull()) { 0188 return true; 0189 } 0190 0191 if (!date.isValid()) { 0192 WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is invalid.")); 0193 return false; 0194 } 0195 0196 // collect all account ids of all selected transactions 0197 QStringList accountIds; 0198 if (!ui->categoryCombo->getSelected().isEmpty()) { 0199 accountIds << ui->categoryCombo->getSelected(); 0200 } 0201 const auto journalModel = MyMoneyFile::instance()->journalModel(); 0202 for (const auto& journalEntryId : selectedJournalEntryIds) { 0203 const auto journalEntry = journalModel->itemById(journalEntryId); 0204 const auto splits = journalEntry.transaction().splits(); 0205 for (const auto& sp : splits) { 0206 accountIds << sp.accountId(); 0207 } 0208 } 0209 0210 bool rc = true; 0211 for (const auto& accountId : accountIds) { 0212 if (!isDatePostOpeningDate(date, accountId)) { 0213 MyMoneyAccount account = MyMoneyFile::instance()->account(accountId); 0214 WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is prior to the opening date of account <b>%1</b>.", account.name())); 0215 rc = false; 0216 break; 0217 } 0218 } 0219 return rc; 0220 } 0221 0222 bool MultiTransactionEditor::Private::costCenterChanged(int costCenterIndex) 0223 { 0224 bool rc = true; 0225 WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to.")); 0226 if (costCenterIndex != -1) { 0227 if (costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) { 0228 WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category.")); 0229 rc = false; 0230 } 0231 } 0232 0233 return rc; 0234 } 0235 0236 bool MultiTransactionEditor::Private::isIncomeExpense(const QString& categoryId) const 0237 { 0238 if (!categoryId.isEmpty()) { 0239 MyMoneyAccount category = MyMoneyFile::instance()->account(categoryId); 0240 return category.isIncomeExpense(); 0241 } 0242 return false; 0243 } 0244 0245 bool MultiTransactionEditor::Private::categoryChanged(const QString& accountId) 0246 { 0247 WidgetHintFrame::hide(ui->categoryCombo, i18n("Select category or account.")); 0248 bool rc = true; 0249 if (!accountId.isEmpty()) { 0250 try { 0251 MyMoneyAccount category = MyMoneyFile::instance()->account(accountId); 0252 ui->costCenterCombo->setEnabled(false); 0253 ui->costCenterLabel->setEnabled(false); 0254 if (category.isAssetLiability()) { 0255 const auto journalModel = MyMoneyFile::instance()->journalModel(); 0256 for (const auto& journalEntryId : selectedJournalEntryIds) { 0257 const auto journalEntry = journalModel->itemById(journalEntryId); 0258 const auto postDate = journalEntry.transaction().postDate(); 0259 if (postDate < category.openingDate()) { 0260 QString msg = i18nc("@info Error when selecting account", 0261 "A selected transaction has a post date (%1) before the opening date of the selected account (%2).", 0262 MyMoneyUtils::formatDate(postDate), 0263 MyMoneyUtils::formatDate(category.openingDate())); 0264 WidgetHintFrame::show(ui->categoryCombo, msg); 0265 rc = false; 0266 } 0267 } 0268 } else if (category.isIncomeExpense()) { 0269 ui->costCenterCombo->setEnabled(true); 0270 ui->costCenterLabel->setEnabled(true); 0271 costCenterRequired = category.isCostCenterRequired(); 0272 costCenterChanged(ui->costCenterCombo->currentIndex()); 0273 } 0274 } catch (MyMoneyException&) { 0275 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; 0276 } 0277 } 0278 return rc; 0279 } 0280 0281 void MultiTransactionEditor::Private::numberChanged(const QString& newNumber) 0282 { 0283 Q_UNUSED(newNumber) 0284 } 0285 0286 void MultiTransactionEditor::Private::amountChanged() 0287 { 0288 } 0289 0290 void MultiTransactionEditor::Private::payeeChanged(int payeeIndex) 0291 { 0292 Q_UNUSED(payeeIndex) 0293 } 0294 0295 void MultiTransactionEditor::Private::tagsChanged(const QStringList& tagIds) 0296 { 0297 Q_UNUSED(tagIds) 0298 } 0299 0300 void MultiTransactionEditor::Private::setupTabOrder() 0301 { 0302 const auto defaultTabOrder = QStringList{ 0303 QLatin1String("accountCombo"), 0304 QLatin1String("dateEdit"), 0305 QLatin1String("creditDebitEdit"), 0306 QLatin1String("payeeEdit"), 0307 QLatin1String("numberEdit"), 0308 QLatin1String("categoryCombo"), 0309 QLatin1String("costCenterCombo"), 0310 QLatin1String("tagContainer"), 0311 QLatin1String("statusCombo"), 0312 QLatin1String("memoEdit"), 0313 QLatin1String("enterButton"), 0314 QLatin1String("cancelButton"), 0315 }; 0316 q->setProperty("kmm_defaulttaborder", defaultTabOrder); 0317 q->setProperty("kmm_currenttaborder", q->tabOrder(QLatin1String("multiTransactionEditor"), defaultTabOrder)); 0318 0319 q->setupTabOrder(q->property("kmm_currenttaborder").toStringList()); 0320 } 0321 0322 MultiTransactionEditor::MultiTransactionEditor(QWidget* parent, const QString& accountId) 0323 : TransactionEditorBase(parent, accountId) 0324 , d(new Private(this)) 0325 { 0326 auto const file = MyMoneyFile::instance(); 0327 auto const model = file->accountsModel(); 0328 0329 d->ui->setupUi(this); 0330 0331 // default is to hide the account selection combobox 0332 // and the number widget 0333 setShowAccountCombo(false); 0334 setShowNumberWidget(false); 0335 0336 d->setupTabOrder(); 0337 0338 // determine order of credit and debit edit widgets 0339 // based on their visual order in the ledger 0340 int creditColumn = JournalModel::Column::Payment; 0341 int debitColumn = JournalModel::Column::Deposit; 0342 0343 QWidget* w(this); 0344 do { 0345 w = w->parentWidget(); 0346 const auto view = qobject_cast<const QTableView*>(w); 0347 if (view) { 0348 creditColumn = view->horizontalHeader()->visualIndex(creditColumn); 0349 debitColumn = view->horizontalHeader()->visualIndex(debitColumn); 0350 break; 0351 } 0352 } while (w); 0353 0354 // in case they are in the opposite order, we swap the edit widgets 0355 if (debitColumn < creditColumn) { 0356 d->ui->creditDebitEdit->swapCreditDebit(); 0357 } 0358 0359 d->accountsModel->addAccountGroup(QVector<eMyMoney::Account::Type>{ 0360 eMyMoney::Account::Type::Asset, 0361 eMyMoney::Account::Type::Liability, 0362 eMyMoney::Account::Type::Equity, 0363 }); 0364 d->accountsModel->setHideEquityAccounts(false); 0365 d->accountsModel->setHideZeroBalancedEquityAccounts(false); 0366 d->accountsModel->setHideZeroBalancedAccounts(false); 0367 d->accountsModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0368 d->accountsModel->setSourceModel(model); 0369 d->accountsModel->sort(AccountsModel::Column::AccountName); 0370 d->ui->accountCombo->setModel(d->accountsModel); 0371 0372 d->categoriesModel->addAccountGroup(QVector<eMyMoney::Account::Type>{ 0373 eMyMoney::Account::Type::Asset, 0374 eMyMoney::Account::Type::Liability, 0375 eMyMoney::Account::Type::Income, 0376 eMyMoney::Account::Type::Expense, 0377 eMyMoney::Account::Type::Equity, 0378 }); 0379 d->categoriesModel->setHideEquityAccounts(false); 0380 d->categoriesModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0381 d->categoriesModel->setSourceModel(model); 0382 d->categoriesModel->sort(AccountsModel::Column::AccountName); 0383 d->ui->categoryCombo->setModel(d->categoriesModel); 0384 0385 d->ui->tagContainer->setModel(file->tagsModel()->modelWithEmptyItem()); 0386 0387 d->costCenterModel->setSortRole(Qt::DisplayRole); 0388 d->costCenterModel->setSourceModel(file->costCenterModel()->modelWithEmptyItem()); 0389 d->costCenterModel->setSortLocaleAware(true); 0390 d->costCenterModel->sort(0); 0391 0392 d->ui->costCenterCombo->setEditable(true); 0393 d->ui->costCenterCombo->setModel(d->costCenterModel); 0394 d->ui->costCenterCombo->setModelColumn(0); 0395 d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains); 0396 0397 d->payeesModel->setSortRole(Qt::DisplayRole); 0398 d->payeesModel->setSourceModel(file->payeesModel()->modelWithEmptyItem()); 0399 d->payeesModel->setSortLocaleAware(true); 0400 d->payeesModel->sort(0); 0401 0402 d->ui->payeeEdit->setEditable(true); 0403 d->ui->payeeEdit->lineEdit()->setClearButtonEnabled(true); 0404 d->ui->payeeEdit->setModel(d->payeesModel); 0405 d->ui->payeeEdit->setModelColumn(0); 0406 d->ui->payeeEdit->completer()->setCompletionMode(QCompleter::PopupCompletion); 0407 d->ui->payeeEdit->completer()->setFilterMode(Qt::MatchContains); 0408 0409 // make sure that there is no selection left in the background 0410 // in case there is no text in the edit field 0411 connect(d->ui->payeeEdit->lineEdit(), &QLineEdit::textEdited, [&](const QString& txt) { 0412 if (txt.isEmpty()) { 0413 d->ui->payeeEdit->setCurrentIndex(-1); 0414 } 0415 }); 0416 0417 connect(d->ui->categoryCombo->lineEdit(), &QLineEdit::textEdited, [&](const QString& txt) { 0418 if (txt.isEmpty()) { 0419 d->ui->categoryCombo->setSelected(QString()); 0420 } 0421 }); 0422 d->ui->enterButton->setIcon(Icons::get(Icon::DialogOK)); 0423 d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel)); 0424 0425 // construct a special status model that supports the unchanged (unknown) entry 0426 QMap<QString, StatusEntry> states = { 0427 {QStringLiteral("ST00"), StatusEntry(QString(), eMyMoney::Split::State::Unknown, QString(), QString())}, 0428 }; 0429 const auto rows = d->statusModel.rowCount(); 0430 for (int row = 0; row < rows; ++row) { 0431 const auto idx = d->statusModel.index(row, 0); 0432 const auto statusEntry = d->statusModel.itemByIndex(idx); 0433 states.insert(statusEntry.id(), statusEntry); 0434 } 0435 d->statusModel.load(states); 0436 0437 d->ui->statusCombo->setModel(&d->statusModel); 0438 0439 d->ui->creditDebitEdit->setAllowEmpty(true); 0440 0441 d->frameCollection = new WidgetHintFrameCollection(this); 0442 d->frameCollection->addFrame(new WidgetHintFrame(d->ui->dateEdit)); 0443 d->frameCollection->addFrame(new WidgetHintFrame(d->ui->categoryCombo)); 0444 d->frameCollection->addFrame(new WidgetHintFrame(d->ui->tagContainer)); 0445 d->frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo)); 0446 d->frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning)); 0447 d->frameCollection->addWidget(d->ui->enterButton); 0448 0449 connect(d->ui->numberEdit, &QLineEdit::textChanged, this, [&](const QString& newNumber) { 0450 d->numberChanged(newNumber); 0451 }); 0452 0453 connect(d->ui->costCenterCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [&](int costCenterIndex) { 0454 d->costCenterChanged(costCenterIndex); 0455 }); 0456 0457 connect(d->ui->categoryCombo, &KMyMoneyAccountCombo::accountSelected, this, [&](const QString& id) { 0458 d->categoryChanged(id); 0459 }); 0460 0461 connect(d->ui->dateEdit, &KMyMoneyDateEdit::dateValidityChanged, this, [&](const QDate& date) { 0462 d->postdateChanged(date); 0463 }); 0464 0465 connect(d->ui->dateEdit, &KMyMoneyDateEdit::dateEntered, this, [&](const QDate& date) { 0466 d->postdateChanged(date); 0467 Q_EMIT postDateChanged(date); 0468 }); 0469 0470 connect(d->ui->creditDebitEdit, &CreditDebitEdit::amountChanged, this, [&]() { 0471 d->amountChanged(); 0472 }); 0473 0474 connect(d->ui->payeeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [&](int payeeIndex) { 0475 d->payeeChanged(payeeIndex); 0476 }); 0477 0478 connect(d->ui->tagContainer, &KTagContainer::tagsChanged, this, [&](const QStringList& tagIds) { 0479 d->tagsChanged(tagIds); 0480 }); 0481 0482 connect(d->ui->cancelButton, &QToolButton::clicked, this, &MultiTransactionEditor::reject); 0483 connect(d->ui->enterButton, &QToolButton::clicked, this, &MultiTransactionEditor::acceptEdit); 0484 0485 // handle some events in certain conditions different from default 0486 d->ui->payeeEdit->installEventFilter(this); 0487 d->ui->costCenterCombo->installEventFilter(this); 0488 d->ui->tagContainer->tagCombo()->installEventFilter(this); 0489 d->ui->categoryCombo->installEventFilter(this); 0490 d->ui->statusCombo->installEventFilter(this); 0491 0492 setCancelButton(d->ui->cancelButton); 0493 setEnterButton(d->ui->enterButton); 0494 0495 // force setup of filters 0496 slotSettingsChanged(); 0497 0498 // setup widget content 0499 d->ui->dateEdit->setAllowEmptyDate(true); 0500 d->ui->dateEdit->clearEditText(); 0501 0502 d->ui->creditDebitEdit->setAllowEmpty(true); 0503 0504 d->ui->categoryCombo->setSplitActionVisible(false); 0505 d->ui->categoryCombo->clearSelection(); 0506 } 0507 0508 MultiTransactionEditor::~MultiTransactionEditor() 0509 { 0510 } 0511 0512 void MultiTransactionEditor::setAmountPlaceHolderText(const QAbstractItemModel* model) 0513 { 0514 d->ui->creditDebitEdit->setPlaceholderText(model->headerData(JournalModel::Column::Payment, Qt::Horizontal).toString(), 0515 model->headerData(JournalModel::Column::Deposit, Qt::Horizontal).toString()); 0516 } 0517 0518 void MultiTransactionEditor::loadTransaction(const QModelIndex& index) 0519 { 0520 Q_UNUSED(index) 0521 } 0522 0523 QStringList MultiTransactionEditor::saveTransaction(const QStringList& selectedJournalEntryIds) 0524 { 0525 auto selection(selectedJournalEntryIds); 0526 connect(MyMoneyFile::instance()->journalModel(), &JournalModel::idChanged, this, [&](const QString& currentId, const QString& previousId) { 0527 selection.replaceInStrings(previousId, currentId); 0528 }); 0529 0530 const auto file = MyMoneyFile::instance(); 0531 0532 auto splitsUseSameCurrency = [&](const MyMoneySplit& split1, const MyMoneySplit& split2) { 0533 const auto accountsModel = file->accountsModel(); 0534 const auto accIdx1 = accountsModel->indexById(split1.accountId()); 0535 const auto accIdx2 = accountsModel->indexById(split2.accountId()); 0536 const auto secId1 = accIdx1.data(eMyMoney::Model::AccountCurrencyIdRole).toString(); 0537 const auto secId2 = accIdx2.data(eMyMoney::Model::AccountCurrencyIdRole).toString(); 0538 return secId1 == secId2; 0539 }; 0540 0541 if (d->anyChanges()) { 0542 const auto journalModel = MyMoneyFile::instance()->journalModel(); 0543 MyMoneyFileTransaction ft; 0544 try { 0545 for (const auto& journalEntryId : selectedJournalEntryIds) { 0546 const auto journalIdx = journalModel->indexById(journalEntryId); 0547 const auto journalEntry = journalModel->itemByIndex(journalIdx); 0548 auto t = journalEntry.transaction(); 0549 auto sp = journalEntry.split(); 0550 MyMoneySplit csp; 0551 0552 if (t.splitCount() == 2) { 0553 for (auto split : t.splits()) { 0554 if (split.id() != sp.id()) { 0555 csp = split; 0556 break; 0557 } 0558 } 0559 } 0560 0561 if (!d->ui->dateEdit->isNull()) { 0562 t.setPostDate(d->ui->dateEdit->date()); 0563 } 0564 if (!d->ui->statusCombo->currentText().isEmpty()) { 0565 const auto idx = d->statusModel.index(d->ui->statusCombo->currentIndex(), 0); 0566 sp.setReconcileFlag(idx.data(eMyMoney::Model::SplitReconcileStateRole).value<eMyMoney::Split::State>()); 0567 t.modifySplit(sp); 0568 } 0569 if (!d->ui->payeeEdit->lineEdit()->text().isEmpty()) { 0570 const auto payeeRow = d->ui->payeeEdit->currentIndex(); 0571 const auto payeeIdx = d->payeesModel->index(payeeRow, 0); 0572 const auto payeeId = payeeIdx.data(eMyMoney::Model::IdRole).toString(); 0573 sp.setPayeeId(payeeId); 0574 t.modifySplit(sp); 0575 if (!csp.id().isEmpty()) { 0576 csp.setPayeeId(payeeId); 0577 t.modifySplit(csp); 0578 } 0579 } 0580 if (!d->ui->categoryCombo->getSelected().isEmpty()) { 0581 if (!csp.id().isEmpty()) { 0582 csp.setAccountId(d->ui->categoryCombo->getSelected()); 0583 t.modifySplit(csp); 0584 } else { 0585 csp = sp; 0586 csp.clearId(); 0587 csp.setValue(-csp.value()); 0588 /// @todo make assignment multi currency safe. For now, we 0589 /// just dump out a debug message in case both securities are 0590 /// not the same 0591 csp.setShares(-csp.shares()); 0592 csp.setAccountId(d->ui->categoryCombo->getSelected()); 0593 if (!splitsUseSameCurrency(sp, csp)) { 0594 qDebug() << "MultiTransactionEditor does not support multiple currencies yet"; 0595 } 0596 csp.setReconcileDate(QDate()); 0597 csp.setReconcileFlag(eMyMoney::Split::State::Unknown); 0598 csp.setBankID(QString()); 0599 csp.setAction(QString()); 0600 csp.setNumber(QString()); 0601 t.addSplit(csp); 0602 } 0603 } 0604 0605 file->modifyTransaction(t); 0606 } 0607 ft.commit(); 0608 } catch (const MyMoneyException& e) { 0609 qDebug() << Q_FUNC_INFO << "something went wrong" << e.what(); 0610 selection = selectedJournalEntryIds; 0611 } 0612 } 0613 return selection; 0614 } 0615 0616 bool MultiTransactionEditor::eventFilter(QObject* o, QEvent* e) 0617 { 0618 auto cb = qobject_cast<QComboBox*>(o); 0619 if (cb) { 0620 // filter out wheel events for combo boxes if the popup view is not visible 0621 if ((e->type() == QEvent::Wheel) && !cb->view()->isVisible()) { 0622 return true; 0623 } 0624 0625 if (e->type() == QEvent::FocusOut) { 0626 if (cb == d->ui->categoryCombo) { 0627 if (needCreateCategory(d->ui->categoryCombo)) { 0628 createCategory(d->ui->categoryCombo, defaultCategoryType(d->ui->creditDebitEdit)); 0629 } 0630 0631 } else if (cb == d->ui->payeeEdit) { 0632 if (needCreatePayee(cb)) { 0633 createPayee(cb); 0634 } 0635 } 0636 } 0637 } 0638 return QWidget::eventFilter(o, e); 0639 } 0640 0641 QDate MultiTransactionEditor::postDate() const 0642 { 0643 return d->ui->dateEdit->date(); 0644 } 0645 0646 void MultiTransactionEditor::setShowAccountCombo(bool show) const 0647 { 0648 d->ui->accountLabel->setVisible(show); 0649 d->ui->accountCombo->setVisible(show); 0650 d->ui->topMarginWidget->setVisible(show); 0651 d->ui->accountCombo->setSplitActionVisible(false); 0652 } 0653 0654 void MultiTransactionEditor::setShowButtons(bool show) const 0655 { 0656 d->ui->enterButton->setVisible(show); 0657 d->ui->cancelButton->setVisible(show); 0658 } 0659 0660 void MultiTransactionEditor::setShowNumberWidget(bool show) const 0661 { 0662 d->ui->numberLabel->setVisible(show); 0663 d->ui->numberEdit->setVisible(show); 0664 } 0665 0666 void MultiTransactionEditor::setAccountId(const QString& accountId) 0667 { 0668 d->ui->accountCombo->setSelected(accountId); 0669 } 0670 0671 void MultiTransactionEditor::setReadOnly(bool readOnly) 0672 { 0673 if (isReadOnly() != readOnly) { 0674 TransactionEditorBase::setReadOnly(readOnly); 0675 if (readOnly) { 0676 d->frameCollection->removeWidget(d->ui->enterButton); 0677 d->ui->enterButton->setDisabled(true); 0678 } else { 0679 // no need to enable the enter button here as the 0680 // frameCollection will take care of it anyway 0681 d->frameCollection->addWidget(d->ui->enterButton); 0682 } 0683 } 0684 } 0685 0686 void MultiTransactionEditor::setupUi(QWidget* parent) 0687 { 0688 if (d->tabOrderUi == nullptr) { 0689 d->tabOrderUi = new Ui::NewTransactionEditor; 0690 } 0691 d->tabOrderUi->setupUi(parent); 0692 d->tabOrderUi->accountLabel->setVisible(false); 0693 d->tabOrderUi->accountCombo->setVisible(false); 0694 } 0695 0696 void MultiTransactionEditor::storeTabOrder(const QStringList& tabOrder) 0697 { 0698 TransactionEditorBase::storeTabOrder(QLatin1String("multiTransactionEditor"), tabOrder); 0699 } 0700 0701 void MultiTransactionEditor::slotSettingsChanged() 0702 { 0703 d->categoriesModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0704 d->accountsModel->setShowAllEntries(KMyMoneySettings::showAllAccounts()); 0705 } 0706 0707 bool MultiTransactionEditor::setSelectedJournalEntryIds(const QStringList& selectedJournalEntryIds) 0708 { 0709 d->selectedJournalEntryIds = selectedJournalEntryIds; 0710 const auto journalModel = MyMoneyFile::instance()->journalModel(); 0711 bool payeeEnabled(true); 0712 bool categoryEnabled(true); 0713 bool amountEnabled(true); 0714 bool costCenterEnabled(true); 0715 bool tagEnabled(true); 0716 bool memoEnabled(true); 0717 bool statusEnabled(true); 0718 0719 for (const auto& journalEntryId : selectedJournalEntryIds) { 0720 const auto idx = journalModel->indexById(journalEntryId); 0721 if (idx.data(eMyMoney::Model::BaseModelRole) == eMyMoney::Model::SchedulesJournalEntryRole) { 0722 d->errorMessage = i18nc("@info Error selecting multiple transactions for edit", "Cannot edit multiple schedules at once."); 0723 return false; 0724 } 0725 if (idx.data(eMyMoney::Model::JournalEntryIsFrozenRole).toBool() == true) { 0726 statusEnabled = false; 0727 } 0728 if (idx.data(eMyMoney::Model::TransactionSplitCountRole).toInt() > 2) { 0729 WidgetHintFrame::hide(d->ui->categoryCombo, 0730 i18nc("@info:tooltip selecting multiple transactions for edit", 0731 "Selection of category or account is not possible when split transactions are part of the selection.")); 0732 categoryEnabled = false; 0733 WidgetHintFrame::hide(d->ui->tagContainer, 0734 i18nc("@info:tooltip selecting multiple transactions for edit", 0735 "Selection of tags is not possible when split transactions are part of the selection.")); 0736 tagEnabled = false; 0737 WidgetHintFrame::hide(d->ui->costCenterCombo, 0738 i18nc("@info:tooltip selecting multiple transactions for edit", 0739 "Selection of cost center is not possible when split transactions are part of the selection.")); 0740 costCenterEnabled = false; 0741 } 0742 } 0743 0744 // now disable the widgets which the user cannot use due to selected transactions 0745 d->ui->dateEdit->setEnabled(true); 0746 d->ui->creditDebitEdit->setEnabled(amountEnabled); 0747 d->ui->payeeEdit->setEnabled(payeeEnabled); 0748 d->ui->numberEdit->setEnabled(false); 0749 d->ui->categoryCombo->setEnabled(categoryEnabled); 0750 d->ui->costCenterCombo->setEnabled(costCenterEnabled); 0751 d->ui->tagContainer->setEnabled(tagEnabled); 0752 d->ui->statusCombo->setEnabled(statusEnabled); 0753 d->ui->memoEdit->setEnabled(memoEnabled); 0754 d->ui->enterButton->setEnabled(true); 0755 return true; 0756 } 0757 0758 QString MultiTransactionEditor::errorMessage() const 0759 { 0760 return d->errorMessage; 0761 } 0762 0763 bool MultiTransactionEditor::isTransactionDataValid() const 0764 { 0765 return true; 0766 }