Git commit 37641841e99f8a0842f3097f00d1249871d03847 by Thomas Baumgart, on behalf of Alexander Kuznetsov. Committed on 29/11/2022 at 10:11. Pushed by tbaumgart into branch 'master'.
Add a button to adjust current split with unassigned amount Adds a button to adjust the selected split with the unassigned or over-assigned amount directly to balance the transaction without using a calculator. M +2 -0 .gitignore M +23 -1 doc/details-ledgers.docbook A +- -- doc/split_unassigned.png M +73 -20 kmymoney/views/splitdialog.cpp M +5 -0 kmymoney/views/splitdialog.h M +16 -1 kmymoney/views/splitdialog.ui https://invent.kde.org/office/kmymoney/commit/37641841e99f8a0842f3097f00d1249871d03847 diff --git a/.gitignore b/.gitignore index cad01d5d7..3cd57c734 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ CMakeSettings.json .idea .vs docker/.env +.vscode + diff --git a/doc/details-ledgers.docbook b/doc/details-ledgers.docbook index 1d51d92ba..10c61f112 100644 --- a/doc/details-ledgers.docbook +++ b/doc/details-ledgers.docbook @@ -472,6 +472,29 @@ transaction unassigned. </para> +<para> + <screenshot> + <screeninfo>Split with unassigned amount</screeninfo> + <mediaobject> + <imageobject> + <imagedata fileref="split_unasigned.png" format="PNG" /> + </imageobject> + <textobject> + <phrase>Split with unasssigned amount</phrase> + </textobject> + </mediaobject> + </screenshot> +</para> + +<para> + To redistribute an 'Unassigned' or 'Overassigned' amount, select a split item that has to be adjusted. + After that click on a button <guibutton>Apply Difference</guibutton>. + If 'Unassigned' was previously shown and non-zero, that amount will be added to the selected line item value. + If 'Overassigned' was previously shown and non-zero, that amount will be subtracted from the selected line item value. + As the result, the 'Unassigned' amount will be set to zero and a transaction could be saved + successfully. +</para> + <para> Note that the category field in the transaction form or the transaction list now displays <emphasis>Split transaction</emphasis>. @@ -729,4 +752,3 @@ </para> </sect1> </chapter> - diff --git a/doc/split_unassigned.png b/doc/split_unassigned.png new file mode 100644 index 000000000..047666601 Binary files /dev/null and b/doc/split_unassigned.png differ diff --git a/kmymoney/views/splitdialog.cpp b/kmymoney/views/splitdialog.cpp index e2042b772..573f85ab5 100644 --- a/kmymoney/views/splitdialog.cpp +++ b/kmymoney/views/splitdialog.cpp @@ -15,9 +15,10 @@ // ---------------------------------------------------------------------------- // KDE Includes +#include <KColorScheme> +#include <KConfigGroup> #include <KLocalizedString> #include <KSharedConfig> -#include <KConfigGroup> // ---------------------------------------------------------------------------- // Project Includes @@ -81,7 +82,8 @@ static const int DiffRow = 1; static const int AmountRow = 2; static const int HeaderCol = 0; static const int ValueCol = 1; - +static const int SummaryRows = 3; +static const int SummaryCols = 2; void SplitDialog::Private::deleteSplits(QModelIndexList indexList) { @@ -106,7 +108,7 @@ void SplitDialog::Private::deleteSplits(QModelIndexList indexList) if (!(id.isEmpty() || id.endsWith('-'))) { model->removeRow(*it); } - } while(it != sortedList.constBegin()); + } while (it != sortedList.constBegin()); blockEditorStart(false); } @@ -125,7 +127,7 @@ void SplitDialog::Private::blockImmediateEditor() void SplitDialog::Private::selectRow(int row) { if (row >= ui->splitView->model()->rowCount()) - row = ui->splitView->model()->rowCount()-1; + row = ui->splitView->model()->rowCount() - 1; if (row >= 0) { blockEditorStart(true); ui->splitView->selectRow(row); @@ -165,6 +167,7 @@ SplitDialog::SplitDialog(const MyMoneySecurity& commodity, connect(d->ui->deleteAllButton, &QAbstractButton::pressed, this, &SplitDialog::deleteAllSplits); connect(d->ui->deleteButton, &QAbstractButton::pressed, this, &SplitDialog::deleteSelectedSplits); connect(d->ui->deleteZeroButton, &QAbstractButton::pressed, this, &SplitDialog::deleteZeroSplits); + connect(d->ui->adjustUnassigned, &QAbstractButton::pressed, this, &SplitDialog::adjustUnassigned); connect(d->ui->mergeButton, &QAbstractButton::pressed, this, &SplitDialog::mergeSplits); connect(d->ui->newSplitButton, &QAbstractButton::pressed, this, &SplitDialog::newSplit); @@ -176,19 +179,24 @@ SplitDialog::SplitDialog(const MyMoneySecurity& commodity, size.setHeight(size.height() - 1); resize(size.expandedTo(minimumSizeHint())); + // m_unassigned_over = KColorScheme(QPalette::Normal).foreground(KColorScheme::PositiveText); + // m_unassigned_under = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText); + m_unassigned_error = KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText); + m_unassigned_normal = KColorScheme(QPalette::Normal).foreground(KColorScheme::NormalText); + // finish polishing the widgets QMetaObject::invokeMethod(this, "adjustSummary", Qt::QueuedConnection); } SplitDialog::~SplitDialog() { - auto grp = KSharedConfig::openConfig()->group("SplitTransactionEditor"); + auto grp = KSharedConfig::openConfig()->group("SplitTransactionEditor"); grp.writeEntry("Geometry", size()); } int SplitDialog::exec() { - if(!d->ui->splitView->model()) { + if (!d->ui->splitView->model()) { qWarning() << "SplitDialog::exec() executed without a model. Use setModel() before calling exec()."; return QDialog::Rejected; } @@ -202,15 +210,15 @@ void SplitDialog::accept() if (d->transactionTotal.isAutoCalc()) { d->transactionTotal = d->splitsTotal; - } else if(d->transactionTotal != d->splitsTotal) { + } else if (d->transactionTotal != d->splitsTotal) { QPointer<SplitAdjustDialog> dlg = new SplitAdjustDialog(this); dlg->setValues(d->ui->summaryView->item(AmountRow, ValueCol)->data(Qt::DisplayRole).toString(), d->ui->summaryView->item(SumRow, ValueCol)->data(Qt::DisplayRole).toString(), d->ui->summaryView->item(DiffRow, ValueCol)->data(Qt::DisplayRole).toString(), d->ui->splitView->model()->rowCount()); accept = false; - if(dlg->exec() == QDialog::Accepted && dlg) { - switch(dlg->selectedOption()) { + if (dlg->exec() == QDialog::Accepted && dlg) { + switch (dlg->selectedOption()) { case SplitAdjustDialog::SplitAdjustContinue: break; case SplitAdjustDialog::SplitAdjustChange: @@ -229,7 +237,7 @@ void SplitDialog::accept() delete dlg; updateButtonState(); } - if(accept) + if (accept) QDialog::accept(); } @@ -248,7 +256,7 @@ void SplitDialog::setModel(SplitModel* model) d->splitModel = model; d->ui->splitView->setModel(model); - if(model->rowCount() > 0) { + if (model->rowCount() > 0) { QModelIndex index = model->index(0, 0); d->ui->splitView->setCurrentIndex(index); } @@ -262,6 +270,15 @@ void SplitDialog::setModel(SplitModel* model) void SplitDialog::adjustSummary() { + // Apply color scheme to the summary panel + for (int row = 0; row < SummaryRows; row++) { + for (int col = 0; col < SummaryCols; col++) { + if (row == DiffRow && col == ValueCol) + continue; + d->ui->summaryView->item(row, col)->setForeground(m_unassigned_normal); + } + } + // Only show the currency symbol when multiple currencies are involved QString currencySymbol = d->commoditySymbol; if (!d->splitModel->hasMultiCurrencySplits()) { @@ -281,7 +298,7 @@ void SplitDialog::adjustSummary() QString formattedValue = (d->splitsTotal * d->inversionFactor).formatMoney(currencySymbol, denom); d->ui->summaryView->item(SumRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); - if(d->transactionEditor) { + if (d->transactionEditor) { if (d->transactionTotal.isAutoCalc()) { formattedValue = (d->splitsTotal * d->inversionFactor).formatMoney(currencySymbol, denom); } else { @@ -290,10 +307,17 @@ void SplitDialog::adjustSummary() d->ui->summaryView->item(AmountRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); if (!d->transactionTotal.isAutoCalc()) { - if ((d->transactionTotal.abs() - d->splitsTotal.abs()).isNegative()) { - d->ui->summaryView->item(DiffRow, HeaderCol)->setData(Qt::DisplayRole, i18nc("Split editor summary", "Assigned too much")); + auto diff = d->transactionTotal.abs() - d->splitsTotal.abs(); + if (diff.isNegative()) { + d->ui->summaryView->item(DiffRow, HeaderCol)->setData(Qt::DisplayRole, i18nc("Split editor summary", "Overassigned")); + d->ui->summaryView->item(DiffRow, ValueCol)->setForeground(m_unassigned_error); } else { d->ui->summaryView->item(DiffRow, HeaderCol)->setData(Qt::DisplayRole, i18nc("Split editor summary", "Unassigned")); + if (diff.isZero()) { + d->ui->summaryView->item(DiffRow, ValueCol)->setForeground(m_unassigned_normal); + } else { + d->ui->summaryView->item(DiffRow, ValueCol)->setForeground(m_unassigned_error); + } } formattedValue = (d->transactionTotal - d->splitsTotal).abs().formatMoney(currencySymbol, denom); d->ui->summaryView->item(DiffRow, ValueCol)->setData(Qt::DisplayRole, formattedValue); @@ -329,15 +353,15 @@ void SplitDialog::newSplit() // are on this row already with the editor closed things // are a bit more complicated. QModelIndex index = d->ui->splitView->currentIndex(); - if(index.isValid()) { + if (index.isValid()) { int row = index.row(); - if(row != d->ui->splitView->model()->rowCount()-1) { - d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount()-1); + if (row != d->ui->splitView->model()->rowCount() - 1) { + d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount() - 1); } else { d->ui->splitView->edit(index); } } else { - d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount()-1); + d->ui->splitView->selectRow(d->ui->splitView->model()->rowCount() - 1); } } @@ -357,6 +381,7 @@ void SplitDialog::updateButtonState() d->ui->deleteAllButton->setEnabled(false); d->ui->mergeButton->setEnabled(false); d->ui->deleteZeroButton->setEnabled(false); + d->ui->adjustUnassigned->setEnabled(false); if (!d->readOnly) { if (d->ui->splitView->selectionModel()->selectedRows().count() > 0) { @@ -367,6 +392,13 @@ void SplitDialog::updateButtonState() d->ui->deleteAllButton->setEnabled(true); } + if (d->ui->splitView->selectionModel()->selectedRows().count() == 1 + && !d->ui->splitView->selectionModel()->selectedIndexes().at(0).data(eMyMoney::Model::IdRole).toString().isEmpty()) { + if (!d->transactionTotal.isAutoCalc()) { + d->ui->adjustUnassigned->setDisabled((d->transactionTotal.abs() - d->splitsTotal.abs()).isZero()); + } + } + QAbstractItemModel* model = d->ui->splitView->model(); QSet<QString> accountIDs; const auto rows = model->rowCount(); @@ -408,14 +440,35 @@ void SplitDialog::deleteAllSplits() d->selectRow(row); } +void SplitDialog::adjustUnassigned() +{ + QModelIndex index = d->ui->splitView->currentIndex(); + if (index.isValid()) { + // extract current values ... + auto shares = index.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>(); + auto value = index.data(eMyMoney::Model::SplitValueRole).value<MyMoneyMoney>(); + const auto price = value / shares; + const auto diff = d->transactionTotal - d->splitsTotal; + // ... and adjust shares and value ... + value += diff; + shares = value / price; + // ... and update the model + auto model = d->ui->splitView->model(); + model->setData(index, QVariant::fromValue<MyMoneyMoney>(shares), eMyMoney::Model::SplitSharesRole); + model->setData(index, QVariant::fromValue<MyMoneyMoney>(value), eMyMoney::Model::SplitValueRole); + + adjustSummary(); + } +} + void SplitDialog::deleteZeroSplits() { QAbstractItemModel* model = d->ui->splitView->model(); QModelIndexList list = model->match(model->index(0, 0), eMyMoney::Model::IdRole, QLatin1String(".+"), -1, Qt::MatchRegularExpression); - for(int row = 0; row < list.count();) { + for (int row = 0; row < list.count();) { const auto idx = list.at(row); - if(!idx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>().isZero()) { + if (!idx.data(eMyMoney::Model::SplitSharesRole).value<MyMoneyMoney>().isZero()) { list.removeAt(row); } else { ++row; diff --git a/kmymoney/views/splitdialog.h b/kmymoney/views/splitdialog.h index 0c874e13b..badf4a5dc 100644 --- a/kmymoney/views/splitdialog.h +++ b/kmymoney/views/splitdialog.h @@ -72,10 +72,15 @@ protected Q_SLOTS: void mergeSplits(); void selectionChanged(); void updateButtonState(); + void adjustUnassigned(); protected: void resizeEvent(QResizeEvent* ev) final override; void adjustSummaryWidth(); + // QBrush m_unassigned_over; + // QBrush m_unassigned_under; + QBrush m_unassigned_normal; + QBrush m_unassigned_error; private: class Private; diff --git a/kmymoney/views/splitdialog.ui b/kmymoney/views/splitdialog.ui index 3536f0bab..490f28f73 100644 --- a/kmymoney/views/splitdialog.ui +++ b/kmymoney/views/splitdialog.ui @@ -33,7 +33,7 @@ <property name="minimumSize"> <size> <width>0</width> - <height>72</height> + <height>82</height> </size> </property> <property name="palette"> @@ -621,6 +621,20 @@ </property> </widget> </item> + + <item> + <widget class="QPushButton" name="adjustUnassigned"> + <property name="toolTip"> + <string>Apply 'Unassigned' or 'Overassigned' amount to the current split. +'Unassigned' amount will be added or 'Overassigned' amount +will be subtracted from the amount of the selected split.</string> + </property> + <property name="text"> + <string>Apply Difference</string> + </property> + </widget> + </item> + <item> <widget class="QPushButton" name="deleteZeroButton"> <property name="toolTip"> @@ -631,6 +645,7 @@ </property> </widget> </item> + <item> <widget class="QPushButton" name="deleteButton"> <property name="text">