File indexing completed on 2024-05-12 16:43:55
0001 /* 0002 SPDX-FileCopyrightText: 2015 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "splitdialog.h" 0007 0008 // ---------------------------------------------------------------------------- 0009 // QT Includes 0010 0011 #include <QDebug> 0012 #include <QHeaderView> 0013 #include <QPointer> 0014 0015 // ---------------------------------------------------------------------------- 0016 // KDE Includes 0017 0018 #include <KLocalizedString> 0019 0020 // ---------------------------------------------------------------------------- 0021 // Project Includes 0022 0023 #include "ui_splitdialog.h" 0024 #include "mymoneyaccount.h" 0025 #include "splitdelegate.h" 0026 #include "newtransactioneditor.h" 0027 #include "splitadjustdialog.h" 0028 #include "modelenums.h" 0029 #include "icons/icons.h" 0030 0031 using namespace Icons; 0032 0033 class SplitDialog::Private 0034 { 0035 public: 0036 Private(SplitDialog* p) 0037 : parent(p) 0038 , ui(new Ui_SplitDialog) 0039 , splitDelegate(nullptr) 0040 , transactionEditor(nullptr) 0041 { 0042 } 0043 0044 ~Private() 0045 { 0046 delete ui; 0047 } 0048 0049 void deleteSplits(QModelIndexList indexList); 0050 0051 SplitDialog* parent; 0052 Ui_SplitDialog* ui; 0053 SplitDelegate* splitDelegate; 0054 0055 /** 0056 * The account in which this split editor was opened 0057 */ 0058 MyMoneyAccount account; 0059 0060 /** 0061 * The parent transaction editor which opened the split editor 0062 */ 0063 NewTransactionEditor* transactionEditor; 0064 0065 MyMoneyMoney transactionTotal; 0066 MyMoneyMoney splitsTotal; 0067 }; 0068 0069 static const int SumRow = 0; 0070 static const int DiffRow = 1; 0071 static const int AmountRow = 2; 0072 static const int HeaderCol = 0; 0073 static const int ValueCol = 1; 0074 0075 0076 void SplitDialog::Private::deleteSplits(QModelIndexList indexList) 0077 { 0078 if (indexList.isEmpty()) { 0079 return; 0080 } 0081 0082 // remove from the end so that the row information stays 0083 // consistent and is not changed due to index changes 0084 QMap<int, int> sortedList; 0085 foreach(auto index, indexList) { 0086 sortedList[index.row()] = index.row(); 0087 } 0088 0089 QMap<int, int>::const_iterator it = sortedList.constEnd(); 0090 do { 0091 --it; 0092 ui->splitView->model()->removeRow(*it); 0093 } while(it != sortedList.constBegin()); 0094 } 0095 0096 0097 SplitDialog::SplitDialog(const MyMoneyAccount& account, const MyMoneyMoney& amount, NewTransactionEditor* parent, Qt::WindowFlags f) 0098 : QDialog(parent, f) 0099 , d(new Private(this)) 0100 { 0101 d->transactionEditor = parent; 0102 d->account = account; 0103 d->transactionTotal = amount; 0104 d->ui->setupUi(this); 0105 0106 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Date, true); 0107 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Number, true); 0108 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Security, true); 0109 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Reconciliation, true); 0110 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Payment, false); 0111 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Deposit, false); 0112 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Quantity, true); 0113 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Price, true); 0114 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Amount, true); 0115 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Value, true); 0116 d->ui->splitView->setColumnHidden((int)eLedgerModel::Column::Balance, true); 0117 d->ui->splitView->setSelectionMode(QAbstractItemView::ExtendedSelection); 0118 d->ui->splitView->setSelectionBehavior(QAbstractItemView::SelectRows); 0119 0120 // setup the item delegate 0121 d->splitDelegate = new SplitDelegate(d->ui->splitView); 0122 d->ui->splitView->setItemDelegate(d->splitDelegate); 0123 0124 d->ui->okButton->setIcon(Icons::get(Icon::DialogOK)); 0125 d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel)); 0126 0127 // setup some connections 0128 connect(d->ui->splitView, &LedgerView::aboutToStartEdit, this, &SplitDialog::disableButtons); 0129 connect(d->ui->splitView, &LedgerView::aboutToFinishEdit, this, &SplitDialog::enableButtons); 0130 0131 connect(d->ui->deleteAllButton, &QAbstractButton::pressed, this, &SplitDialog::deleteAllSplits); 0132 connect(d->ui->deleteButton, &QAbstractButton::pressed, this, &SplitDialog::deleteSelectedSplits); 0133 connect(d->ui->deleteZeroButton, &QAbstractButton::pressed, this, &SplitDialog::deleteZeroSplits); 0134 connect(d->ui->mergeButton, &QAbstractButton::pressed, this, &SplitDialog::mergeSplits); 0135 connect(d->ui->newSplitButton, &QAbstractButton::pressed, this, &SplitDialog::newSplit); 0136 0137 // finish polishing the widgets 0138 QMetaObject::invokeMethod(this, "adjustSummary", Qt::QueuedConnection); 0139 } 0140 0141 SplitDialog::~SplitDialog() 0142 { 0143 } 0144 0145 int SplitDialog::exec() 0146 { 0147 if(!d->ui->splitView->model()) { 0148 qWarning() << "SplitDialog::exec() executed without a model. Use setModel() before calling exec()."; 0149 return QDialog::Rejected; 0150 } 0151 return QDialog::exec(); 0152 } 0153 0154 void SplitDialog::accept() 0155 { 0156 adjustSummary(); 0157 bool accept = true; 0158 if(d->transactionTotal != d->splitsTotal) { 0159 QPointer<SplitAdjustDialog> dlg = new SplitAdjustDialog(this); 0160 dlg->setValues(d->ui->summaryView->item(AmountRow, ValueCol)->data(Qt::DisplayRole).toString(), 0161 d->ui->summaryView->item(SumRow, ValueCol)->data(Qt::DisplayRole).toString(), 0162 d->ui->summaryView->item(DiffRow, ValueCol)->data(Qt::DisplayRole).toString(), 0163 d->ui->splitView->model()->rowCount()); 0164 accept = false; 0165 if(dlg->exec() == QDialog::Accepted && dlg) { 0166 switch(dlg->selectedOption()) { 0167 case SplitAdjustDialog::SplitAdjustContinue: 0168 break; 0169 case SplitAdjustDialog::SplitAdjustChange: 0170 d->transactionTotal = d->splitsTotal; 0171 accept = true; 0172 break; 0173 case SplitAdjustDialog::SplitAdjustDistribute: 0174 qWarning() << "SplitDialog::accept needs to implement the case SplitAdjustDialog::SplitAdjustDistribute"; 0175 accept = true; 0176 break; 0177 case SplitAdjustDialog::SplitAdjustLeaveAsIs: 0178 accept = true; 0179 break; 0180 } 0181 } 0182 delete dlg; 0183 updateButtonState(); 0184 } 0185 if(accept) 0186 QDialog::accept(); 0187 } 0188 0189 void SplitDialog::enableButtons() 0190 { 0191 d->ui->buttonContainer->setEnabled(true); 0192 } 0193 0194 void SplitDialog::disableButtons() 0195 { 0196 d->ui->buttonContainer->setEnabled(false); 0197 } 0198 0199 void SplitDialog::setModel(QAbstractItemModel* model) 0200 { 0201 d->ui->splitView->setModel(model); 0202 0203 if(model->rowCount() > 0) { 0204 QModelIndex index = model->index(0, 0); 0205 d->ui->splitView->setCurrentIndex(index); 0206 } 0207 0208 adjustSummary(); 0209 0210 // force an update of the summary if data changes in the model 0211 connect(model, &QAbstractItemModel::dataChanged, this, &SplitDialog::adjustSummary); 0212 connect(d->ui->splitView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SplitDialog::selectionChanged); 0213 } 0214 0215 void SplitDialog::adjustSummary() 0216 { 0217 d->splitsTotal = 0; 0218 for(int row = 0; row < d->ui->splitView->model()->rowCount(); ++row) { 0219 QModelIndex index = d->ui->splitView->model()->index(row, 0); 0220 if(index.isValid()) { 0221 d->splitsTotal += d->ui->splitView->model()->data(index, (int)eLedgerModel::Role::SplitValue).value<MyMoneyMoney>(); 0222 } 0223 } 0224 QString formattedValue = d->splitsTotal.formatMoney(d->account.fraction()); 0225 d->ui->summaryView->item(SumRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); 0226 0227 if(d->transactionEditor) { 0228 d->transactionTotal = d->transactionEditor->transactionAmount(); 0229 formattedValue = d->transactionTotal.formatMoney(d->account.fraction()); 0230 d->ui->summaryView->item(AmountRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); 0231 if((d->transactionTotal - d->splitsTotal).isNegative()) { 0232 d->ui->summaryView->item(DiffRow, HeaderCol)->setData(Qt::DisplayRole, i18nc("Split editor summary", "Assigned too much")); 0233 } else { 0234 d->ui->summaryView->item(DiffRow, HeaderCol)->setData(Qt::DisplayRole, i18nc("Split editor summary", "Unassigned")); 0235 } 0236 formattedValue = (d->transactionTotal - d->splitsTotal).abs().formatMoney(d->account.fraction()); 0237 d->ui->summaryView->item(DiffRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); 0238 } else { 0239 d->ui->summaryView->item(SumRow, ValueCol)->setData(Qt::DisplayRole, QString()); 0240 d->ui->summaryView->item(AmountRow, ValueCol)->setData(Qt::DisplayRole, QString()); 0241 } 0242 0243 adjustSummaryWidth(); 0244 updateButtonState(); 0245 } 0246 0247 void SplitDialog::resizeEvent(QResizeEvent* ev) 0248 { 0249 QDialog::resizeEvent(ev); 0250 adjustSummaryWidth(); 0251 } 0252 0253 void SplitDialog::adjustSummaryWidth() 0254 { 0255 d->ui->summaryView->resizeColumnToContents(1); 0256 d->ui->summaryView->horizontalHeader()->resizeSection(0, d->ui->summaryView->width() - d->ui->summaryView->horizontalHeader()->sectionSize(1) - 10); 0257 } 0258 0259 void SplitDialog::newSplit() 0260 { 0261 // creating a new split is easy, because we simply 0262 // need to select the last entry in the view. If we 0263 // are on this row already with the editor closed things 0264 // are a bit more complicated. 0265 QModelIndex index = d->ui->splitView->currentIndex(); 0266 if(index.isValid()) { 0267 int row = index.row(); 0268 if(row != d->ui->splitView->model()->rowCount()-1) { 0269 d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount()-1); 0270 } else { 0271 d->ui->splitView->edit(index); 0272 } 0273 } else { 0274 d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount()-1); 0275 } 0276 } 0277 0278 MyMoneyMoney SplitDialog::transactionAmount() const 0279 { 0280 return d->transactionTotal; 0281 } 0282 0283 void SplitDialog::selectionChanged() 0284 { 0285 updateButtonState(); 0286 } 0287 0288 void SplitDialog::updateButtonState() 0289 { 0290 d->ui->deleteButton->setEnabled(false); 0291 d->ui->deleteAllButton->setEnabled(false); 0292 d->ui->mergeButton->setEnabled(false); 0293 d->ui->deleteZeroButton->setEnabled(false); 0294 0295 if(d->ui->splitView->selectionModel()->selectedRows().count() >= 1) { 0296 d->ui->deleteButton->setEnabled(true); 0297 } 0298 0299 if(d->ui->splitView->model()->rowCount() > 2) { 0300 d->ui->deleteAllButton->setEnabled(true); 0301 } 0302 0303 QAbstractItemModel* model = d->ui->splitView->model(); 0304 QSet<QString> accountIDs; 0305 for(int row = 0; row < model->rowCount(); ++row) { 0306 const QModelIndex index = model->index(row,0); 0307 // don't check the empty line at the end 0308 if(model->data(index, (int)eLedgerModel::Role::SplitId).toString().isEmpty()) 0309 continue; 0310 0311 const QString accountID = model->data(index, (int)eLedgerModel::Role::AccountId).toString(); 0312 const MyMoneyMoney value = model->data(index, (int)eLedgerModel::Role::SplitShares).value<MyMoneyMoney>(); 0313 if(accountIDs.contains(accountID)) { 0314 d->ui->mergeButton->setEnabled(true); 0315 } 0316 if(value.isZero()) { 0317 d->ui->deleteZeroButton->setEnabled(true); 0318 } 0319 } 0320 } 0321 0322 void SplitDialog::deleteSelectedSplits() 0323 { 0324 d->deleteSplits(d->ui->splitView->selectionModel()->selectedRows()); 0325 adjustSummary(); 0326 } 0327 0328 void SplitDialog::deleteAllSplits() 0329 { 0330 QAbstractItemModel* model = d->ui->splitView->model(); 0331 QModelIndexList list = model->match(model->index(0,0), 0332 (int)eLedgerModel::Role::SplitId, 0333 QLatin1String(".+"), 0334 -1, 0335 Qt::MatchRegExp 0336 ); 0337 d->deleteSplits(list); 0338 adjustSummary(); 0339 } 0340 0341 void SplitDialog::deleteZeroSplits() 0342 { 0343 QAbstractItemModel* model = d->ui->splitView->model(); 0344 QModelIndexList list = model->match(model->index(0,0), 0345 (int)eLedgerModel::Role::SplitId, 0346 QLatin1String(".+"), 0347 -1, 0348 Qt::MatchRegExp 0349 ); 0350 for(int idx = 0; idx < list.count();) { 0351 QModelIndex index = list.at(idx); 0352 if(!model->data(index, (int)eLedgerModel::Role::SplitShares).value<MyMoneyMoney>().isZero()) { 0353 list.removeAt(idx); 0354 } else { 0355 ++idx; 0356 } 0357 } 0358 d->deleteSplits(list); 0359 adjustSummary(); 0360 } 0361 0362 void SplitDialog::mergeSplits() 0363 { 0364 qDebug() << "Merge splits not yet implemented."; 0365 adjustSummary(); 0366 }