[office/kmymoney] kmymoney/mymoney: Fix detection logic of opening balance transactions

Thomas Baumgart <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 3bbedfea0b90a367e0bb54edb7af801f845e98ed by Thomas Baumgart.
Committed on 27/07/2026 at 19:06.
Pushed by tbaumgart into branch 'master'.

Fix detection logic of opening balance transactions

Also add testcase for the scenario described in the bug report

BUG: 523555
FIXED-IN: 5.2.3

M  +10   -9    kmymoney/mymoney/mymoneyfile.cpp
M  +53   -0    kmymoney/mymoney/tests/test-mymoneyfile.cpp
M  +1    -0    kmymoney/mymoney/tests/test-mymoneyfile.h

https://invent.kde.org/office/kmymoney/-/commit/3bbedfea0b90a367e0bb54edb7af801f845e98ed

diff --git a/kmymoney/mymoney/mymoneyfile.cpp b/kmymoney/mymoney/mymoneyfile.cpp
index 50943d06d..52c53c963 100644
--- a/kmymoney/mymoney/mymoneyfile.cpp
+++ b/kmymoney/mymoney/mymoneyfile.cpp
@@ -2229,32 +2229,33 @@ QString MyMoneyFile::openingBalanceTransaction(const MyMoneyAccount& acc) const
     const auto end = d->journalModel.rowCount();
 
     // look for a transaction with two splits, one referencing
-    // acc.id(), the other openAcc.id()
-    int matchCount = 0;
+    // acc.id(), the other openAcc.id(). Keep in mind, that an
+    // account can be referenced in multiple splits of the same
+    // transaction
     QString lastTxId;
     QString txId;
     QString splitAccoountId;
     QModelIndex idx;
+    QSet<QString> matchedAccountIds;
     for (int row = start; row < end; ++row) {
         idx = d->journalModel.index(row, 0);
         txId = idx.data(eMyMoney::Model::JournalTransactionIdRole).toString();
         if (lastTxId != txId) {
-            matchCount = 0;
+            matchedAccountIds.clear();
             lastTxId = txId;
         }
         splitAccoountId = idx.data(eMyMoney::Model::SplitAccountIdRole).toString();
-        if (splitAccoountId == acc.id())
-            ++matchCount;
-        else if (splitAccoountId == openAcc.id())
-            ++matchCount;
+        if (splitAccoountId == acc.id() || (splitAccoountId == openAcc.id())) {
+            matchedAccountIds.insert(splitAccoountId);
+        }
 
         // if we found both accounts in a transaction we have a match
-        if (matchCount == 2) {
+        if (matchedAccountIds.count() == 2) {
             return txId;
         }
     }
     // no opening balance transaction found
-    return QString();
+    return {};
 }
 
 MyMoneyAccount MyMoneyFile::openingBalanceAccount(const MyMoneySecurity& security)
diff --git a/kmymoney/mymoney/tests/test-mymoneyfile.cpp b/kmymoney/mymoney/tests/test-mymoneyfile.cpp
index 109f83dcd..9b9b711fe 100644
--- a/kmymoney/mymoney/tests/test-mymoneyfile.cpp
+++ b/kmymoney/mymoney/tests/test-mymoneyfile.cpp
@@ -495,6 +495,7 @@ void MyMoneyFileTest::testAddAccounts()
         QCOMPARE(a.id(), QLatin1String("A000001"));
         QCOMPARE(a.institutionId(), QLatin1String("I000001"));
         QCOMPARE(a.currencyId(), QLatin1String("EUR"));
+        QCOMPARE(a.openingDate(), QDate::currentDate());
         QCOMPARE(m->dirty(), true);
         QCOMPARE(m->asset().accountList().count(), 1);
         QCOMPARE(m->asset().accountList().at(0), QLatin1String("A000001"));
@@ -1889,6 +1890,58 @@ void MyMoneyFileTest::testOpeningBalance()
     }
 }
 
+void MyMoneyFileTest::testOpeningBalanceMultipleSplits()
+{
+    testAddAccounts();
+
+    MyMoneyAccount openingAcc;
+
+    try {
+        openingAcc = m->openingBalanceAccount(m->baseCurrency());
+        QCOMPARE(openingAcc.parentAccountId(), m->equity().id());
+        QCOMPARE(openingAcc.name(), MyMoneyFile::openingBalancesPrefix());
+        QCOMPARE(openingAcc.openingDate(), QDate::currentDate());
+    } catch (const MyMoneyException& e) {
+        unexpectedException(e);
+    }
+
+    QDate postDate = QDate::currentDate().addDays(15);
+
+    MyMoneyFileTransaction ft;
+    MyMoneyTransaction t;
+
+    // construct a transaction at the day of the last transaction import and add it to the pool
+    t.setPostDate(postDate);
+
+    const auto firstAssetAccountId("A000001");
+    const auto secondAssetAccountId("A000002");
+
+    MyMoneySplit split1;
+
+    split1.setAccountId(firstAssetAccountId);
+    split1.setShares(MyMoneyMoney(-1000, 100));
+    split1.setValue(MyMoneyMoney(-1000, 100));
+    t.addSplit(split1);
+
+    MyMoneySplit split2;
+    split2.setAccountId(secondAssetAccountId);
+    split2.setShares(MyMoneyMoney(500, 100));
+    split2.setValue(MyMoneyMoney(500, 100));
+    for (int i = 0; i < 2; ++i) {
+        split2.clearId();
+        t.addSplit(split2);
+    }
+
+    m->addTransaction(t);
+    ft.commit();
+
+    const auto firstAssetAccount = m->account(firstAssetAccountId);
+    const auto secondAssetAccount = m->account(secondAssetAccountId);
+
+    QCOMPARE(m->openingBalanceTransaction(firstAssetAccount), QString());
+    QCOMPARE(m->openingBalanceTransaction(secondAssetAccount), QString());
+}
+
 void MyMoneyFileTest::testModifyStdAccount()
 {
     QVERIFY(m->asset().currencyId().isEmpty());
diff --git a/kmymoney/mymoney/tests/test-mymoneyfile.h b/kmymoney/mymoney/tests/test-mymoneyfile.h
index 8ad6d4fe9..57db4f91c 100644
--- a/kmymoney/mymoney/tests/test-mymoneyfile.h
+++ b/kmymoney/mymoney/tests/test-mymoneyfile.h
@@ -68,6 +68,7 @@ private Q_SLOTS:
     void testBaseCurrency();
     void testOpeningBalanceNoBase();
     void testOpeningBalance();
+    void testOpeningBalanceMultipleSplits();
     void testAddPrice();
     void testRemovePrice();
     void testGetPrice();
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.