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

Thomas Baumgart <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 560678371f6bca10c1a898fac66cb0c26485f3ee by Thomas Baumgart.
Committed on 28/07/2026 at 06:06.
Pushed by tbaumgart into branch '5.2'.

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
(cherry picked from commit 3bbedfea0b90a367e0bb54edb7af801f845e98ed)

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

https://invent.kde.org/office/kmymoney/-/commit/560678371f6bca10c1a898fac66cb0c26485f3ee

diff --git a/kmymoney/mymoney/mymoneyfile.cpp b/kmymoney/mymoney/mymoneyfile.cpp
index 7c8f43c5f..d8d195292 100644
--- a/kmymoney/mymoney/mymoneyfile.cpp
+++ b/kmymoney/mymoney/mymoneyfile.cpp
@@ -2233,32 +2233,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/mymoneyfile-test.cpp b/kmymoney/mymoney/tests/mymoneyfile-test.cpp
index 35f7bccc2..b67c8b92b 100644
--- a/kmymoney/mymoney/tests/mymoneyfile-test.cpp
+++ b/kmymoney/mymoney/tests/mymoneyfile-test.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/mymoneyfile-test.h b/kmymoney/mymoney/tests/mymoneyfile-test.h
index b1bf209b8..63ccfb6f0 100644
--- a/kmymoney/mymoney/tests/mymoneyfile-test.h
+++ b/kmymoney/mymoney/tests/mymoneyfile-test.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.