[office/kmymoney] kmymoney: Add optional memo column to ledger

Thomas Baumgart <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit e86b2bad06eda064624769d7dc101702e58e8d66 by Thomas Baumgart, on behalf of Simone Iori.
Committed on 24/07/2026 at 15:38.
Pushed by tbaumgart into branch 'master'.

Add optional memo column to ledger

Implements the wishlist from https://bugs.kde.org/show_bug.cgi?id=439376: since the ledger rework, the memo is only visible with "show all register entries in full detail" (which doubles row height) or the ledger lens (which requires selecting each transaction). After importing a batch of transactions, the ledger shows many nearly identical rows where the memo is the only distinguishing information.

![screenshot.png](/uploads/b98a4d648fa6d481bda9bb69fadcc06f/screenshot.png){width=661 height=168}

This adds a **Memo** column to `JournalModel`, showing the split's memo flattened to a single line (same flattening as the existing `SplitSingleLineMemoRole`). It follows the same pattern used to add the entry-date column (commit aa021cb18).

Behavior:

* Toggleable via the existing column selector (header context menu), like Number/Payee/Entry — no new settings UI needed. 
* Hidden for existing users: a saved `ColumnsSelection `doesn't include the new column, so nothing changes unless the user opts in. On fresh configs it behaves like the other selectable columns. 
* The transaction select and search dialogs pin it hidden to keep their fixed column sets unchanged. 
* Related to bug 274130 (category column): this MR doesn't implement that, but exercises the same mechanism a category column could later use; the concerns raised there (multi-split, small screens) don't apply since the column is opt-in and single-line. The second commit fixes a pre-existing issue noticed while testing: the tags view register had no column selector config group, so column layout (order/width/visibility) was lost on restart. It now persists via a `TagLedger `group, mirroring the payees view's `PayeeLedger`.

Note: `QHeaderView::restoreState()` rejects saved state with a different section count, so users upgrading will see a one-time reset of ledger column widths/order (visibility choices are preserved via `ColumnsSelection`).

Tested with a 17k-transaction file: toggling/persistence across restarts, standard + investment + reconciliation ledgers, coexistence with detail/lens modes, multi-line memos, sorting, and the pinned dialogs.

M  +10   -0    kmymoney/mymoney/storage/journalmodel.cpp
M  +1    -0    kmymoney/mymoney/storage/journalmodel.h
M  +9    -4    kmymoney/views/journaldelegate.cpp
M  +1    -0    kmymoney/views/ksearchtransactiondlg.cpp
M  +1    -0    kmymoney/views/ktagsview.cpp
M  +1    -0    kmymoney/views/ktransactionselectdlg.cpp

https://invent.kde.org/office/kmymoney/-/commit/e86b2bad06eda064624769d7dc101702e58e8d66

diff --git a/kmymoney/mymoney/storage/journalmodel.cpp b/kmymoney/mymoney/storage/journalmodel.cpp
index 9a3ab6f32..5e58ef351 100644
--- a/kmymoney/mymoney/storage/journalmodel.cpp
+++ b/kmymoney/mymoney/storage/journalmodel.cpp
@@ -69,6 +69,7 @@ struct JournalModel::Private {
               {Value, i18nc("@title:column", "Value")},
               {Balance, i18nc("@title:column", "Balance")},
               {EntryDate, i18nc("@title:column Entry date", "Entry")},
+              {Memo, i18nc("@title:column", "Memo")},
           }))
         , extendedHeaderData(QHash<Column, QString>({
               {Number, i18nc("@title:column Cheque Number (ext)", "Number")},
@@ -725,6 +726,15 @@ QVariant JournalModel::data(const QModelIndex& idx, int role) const
 
         case EntryDate:
             return MyMoneyUtils::formatDate(transaction.entryDate());
+
+        case Memo: {
+            QString memo(split.memo());
+            // remove empty lines
+            memo.replace(QStringLiteral("\n\n"), QStringLiteral("\n"));
+            // replace '\n' with ", "
+            memo.replace(QStringLiteral("\n"), QStringLiteral(", "));
+            return memo;
+        }
         }
         break;
 
diff --git a/kmymoney/mymoney/storage/journalmodel.h b/kmymoney/mymoney/storage/journalmodel.h
index 5c822d7de..5d80b6755 100644
--- a/kmymoney/mymoney/storage/journalmodel.h
+++ b/kmymoney/mymoney/storage/journalmodel.h
@@ -159,6 +159,7 @@ public:
         Amount,
         Value,
         Balance,
+        Memo,
         // insert new columns above this line
         MaxColumns,
     };
diff --git a/kmymoney/views/journaldelegate.cpp b/kmymoney/views/journaldelegate.cpp
index fba9bc584..699e795d3 100644
--- a/kmymoney/views/journaldelegate.cpp
+++ b/kmymoney/views/journaldelegate.cpp
@@ -118,6 +118,7 @@ public:
         const auto showLedgerLens = LedgerViewSettings::instance()->showLedgerLens();
         const auto showAllSplits = LedgerViewSettings::instance()->showAllSplits() & showLedgerLens;
         const auto havePayeeColumn = !m_view->isColumnHidden(JournalModel::Payee);
+        const auto haveMemoColumn = !m_view->isColumnHidden(JournalModel::Memo);
 
         if (index.column() == JournalModel::Column::Detail) {
             const auto showDetails = LedgerViewSettings::instance()->showTransactionDetails();
@@ -128,7 +129,9 @@ public:
                     rc.lines << index.data(eMyMoney::Model::TransactionBrokerageAccountRole).toString();
                     rc.lines << index.data(eMyMoney::Model::TransactionInterestCategoryRole).toString();
                     rc.lines << index.data(eMyMoney::Model::TransactionFeesCategoryRole).toString();
-                    rc.lines << index.data(eMyMoney::Model::SplitSingleLineMemoRole).toString();
+                    if (!haveMemoColumn) {
+                        rc.lines << index.data(eMyMoney::Model::SplitSingleLineMemoRole).toString();
+                    }
                 } else {
                     rc.lines << index.data(eMyMoney::Model::SplitActivityRole).toString();
                 }
@@ -151,7 +154,7 @@ public:
                             ++rc.italicStartLine;
                         }
                     }
