File indexing completed on 2024-05-12 16:43:53
0001 /* 0002 SPDX-FileCopyrightText: 2016 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "newspliteditor.h" 0007 0008 // ---------------------------------------------------------------------------- 0009 // QT Includes 0010 0011 #include <QCompleter> 0012 #include <QSortFilterProxyModel> 0013 #include <QStringList> 0014 #include <QDebug> 0015 #include <QStandardItemModel> 0016 0017 // ---------------------------------------------------------------------------- 0018 // KDE Includes 0019 0020 #include <KLocalizedString> 0021 0022 // ---------------------------------------------------------------------------- 0023 // Project Includes 0024 0025 #include "creditdebithelper.h" 0026 #include "kmymoneyutils.h" 0027 #include "kmymoneyaccountcombo.h" 0028 #include "models.h" 0029 #include "accountsmodel.h" 0030 #include "costcentermodel.h" 0031 #include "ledgermodel.h" 0032 #include "splitmodel.h" 0033 #include "mymoneyaccount.h" 0034 #include "mymoneyexception.h" 0035 #include "ui_newspliteditor.h" 0036 #include "widgethintframe.h" 0037 #include "ledgerview.h" 0038 #include "icons/icons.h" 0039 #include "mymoneyenums.h" 0040 #include "modelenums.h" 0041 0042 using namespace Icons; 0043 0044 struct NewSplitEditor::Private 0045 { 0046 Private(NewSplitEditor* parent) 0047 : ui(new Ui_NewSplitEditor) 0048 , accountsModel(new AccountNamesFilterProxyModel(parent)) 0049 , costCenterModel(new QSortFilterProxyModel(parent)) 0050 , splitModel(0) 0051 , accepted(false) 0052 , costCenterRequired(false) 0053 , showValuesInverted(false) 0054 , amountHelper(nullptr) 0055 { 0056 accountsModel->setObjectName("AccountNamesFilterProxyModel"); 0057 costCenterModel->setObjectName("SortedCostCenterModel"); 0058 statusModel.setObjectName("StatusModel"); 0059 0060 costCenterModel->setSortLocaleAware(true); 0061 costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0062 0063 createStatusEntry(eMyMoney::Split::State::NotReconciled); 0064 createStatusEntry(eMyMoney::Split::State::Cleared); 0065 createStatusEntry(eMyMoney::Split::State::Reconciled); 0066 // createStatusEntry(eMyMoney::Split::State::Frozen); 0067 } 0068 0069 ~Private() 0070 { 0071 delete ui; 0072 } 0073 0074 void createStatusEntry(eMyMoney::Split::State status); 0075 bool checkForValidSplit(bool doUserInteraction = true); 0076 0077 bool costCenterChanged(int costCenterIndex); 0078 bool categoryChanged(const QString& accountId); 0079 bool numberChanged(const QString& newNumber); 0080 bool amountChanged(CreditDebitHelper* valueHelper); 0081 0082 Ui_NewSplitEditor* ui; 0083 AccountNamesFilterProxyModel* accountsModel; 0084 QSortFilterProxyModel* costCenterModel; 0085 SplitModel* splitModel; 0086 bool accepted; 0087 bool costCenterRequired; 0088 bool showValuesInverted; 0089 QStandardItemModel statusModel; 0090 QString transactionSplitId; 0091 MyMoneyAccount counterAccount; 0092 MyMoneyAccount category; 0093 CreditDebitHelper* amountHelper; 0094 }; 0095 0096 void NewSplitEditor::Private::createStatusEntry(eMyMoney::Split::State status) 0097 { 0098 QStandardItem* p = new QStandardItem(KMyMoneyUtils::reconcileStateToString(status, true)); 0099 p->setData((int)status); 0100 statusModel.appendRow(p); 0101 } 0102 0103 bool NewSplitEditor::Private::checkForValidSplit(bool doUserInteraction) 0104 { 0105 QStringList infos; 0106 bool rc = true; 0107 if(!costCenterChanged(ui->costCenterCombo->currentIndex())) { 0108 infos << ui->costCenterCombo->toolTip(); 0109 rc = false; 0110 } 0111 0112 if(doUserInteraction) { 0113 /// @todo add dialog here that shows the @a infos 0114 } 0115 return rc; 0116 } 0117 0118 bool NewSplitEditor::Private::costCenterChanged(int costCenterIndex) 0119 { 0120 bool rc = true; 0121 WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to.")); 0122 if(costCenterIndex != -1) { 0123 if(costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) { 0124 WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category.")); 0125 rc = false; 0126 } 0127 } 0128 return rc; 0129 } 0130 0131 bool NewSplitEditor::Private::categoryChanged(const QString& accountId) 0132 { 0133 bool rc = true; 0134 if(!accountId.isEmpty()) { 0135 try { 0136 QModelIndex index = Models::instance()->accountsModel()->accountById(accountId); 0137 category = Models::instance()->accountsModel()->data(index, (int)eAccountsModel::Role::Account).value<MyMoneyAccount>(); 0138 const bool isIncomeExpense = category.isIncomeExpense(); 0139 ui->costCenterCombo->setEnabled(isIncomeExpense); 0140 ui->costCenterLabel->setEnabled(isIncomeExpense); 0141 ui->numberEdit->setDisabled(isIncomeExpense); 0142 ui->numberLabel->setDisabled(isIncomeExpense); 0143 0144 costCenterRequired = category.isCostCenterRequired(); 0145 rc &= costCenterChanged(ui->costCenterCombo->currentIndex()); 0146 } catch (MyMoneyException &e) { 0147 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO; 0148 } 0149 } 0150 return rc; 0151 } 0152 0153 bool NewSplitEditor::Private::numberChanged(const QString& newNumber) 0154 { 0155 bool rc = true; 0156 WidgetHintFrame::hide(ui->numberEdit, i18n("The check number used for this transaction.")); 0157 if(!newNumber.isEmpty()) { 0158 const LedgerModel* model = Models::instance()->ledgerModel(); 0159 QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::Number, 0160 QVariant(newNumber), 0161 -1, // all splits 0162 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); 0163 0164 foreach(QModelIndex index, list) { 0165 if(model->data(index, (int)eLedgerModel::Role::AccountId) == ui->accountCombo->getSelected() 0166 && model->data(index, (int)eLedgerModel::Role::TransactionSplitId) != transactionSplitId) { 0167 WidgetHintFrame::show(ui->numberEdit, i18n("The check number <b>%1</b> has already been used in this account.", newNumber)); 0168 rc = false; 0169 break; 0170 } 0171 } 0172 } 0173 return rc; 0174 } 0175 0176 bool NewSplitEditor::Private::amountChanged(CreditDebitHelper* valueHelper) 0177 { 0178 Q_UNUSED(valueHelper); 0179 bool rc = true; 0180 return rc; 0181 } 0182 0183 0184 0185 NewSplitEditor::NewSplitEditor(QWidget* parent, const QString& counterAccountId) 0186 : QFrame(parent, Qt::FramelessWindowHint /* | Qt::X11BypassWindowManagerHint */) 0187 , d(new Private(this)) 0188 { 0189 SplitView* view = qobject_cast<SplitView*>(parent->parentWidget()); 0190 Q_ASSERT(view != 0); 0191 d->splitModel = qobject_cast<SplitModel*>(view->model()); 0192 0193 QModelIndex index = Models::instance()->accountsModel()->accountById(counterAccountId); 0194 d->counterAccount = Models::instance()->accountsModel()->data(index, (int)eAccountsModel::Role::Account).value<MyMoneyAccount>(); 0195 0196 d->ui->setupUi(this); 0197 d->ui->enterButton->setIcon(Icons::get(Icon::DialogOK)); 0198 d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel)); 0199 0200 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,}); 0201 d->accountsModel->setHideEquityAccounts(false); 0202 auto const model = Models::instance()->accountsModel(); 0203 d->accountsModel->setSourceColumns(model->getColumns()); 0204 d->accountsModel->setSourceModel(model); 0205 d->accountsModel->sort((int)eAccountsModel::Column::Account); 0206 d->ui->accountCombo->setModel(d->accountsModel); 0207 0208 d->costCenterModel->setSortRole(Qt::DisplayRole); 0209 d->costCenterModel->setSourceModel(Models::instance()->costCenterModel()); 0210 d->costCenterModel->sort((int)eAccountsModel::Column::Account); 0211 0212 d->ui->costCenterCombo->setEditable(true); 0213 d->ui->costCenterCombo->setModel(d->costCenterModel); 0214 d->ui->costCenterCombo->setModelColumn(0); 0215 d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains); 0216 0217 WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this); 0218 frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo)); 0219 frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning)); 0220 frameCollection->addWidget(d->ui->enterButton); 0221 0222 d->amountHelper = new CreditDebitHelper(this, d->ui->amountEditCredit, d->ui->amountEditDebit); 0223 0224 connect(d->ui->numberEdit, SIGNAL(textChanged(QString)), this, SLOT(numberChanged(QString))); 0225 connect(d->ui->costCenterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(costCenterChanged(int))); 0226 connect(d->ui->accountCombo, SIGNAL(accountSelected(QString)), this, SLOT(categoryChanged(QString))); 0227 connect(d->amountHelper, SIGNAL(valueChanged()), this, SLOT(amountChanged())); 0228 0229 connect(d->ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject())); 0230 connect(d->ui->enterButton, SIGNAL(clicked(bool)), this, SLOT(acceptEdit())); 0231 } 0232 0233 NewSplitEditor::~NewSplitEditor() 0234 { 0235 } 0236 0237 void NewSplitEditor::setShowValuesInverted(bool inverse) 0238 { 0239 d->showValuesInverted = inverse; 0240 } 0241 0242 bool NewSplitEditor::showValuesInverted() 0243 { 0244 return d->showValuesInverted; 0245 } 0246 0247 bool NewSplitEditor::accepted() const 0248 { 0249 return d->accepted; 0250 } 0251 0252 void NewSplitEditor::acceptEdit() 0253 { 0254 if(d->checkForValidSplit()) { 0255 d->accepted = true; 0256 emit done(); 0257 } 0258 } 0259 0260 void NewSplitEditor::reject() 0261 { 0262 emit done(); 0263 } 0264 0265 void NewSplitEditor::keyPressEvent(QKeyEvent* e) 0266 { 0267 if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { 0268 switch (e->key()) { 0269 case Qt::Key_Enter: 0270 case Qt::Key_Return: 0271 { 0272 if(focusWidget() == d->ui->cancelButton) { 0273 reject(); 0274 } else { 0275 if(d->ui->enterButton->isEnabled()) { 0276 d->ui->enterButton->click(); 0277 } 0278 return; 0279 } 0280 } 0281 break; 0282 0283 case Qt::Key_Escape: 0284 reject(); 0285 break; 0286 0287 default: 0288 e->ignore(); 0289 return; 0290 } 0291 } else { 0292 e->ignore(); 0293 } 0294 } 0295 0296 QString NewSplitEditor::accountId() const 0297 { 0298 return d->ui->accountCombo->getSelected(); 0299 } 0300 0301 void NewSplitEditor::setAccountId(const QString& id) 0302 { 0303 d->ui->accountCombo->clearEditText(); 0304 d->ui->accountCombo->setSelected(id); 0305 } 0306 0307 0308 QString NewSplitEditor::memo() const 0309 { 0310 return d->ui->memoEdit->toPlainText(); 0311 } 0312 0313 void NewSplitEditor::setMemo(const QString& memo) 0314 { 0315 d->ui->memoEdit->setPlainText(memo); 0316 } 0317 0318 MyMoneyMoney NewSplitEditor::amount() const 0319 { 0320 return d->amountHelper->value(); 0321 } 0322 0323 void NewSplitEditor::setAmount(MyMoneyMoney value) 0324 { 0325 d->amountHelper->setValue(value); 0326 } 0327 0328 QString NewSplitEditor::costCenterId() const 0329 { 0330 const int row = d->ui->costCenterCombo->currentIndex(); 0331 QModelIndex index = d->ui->costCenterCombo->model()->index(row, 0); 0332 return d->ui->costCenterCombo->model()->data(index, CostCenterModel::CostCenterIdRole).toString(); 0333 } 0334 0335 void NewSplitEditor::setCostCenterId(const QString& id) 0336 { 0337 QModelIndex index = Models::indexById(d->costCenterModel, CostCenterModel::CostCenterIdRole, id); 0338 if(index.isValid()) { 0339 d->ui->costCenterCombo->setCurrentIndex(index.row()); 0340 } 0341 } 0342 0343 QString NewSplitEditor::number() const 0344 { 0345 return d->ui->numberEdit->text(); 0346 } 0347 0348 void NewSplitEditor::setNumber(const QString& number) 0349 { 0350 d->ui->numberEdit->setText(number); 0351 } 0352 0353 0354 QString NewSplitEditor::splitId() const 0355 { 0356 return d->transactionSplitId; 0357 } 0358 0359 void NewSplitEditor::numberChanged(const QString& newNumber) 0360 { 0361 d->numberChanged(newNumber); 0362 } 0363 0364 void NewSplitEditor::categoryChanged(const QString& accountId) 0365 { 0366 d->categoryChanged(accountId); 0367 } 0368 0369 void NewSplitEditor::costCenterChanged(int costCenterIndex) 0370 { 0371 d->costCenterChanged(costCenterIndex); 0372 } 0373 0374 void NewSplitEditor::amountChanged() 0375 { 0376 // d->amountChanged(d->amountHelper); // useless call as reported by coverity scan 0377 }