-                    const auto memo = index.data(eMyMoney::Model::Roles::SplitStyledSingleLineMemoRole).toString();
+                    const auto memo = haveMemoColumn ? QString() : index.data(eMyMoney::Model::Roles::SplitStyledSingleLineMemoRole).toString();
                     if (!memo.isEmpty()) {
                         rc.lines << memo;
                         ++rc.italicStartLine;
@@ -193,11 +196,13 @@ public:
                         rc.lines << index.data(eMyMoney::Model::Roles::SplitPayeeRole).toString();
                     }
                     rc.lines << index.data(eMyMoney::Model::Roles::TransactionCounterAccountRole).toString();
-                    rc.lines << index.data(eMyMoney::Model::Roles::SplitStyledSingleLineMemoRole).toString();
+                    if (!haveMemoColumn) {
+                        rc.lines << index.data(eMyMoney::Model::Roles::SplitStyledSingleLineMemoRole).toString();
+                    }
                     addTags();
 
                 } else {
-                    if (rc.lines.at(0).isEmpty()) {
+                    if (!haveMemoColumn && rc.lines.at(0).isEmpty()) {
                         rc.lines.clear();
                         rc.lines << index.data(eMyMoney::Model::Roles::SplitStyledSingleLineMemoRole).toString();
                     }
diff --git a/kmymoney/views/ksearchtransactiondlg.cpp b/kmymoney/views/ksearchtransactiondlg.cpp
index 734fd3f4e..36f8cbef2 100644
--- a/kmymoney/views/ksearchtransactiondlg.cpp
+++ b/kmymoney/views/ksearchtransactiondlg.cpp
@@ -92,6 +92,7 @@ public:
             JournalModel::Column::Amount,
             JournalModel::Column::Value,
             JournalModel::Column::Balance,
+            JournalModel::Column::Memo,
         };
         ui.m_ledgerView->setColumnsHidden(columns);
         columns = {
diff --git a/kmymoney/views/ktagsview.cpp b/kmymoney/views/ktagsview.cpp
index b620a103a..a6eeb23f7 100644
--- a/kmymoney/views/ktagsview.cpp
+++ b/kmymoney/views/ktagsview.cpp
@@ -95,6 +95,7 @@ public:
         m_updateAction->setEnabled(false);
 
         ui->m_register->setSingleLineDetailRole(eMyMoney::Model::TransactionCounterAccountRole);
+        ui->m_register->setColumnSelectorGroupName(QLatin1String("TagLedger"));
         ui->m_tagsList->setContextMenuPolicy(Qt::CustomContextMenu);
 
         ui->m_filterBox->addItem(i18nc("@item Show all tags", "All"), ItemRenameProxyModel::eAllItem);
diff --git a/kmymoney/views/ktransactionselectdlg.cpp b/kmymoney/views/ktransactionselectdlg.cpp
index 94baed0ef..423c988ea 100644
--- a/kmymoney/views/ktransactionselectdlg.cpp
+++ b/kmymoney/views/ktransactionselectdlg.cpp
@@ -78,6 +78,7 @@ KTransactionSelectDlg::KTransactionSelectDlg(QWidget* parent)
         JournalModel::Column::Amount,
         JournalModel::Column::Value,
         JournalModel::Column::Balance,
+        JournalModel::Column::Memo,
     };
     d->ui->m_ledgerView->setColumnsHidden(columns);
     columns = {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.