gnucash stable: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Mon Jun 30 10:48:44 EDT 2025


Updated	 via  https://github.com/Gnucash/gnucash/commit/aa339b46 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01366f42 (commit)
	from  https://github.com/Gnucash/gnucash/commit/eeedd7bf (commit)



commit aa339b4674a273d6f62485bb1a82b3d5c2e3c9a8
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jun 22 09:26:36 2025 +0800

    [Transaction.cpp] use g_list_copy_deep

diff --git a/libgnucash/engine/Transaction.cpp b/libgnucash/engine/Transaction.cpp
index 7379ea9a35..830dd4f6e7 100644
--- a/libgnucash/engine/Transaction.cpp
+++ b/libgnucash/engine/Transaction.cpp
@@ -595,19 +595,12 @@ static Transaction *
 dupe_trans (const Transaction *from)
 {
     Transaction *to;
-    GList *node;
-
     to = GNC_TRANSACTION(g_object_new (GNC_TYPE_TRANSACTION, nullptr));
 
     CACHE_REPLACE (to->num, from->num);
     CACHE_REPLACE (to->description, from->description);
 
-    to->splits = g_list_copy (from->splits);
-    for (node = to->splits; node; node = node->next)
-    {
-        node->data = xaccDupeSplit (GNC_SPLIT(node->data));
-    }
-
+    to->splits = g_list_copy_deep (from->splits, (GCopyFunc)xaccDupeSplit, nullptr);
     to->date_entered = from->date_entered;
     to->date_posted = from->date_posted;
     qof_instance_copy_version(to, from);

commit 01366f425fa2ebe70152de7bd994558699e028fa
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Jun 28 23:09:22 2025 +0800

    [gtest-load-and-test-datafile.cpp] add example .gnucash to test contents
    
    seed xml datafile to test stability of XML serialization routines

diff --git a/libgnucash/backend/xml/test/CMakeLists.txt b/libgnucash/backend/xml/test/CMakeLists.txt
index 81b5e72ea5..0e711a1c4a 100644
--- a/libgnucash/backend/xml/test/CMakeLists.txt
+++ b/libgnucash/backend/xml/test/CMakeLists.txt
@@ -38,6 +38,7 @@ set_local_dist(test_backend_xml_DIST_local
   test-load-backend.cpp
   test-load-example-account.cpp
   gtest-load-save-files.cpp
+  gtest-xml-contents.cpp
   test-load-xml2.cpp
   test-real-data.sh
   test-save-in-lang.cpp
@@ -69,6 +70,10 @@ target_compile_options(test-load-example-account PRIVATE -DU_SHOW_CPLUSPLUS_API=
 add_xml_gtest(test-load-save-files gtest-load-save-files.cpp
   GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/load-save
 )
+add_xml_gtest(test-xml-contents gtest-xml-contents.cpp
+  GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/xml-contents
+)
+
 add_xml_test(test-string-converters "test-string-converters.cpp")
 add_xml_test(test-xml-account "test-xml-account.cpp;test-file-stuff.cpp")
 add_xml_test(test-xml-commodity "test-xml-commodity.cpp;test-file-stuff.cpp")
diff --git a/libgnucash/backend/xml/test/gtest-xml-contents.cpp b/libgnucash/backend/xml/test/gtest-xml-contents.cpp
new file mode 100644
index 0000000000..e11204ad1d
--- /dev/null
+++ b/libgnucash/backend/xml/test/gtest-xml-contents.cpp
@@ -0,0 +1,204 @@
+/********************************************************************\
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+#include <glib.h>
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <cashobjects.h>
+#include <TransLog.h>
+#include <gnc-engine.h>
+#include <gnc-lot.h>
+#include <gnc-prefs.h>
+#include <Account.hpp>
+#include <gnc-datetime.hpp>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
+#include <gtest/gtest.h>
+#pragma GCC diagnostic pop
+#include <unittest-support.h>
+
+#include "../gnc-backend-xml.h"
+#include "../io-gncxml-v2.h"
+
+#define GNC_LIB_NAME "gncmod-backend-xml"
+#define GNC_LIB_REL_PATH "xml"
+
+class LoadFile : public testing::Test
+{
+public:
+    static void SetUpTestSuite ()
+    {
+        g_setenv ("GNC_UNINSTALLED", "1", TRUE);
+        qof_init ();
+        cashobjects_register ();
+        ASSERT_TRUE(qof_load_backend_library (GNC_LIB_REL_PATH, GNC_LIB_NAME)) << "loading gnc-backend-xml GModule failed";
+        xaccLogDisable ();
+    }
+
+    static void TearDownTestSuite () { qof_close (); }
+};
+
+static QofBook*
+session_load (QofSession* session, const char* filename)
+{
+    if (!session || !filename) return nullptr;
+
+    qof_session_begin (session, filename, SESSION_READ_ONLY);
+    if (qof_session_get_error(session) != 0)
+    {
+        std::cerr << "Session begin failed: " << qof_session_get_error_message(session);
+        return nullptr;
+    }
+
+    qof_session_load(session, nullptr);
+    if (qof_session_get_error(session) != 0)
+    {
+        std::cerr << "Session begin failed: " << qof_session_get_error_message(session);
+        return nullptr;
+    }
+
+    return qof_session_get_book(session);
+}
+
+TEST_F(LoadFile, LoadAndVerifyKVP)
+{
+    auto location{g_getenv ("GNC_TEST_FILES")};
+    if (!location)
+        location = "test-files/load-save";
+    auto filename{"xml-datafile.gnucash"};
+    std::shared_ptr<gchar> to_open{g_build_filename (location, filename, (gchar*)nullptr), g_free};
+    std::shared_ptr<QofSession> session{qof_session_new(qof_book_new()), qof_session_destroy};
+    auto book{session_load (session.get(), to_open.get())};
+    ASSERT_NE(book, nullptr);
+
+    auto bank_acct = gnc_account_lookup_by_name (gnc_book_get_root_account (book), "Bank");
+    ASSERT_TRUE (bank_acct != nullptr);
+
+    // THE FOLLOWING TESTS BANK ACCOUNT
+    const auto& bank_splitlist = xaccAccountGetSplits (bank_acct);
+    ASSERT_EQ (bank_splitlist.size(), static_cast<uint>(4));
+
+    // first split is from a regular transaction
+    auto bank_reg_split{bank_splitlist[0]};
+    auto bank_reg_txn{xaccSplitGetParent(bank_reg_split)};
+    EXPECT_STREQ (xaccTransGetDescription (bank_reg_txn), "income");
+    EXPECT_STREQ (xaccTransGetNotes (bank_reg_txn), "notes");
+    EXPECT_STREQ (xaccTransGetNum (bank_reg_txn), "num");
+    EXPECT_STREQ (xaccTransGetDocLink (bank_reg_txn), "https://www.gnucash.org/");
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetAmount (bank_reg_split), gnc_numeric_create (200, 1)));
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetValue (bank_reg_split), gnc_numeric_create (200, 1)));
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(bank_reg_txn)).format_iso8601(), "2025-01-01 10:59:00");
+    EXPECT_EQ (xaccTransGetTxnType (bank_reg_txn), TXN_TYPE_NONE);
+    EXPECT_FALSE (xaccTransGetIsClosingTxn (bank_reg_txn));
+    EXPECT_STREQ (xaccTransGetReadOnly (bank_reg_txn), nullptr);
+
+    // 2nd split is a payment split
+    auto bank_pmt_split{bank_splitlist[1]};
+    auto bank_pmt_txn{xaccSplitGetParent(bank_pmt_split)};
+    EXPECT_STREQ (xaccTransGetDescription (bank_pmt_txn), "customer-name");
+    EXPECT_STREQ (xaccTransGetNotes (bank_pmt_txn), NULL);
+    EXPECT_STREQ (xaccTransGetNum (bank_pmt_txn), "pmt-num");
+    EXPECT_STREQ (xaccTransGetDocLink (bank_pmt_txn), nullptr);
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(bank_pmt_txn)).format_iso8601(), "2025-02-12 10:59:00");
+    EXPECT_EQ (xaccTransGetTxnType (bank_pmt_txn), TXN_TYPE_PAYMENT);
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetAmount (bank_pmt_split), gnc_numeric_create (194, 100)));
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetValue (bank_pmt_split), gnc_numeric_create (194, 100)));
+    EXPECT_STREQ (xaccTransGetReadOnly (bank_pmt_txn), nullptr);
+    EXPECT_STREQ (xaccTransGetVoidReason (bank_pmt_txn), nullptr);
+
+    // 3nd split is a voided split
+    auto bank_voided_split{bank_splitlist[2]};
+    auto bank_voided_txn{xaccSplitGetParent(bank_voided_split)};
+    EXPECT_STREQ (xaccTransGetDescription (bank_voided_txn), "another income voided");
+    EXPECT_STREQ (xaccTransGetNotes (bank_voided_txn), "Voided transaction");
+    EXPECT_STREQ (xaccTransGetNum (bank_voided_txn), "");
+    EXPECT_STREQ (xaccTransGetDocLink (bank_voided_txn), nullptr);
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(bank_voided_txn)).format_iso8601(), "2025-03-01 10:59:00");
+    EXPECT_EQ (xaccTransGetTxnType (bank_voided_txn), TXN_TYPE_NONE);
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetAmount (bank_voided_split), gnc_numeric_create (0, 100)));
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetValue (bank_voided_split), gnc_numeric_create (0, 100)));
+    EXPECT_STREQ (xaccTransGetReadOnly (bank_voided_txn), "Transaction Voided");
+    EXPECT_STREQ (xaccTransGetVoidReason (bank_voided_txn), "cancelled");
+
+    // 4th split is the 2nd payment reversal txn, reversed
+    auto bank_pmt_rev_split{bank_splitlist[3]};
+    auto bank_pmt_rev_txn{xaccSplitGetParent(bank_pmt_rev_split)};
+    EXPECT_STREQ (xaccTransGetDescription (bank_pmt_rev_txn), "customer-name");
+    EXPECT_STREQ (xaccTransGetNotes (bank_pmt_rev_txn), nullptr);
+    EXPECT_STREQ (xaccTransGetNum (bank_pmt_rev_txn), "pmt-num");
+    EXPECT_STREQ (xaccTransGetDocLink (bank_pmt_rev_txn), nullptr);
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(bank_pmt_rev_txn)).format_iso8601(), "2025-04-01 10:59:00");
+    EXPECT_EQ (xaccTransGetTxnType (bank_pmt_rev_txn), TXN_TYPE_PAYMENT);
+    EXPECT_EQ (xaccTransGetReversedBy (bank_pmt_txn), bank_pmt_rev_txn);
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetAmount (bank_pmt_rev_split), gnc_numeric_create (-194, 100)));
+    EXPECT_TRUE (gnc_numeric_equal (xaccSplitGetValue (bank_pmt_rev_split), gnc_numeric_create (-194, 100)));
+    EXPECT_STREQ (xaccTransGetReadOnly (bank_pmt_rev_txn), nullptr);
+    EXPECT_STREQ (xaccTransGetVoidReason (bank_pmt_rev_txn), nullptr);
+
+    // THE FOLLOWING TESTS AR ACCOUNT
+    auto AR_acct{gnc_account_lookup_by_name (gnc_book_get_root_account (book), "AReceivable")};
+    ASSERT_TRUE (AR_acct != nullptr);
+
+    const auto& AR_splitlist{xaccAccountGetSplits (AR_acct)};
+    ASSERT_EQ (AR_splitlist.size(), static_cast<uint>(3));
+
+    // 1st split is invoice posting txn
+    auto inv_post_txn{xaccSplitGetParent(AR_splitlist[0])};
+    EXPECT_STREQ (xaccTransGetDescription (inv_post_txn), "customer-name");
+    EXPECT_STREQ (xaccTransGetNotes (inv_post_txn), nullptr);
+    EXPECT_STREQ (xaccTransGetNum (inv_post_txn), "000001");
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(inv_post_txn)).format_iso8601(), "2025-02-01 10:59:00");
+    EXPECT_EQ (xaccTransGetTxnType (inv_post_txn), TXN_TYPE_INVOICE);
+    EXPECT_EQ (GncDateTime(xaccTransRetDateDue(inv_post_txn)).format_iso8601(), "2025-02-14 10:59:00");
+    EXPECT_FALSE (xaccTransGetIsClosingTxn (inv_post_txn));
+    EXPECT_STREQ (xaccTransGetReadOnly (inv_post_txn), "Generated from an invoice. Try unposting the invoice.");
+
+    // test invoice lot properties
+    auto inv_lot{xaccSplitGetLot (AR_splitlist[0])};
+    EXPECT_STREQ (gnc_lot_get_title (inv_lot), "Invoice 000001");
+    EXPECT_STREQ (gnc_lot_get_notes (inv_lot), nullptr);
+
+    // 2nd split is from payment txn, should be identical to bank payment txn
+    auto inv_pmt_txn{xaccSplitGetParent(AR_splitlist[1])};
+    EXPECT_EQ (inv_pmt_txn, bank_pmt_txn);
+
+    // THE FOLLOWING TESTS CLOSING ACCOUNT
+    auto close_acct{gnc_account_lookup_by_name (gnc_book_get_root_account (book), "Closing")};
+    ASSERT_TRUE (close_acct != nullptr);
+
+    const auto& close_splitlist{xaccAccountGetSplits (close_acct)};
+    ASSERT_EQ (close_splitlist.size(), static_cast<uint>(1));
+
+    // 3rd split is a closing txn
+    auto closing_txn{xaccSplitGetParent(close_splitlist[0])};
+    EXPECT_STREQ (xaccTransGetDescription (closing_txn), "Closing Txn");
+    EXPECT_STREQ (xaccTransGetNotes (closing_txn), nullptr);
+    EXPECT_STREQ (xaccTransGetNum (closing_txn), "");
+    EXPECT_EQ (xaccTransGetTxnType (closing_txn), TXN_TYPE_NONE);
+    EXPECT_EQ (GncDateTime(xaccTransGetDate(closing_txn)).format_iso8601(), "2025-03-01 10:59:00");
+    EXPECT_EQ (GncDateTime(xaccTransRetDateDue(closing_txn)).format_iso8601(), "2025-03-01 10:59:00");
+    EXPECT_TRUE (xaccTransGetIsClosingTxn (closing_txn));
+    EXPECT_STREQ (xaccTransGetReadOnly (closing_txn), nullptr);
+
+    qof_session_end(session.get());
+}
diff --git a/libgnucash/backend/xml/test/test-files/CMakeLists.txt b/libgnucash/backend/xml/test/test-files/CMakeLists.txt
index 126fc36391..269a9e5b90 100644
--- a/libgnucash/backend/xml/test/test-files/CMakeLists.txt
+++ b/libgnucash/backend/xml/test/test-files/CMakeLists.txt
@@ -1,6 +1,9 @@
 
 add_subdirectory(load-save)
+add_subdirectory(xml-contents)
 add_subdirectory(xml2)
 
 set_local_dist(test_backend_xml_test_files_DIST_local CMakeLists.txt )
-set(test_backend_xml_test_files_DIST ${test_backend_xml_test_files_DIST_local} ${load_save_DIST} ${xml2_DIST} PARENT_SCOPE)
+set(test_backend_xml_test_files_DIST ${test_backend_xml_test_files_DIST_local} ${load_save_DIST} ${xml2_DIST}
+    ${xml_contents_DIST}
+    PARENT_SCOPE)
diff --git a/libgnucash/backend/xml/test/test-files/xml-contents/CMakeLists.txt b/libgnucash/backend/xml/test/test-files/xml-contents/CMakeLists.txt
new file mode 100644
index 0000000000..6419e75608
--- /dev/null
+++ b/libgnucash/backend/xml/test/test-files/xml-contents/CMakeLists.txt
@@ -0,0 +1,4 @@
+set_dist_list(xml_contents_DIST
+  xml-datafile.gnucash
+  CMakeLists.txt
+)
diff --git a/libgnucash/backend/xml/test/test-files/xml-contents/xml-datafile.gnucash b/libgnucash/backend/xml/test/test-files/xml-contents/xml-datafile.gnucash
new file mode 100644
index 0000000000..2e3863c765
--- /dev/null
+++ b/libgnucash/backend/xml/test/test-files/xml-contents/xml-datafile.gnucash
@@ -0,0 +1,595 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<gnc-v2
+     xmlns:gnc="http://www.gnucash.org/XML/gnc"
+     xmlns:act="http://www.gnucash.org/XML/act"
+     xmlns:book="http://www.gnucash.org/XML/book"
+     xmlns:cd="http://www.gnucash.org/XML/cd"
+     xmlns:cmdty="http://www.gnucash.org/XML/cmdty"
+     xmlns:price="http://www.gnucash.org/XML/price"
+     xmlns:slot="http://www.gnucash.org/XML/slot"
+     xmlns:split="http://www.gnucash.org/XML/split"
+     xmlns:sx="http://www.gnucash.org/XML/sx"
+     xmlns:trn="http://www.gnucash.org/XML/trn"
+     xmlns:ts="http://www.gnucash.org/XML/ts"
+     xmlns:fs="http://www.gnucash.org/XML/fs"
+     xmlns:bgt="http://www.gnucash.org/XML/bgt"
+     xmlns:recurrence="http://www.gnucash.org/XML/recurrence"
+     xmlns:lot="http://www.gnucash.org/XML/lot"
+     xmlns:addr="http://www.gnucash.org/XML/addr"
+     xmlns:billterm="http://www.gnucash.org/XML/billterm"
+     xmlns:bt-days="http://www.gnucash.org/XML/bt-days"
+     xmlns:bt-prox="http://www.gnucash.org/XML/bt-prox"
+     xmlns:cust="http://www.gnucash.org/XML/cust"
+     xmlns:employee="http://www.gnucash.org/XML/employee"
+     xmlns:entry="http://www.gnucash.org/XML/entry"
+     xmlns:invoice="http://www.gnucash.org/XML/invoice"
+     xmlns:job="http://www.gnucash.org/XML/job"
+     xmlns:order="http://www.gnucash.org/XML/order"
+     xmlns:owner="http://www.gnucash.org/XML/owner"
+     xmlns:taxtable="http://www.gnucash.org/XML/taxtable"
+     xmlns:tte="http://www.gnucash.org/XML/tte"
+     xmlns:vendor="http://www.gnucash.org/XML/vendor">
+<gnc:count-data cd:type="book">1</gnc:count-data>
+<gnc:book version="2.0.0">
+<book:id type="guid">f70b5dc6711143939c0dd7ae25e2e773</book:id>
+<book:slots>
+  <slot>
+    <slot:key>counters</slot:key>
+    <slot:value type="frame">
+      <slot>
+        <slot:key>gncCustomer</slot:key>
+        <slot:value type="integer">1</slot:value>
+      </slot>
+      <slot>
+        <slot:key>gncInvoice</slot:key>
+        <slot:value type="integer">1</slot:value>
+      </slot>
+    </slot:value>
+  </slot>
+  <slot>
+    <slot:key>features</slot:key>
+    <slot:value type="frame">
+      <slot>
+        <slot:key>Register sort and filter settings stored in .gcm file</slot:key>
+        <slot:value type="string">Store the register sort and filter settings in .gcm metadata file (requires at least GnuCash 3.3)</slot:value>
+      </slot>
+      <slot>
+        <slot:key>Use a dedicated opening balance account identified by an 'equity-type' slot</slot:key>
+        <slot:value type="string">Use a dedicated opening balance account identified by an 'equity-type' slot (requires at least Gnucash 4.3)</slot:value>
+      </slot>
+    </slot:value>
+  </slot>
+  <slot>
+    <slot:key>remove-color-not-set-slots</slot:key>
+    <slot:value type="string">true</slot:value>
+  </slot>
+</book:slots>
+<gnc:count-data cd:type="commodity">1</gnc:count-data>
+<gnc:count-data cd:type="account">5</gnc:count-data>
+<gnc:count-data cd:type="transaction">6</gnc:count-data>
+<gnc:count-data cd:type="gnc:GncCustomer">1</gnc:count-data>
+<gnc:count-data cd:type="gnc:GncEntry">1</gnc:count-data>
+<gnc:count-data cd:type="gnc:GncInvoice">1</gnc:count-data>
+<gnc:commodity version="2.0.0">
+  <cmdty:space>CURRENCY</cmdty:space>
+  <cmdty:id>AUD</cmdty:id>
+  <cmdty:get_quotes/>
+  <cmdty:quote_source>currency</cmdty:quote_source>
+  <cmdty:quote_tz/>
+</gnc:commodity>
+<gnc:commodity version="2.0.0">
+  <cmdty:space>template</cmdty:space>
+  <cmdty:id>template</cmdty:id>
+  <cmdty:name>template</cmdty:name>
+  <cmdty:xcode>template</cmdty:xcode>
+  <cmdty:fraction>1</cmdty:fraction>
+</gnc:commodity>
+<gnc:account version="2.0.0">
+  <act:name>Root Account</act:name>
+  <act:id type="guid">ea1d654fedbf4d4c88be7df1b3e8be2a</act:id>
+  <act:type>ROOT</act:type>
+</gnc:account>
+<gnc:account version="2.0.0">
+  <act:name>Bank</act:name>
+  <act:id type="guid">0d0c14d1661b46819c18319c21dc2666</act:id>
+  <act:type>BANK</act:type>
+  <act:commodity>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </act:commodity>
+  <act:commodity-scu>100</act:commodity-scu>
+  <act:slots>
+    <slot>
+      <slot:key>balance-limit</slot:key>
+      <slot:value type="frame"/>
+    </slot>
+  </act:slots>
+  <act:parent type="guid">ea1d654fedbf4d4c88be7df1b3e8be2a</act:parent>
+</gnc:account>
+<gnc:account version="2.0.0">
+  <act:name>Income</act:name>
+  <act:id type="guid">5fb86abd10e94abba7463655767a5e63</act:id>
+  <act:type>INCOME</act:type>
+  <act:commodity>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </act:commodity>
+  <act:commodity-scu>100</act:commodity-scu>
+  <act:slots>
+    <slot>
+      <slot:key>balance-limit</slot:key>
+      <slot:value type="frame"/>
+    </slot>
+  </act:slots>
+  <act:parent type="guid">ea1d654fedbf4d4c88be7df1b3e8be2a</act:parent>
+</gnc:account>
+<gnc:account version="2.0.0">
+  <act:name>AReceivable</act:name>
+  <act:id type="guid">0656224544884e8e8196f23af8e67a1e</act:id>
+  <act:type>RECEIVABLE</act:type>
+  <act:commodity>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </act:commodity>
+  <act:commodity-scu>100</act:commodity-scu>
+  <act:slots>
+    <slot>
+      <slot:key>balance-limit</slot:key>
+      <slot:value type="frame"/>
+    </slot>
+  </act:slots>
+  <act:parent type="guid">ea1d654fedbf4d4c88be7df1b3e8be2a</act:parent>
+  <act:lots>
+    <gnc:lot version="2.0.0">
+      <lot:id type="guid">73ab1ddf8d4943799c6705e1813ae582</lot:id>
+      <lot:slots>
+        <slot>
+          <slot:key>gncInvoice</slot:key>
+          <slot:value type="frame">
+            <slot>
+              <slot:key>invoice-guid</slot:key>
+              <slot:value type="guid">2d4e6dbfba6949abb6e4e1b0fe0ad54a</slot:value>
+            </slot>
+          </slot:value>
+        </slot>
+        <slot>
+          <slot:key>title</slot:key>
+          <slot:value type="string">Invoice 000001</slot:value>
+        </slot>
+      </lot:slots>
+    </gnc:lot>
+  </act:lots>
+</gnc:account>
+<gnc:account version="2.0.0">
+  <act:name>Closing</act:name>
+  <act:id type="guid">5ec7e02aafcf4e438cab06530ebaffd2</act:id>
+  <act:type>EQUITY</act:type>
+  <act:commodity>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </act:commodity>
+  <act:commodity-scu>100</act:commodity-scu>
+  <act:slots>
+    <slot>
+      <slot:key>balance-limit</slot:key>
+      <slot:value type="frame"/>
+    </slot>
+  </act:slots>
+  <act:parent type="guid">ea1d654fedbf4d4c88be7df1b3e8be2a</act:parent>
+</gnc:account>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">e0f0c58c19d9460a908e4f76e065cf50</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:num>num</trn:num>
+  <trn:date-posted>
+    <ts:date>2025-01-01 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-28 13:27:50 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>income</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>assoc_uri</slot:key>
+      <slot:value type="string">https://www.gnucash.org/</slot:value>
+    </slot>
+    <slot>
+      <slot:key>date-posted</slot:key>
+      <slot:value type="gdate">
+        <gdate>2025-01-01</gdate>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>notes</slot:key>
+      <slot:value type="string">notes</slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">1a3f6ea60fb6496183ee6e4fab152c51</split:id>
+      <split:action>Action</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>20000/100</split:value>
+      <split:quantity>20000/100</split:quantity>
+      <split:account type="guid">0d0c14d1661b46819c18319c21dc2666</split:account>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">ccb98b52387a4e17a84ffad4e836e108</split:id>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>-20000/100</split:value>
+      <split:quantity>-20000/100</split:quantity>
+      <split:account type="guid">5fb86abd10e94abba7463655767a5e63</split:account>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">31f65079a2f449cab30f17ff5a88c4e1</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:num>pmt-num</trn:num>
+  <trn:date-posted>
+    <ts:date>2025-02-12 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-28 15:05:43 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>customer-name</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>reversed-by</slot:key>
+      <slot:value type="guid">5843eb8d60d24e589eee7fca998c1e83</slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-txn-type</slot:key>
+      <slot:value type="string">P</slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">2d8a86ad48314d4488a3150d34a680c5</split:id>
+      <split:memo>pmt-memo</split:memo>
+      <split:action>Payment</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>194/100</split:value>
+      <split:quantity>194/100</split:quantity>
+      <split:account type="guid">0d0c14d1661b46819c18319c21dc2666</split:account>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">7800a00db0b94ef2aaa95039f3d27934</split:id>
+      <split:memo>pmt-memo</split:memo>
+      <split:action>Payment</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>-194/100</split:value>
+      <split:quantity>-194/100</split:quantity>
+      <split:account type="guid">0656224544884e8e8196f23af8e67a1e</split:account>
+      <split:lot type="guid">73ab1ddf8d4943799c6705e1813ae582</split:lot>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">a74a070640fe471dbef879ee55a63fa4</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:date-posted>
+    <ts:date>2025-03-01 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-29 15:32:13 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>another income voided</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>date-posted</slot:key>
+      <slot:value type="gdate">
+        <gdate>2025-03-01</gdate>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>notes</slot:key>
+      <slot:value type="string">Voided transaction</slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-read-only</slot:key>
+      <slot:value type="string">Transaction Voided</slot:value>
+    </slot>
+    <slot>
+      <slot:key>void-reason</slot:key>
+      <slot:value type="string">cancelled</slot:value>
+    </slot>
+    <slot>
+      <slot:key>void-time</slot:key>
+      <slot:value type="string">2025-06-29 15:32:22</slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">77cc7eb8b6f142cb94518d94aa6f234f</split:id>
+      <split:reconciled-state>v</split:reconciled-state>
+      <split:value>0/100</split:value>
+      <split:quantity>0/100</split:quantity>
+      <split:account type="guid">0d0c14d1661b46819c18319c21dc2666</split:account>
+      <split:slots>
+        <slot>
+          <slot:key>void-former-amount</slot:key>
+          <slot:value type="numeric">3456/100</slot:value>
+        </slot>
+        <slot>
+          <slot:key>void-former-value</slot:key>
+          <slot:value type="numeric">3456/100</slot:value>
+        </slot>
+      </split:slots>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">a0d3f31ab7954c8a8870347abdc0d787</split:id>
+      <split:reconciled-state>v</split:reconciled-state>
+      <split:value>0/100</split:value>
+      <split:quantity>0/100</split:quantity>
+      <split:account type="guid">5fb86abd10e94abba7463655767a5e63</split:account>
+      <split:slots>
+        <slot>
+          <slot:key>void-former-amount</slot:key>
+          <slot:value type="numeric">-3456/100</slot:value>
+        </slot>
+        <slot>
+          <slot:key>void-former-value</slot:key>
+          <slot:value type="numeric">-3456/100</slot:value>
+        </slot>
+      </split:slots>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">5843eb8d60d24e589eee7fca998c1e83</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:num>pmt-num</trn:num>
+  <trn:date-posted>
+    <ts:date>2025-04-01 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-29 15:41:00 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>customer-name</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>date-posted</slot:key>
+      <slot:value type="gdate">
+        <gdate>2025-04-01</gdate>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-txn-type</slot:key>
+      <slot:value type="string">P</slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">120d972af0c14e2e8848bcff6a156363</split:id>
+      <split:memo>pmt-memo</split:memo>
+      <split:action>Payment</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>194/100</split:value>
+      <split:quantity>194/100</split:quantity>
+      <split:account type="guid">0656224544884e8e8196f23af8e67a1e</split:account>
+      <split:lot type="guid">73ab1ddf8d4943799c6705e1813ae582</split:lot>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">4c7d0e0a00cc43dbad481d357c76db46</split:id>
+      <split:memo>pmt-memo</split:memo>
+      <split:action>Payment</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>-194/100</split:value>
+      <split:quantity>-194/100</split:quantity>
+      <split:account type="guid">0d0c14d1661b46819c18319c21dc2666</split:account>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">372e0950eebc4bc0982768f6ad920c99</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:num>000001</trn:num>
+  <trn:date-posted>
+    <ts:date>2025-02-01 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-28 15:03:49 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>customer-name</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>date-posted</slot:key>
+      <slot:value type="gdate">
+        <gdate>2025-02-01</gdate>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>gncInvoice</slot:key>
+      <slot:value type="frame">
+        <slot>
+          <slot:key>invoice-guid</slot:key>
+          <slot:value type="guid">2d4e6dbfba6949abb6e4e1b0fe0ad54a</slot:value>
+        </slot>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-date-due</slot:key>
+      <slot:value type="timespec">
+        <ts:date>2025-02-14 10:59:00 +0000</ts:date>
+      </slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-read-only</slot:key>
+      <slot:value type="string">Generated from an invoice. Try unposting the invoice.</slot:value>
+    </slot>
+    <slot>
+      <slot:key>trans-txn-type</slot:key>
+      <slot:value type="string">I</slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">a48caa6921c14aaa8e304649702cf195</split:id>
+      <split:memo>inv-desc</split:memo>
+      <split:action>Invoice</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>194/100</split:value>
+      <split:quantity>194/100</split:quantity>
+      <split:account type="guid">0656224544884e8e8196f23af8e67a1e</split:account>
+      <split:lot type="guid">73ab1ddf8d4943799c6705e1813ae582</split:lot>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">1746e3d2fc4a4561809803a36dbfefdc</split:id>
+      <split:memo>inv-desc</split:memo>
+      <split:action>Invoice</split:action>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>-194/100</split:value>
+      <split:quantity>-194/100</split:quantity>
+      <split:account type="guid">5fb86abd10e94abba7463655767a5e63</split:account>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:transaction version="2.0.0">
+  <trn:id type="guid">c145788050934e00be3e28b2a15901aa</trn:id>
+  <trn:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </trn:currency>
+  <trn:date-posted>
+    <ts:date>2025-03-01 10:59:00 +0000</ts:date>
+  </trn:date-posted>
+  <trn:date-entered>
+    <ts:date>2025-06-29 15:23:29 +0000</ts:date>
+  </trn:date-entered>
+  <trn:description>Closing Txn</trn:description>
+  <trn:slots>
+    <slot>
+      <slot:key>book_closing</slot:key>
+      <slot:value type="integer">1</slot:value>
+    </slot>
+    <slot>
+      <slot:key>date-posted</slot:key>
+      <slot:value type="gdate">
+        <gdate>2025-03-01</gdate>
+      </slot:value>
+    </slot>
+  </trn:slots>
+  <trn:splits>
+    <trn:split>
+      <split:id type="guid">583b1c39b0664eeebdf1165763defb91</split:id>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>20194/100</split:value>
+      <split:quantity>20194/100</split:quantity>
+      <split:account type="guid">5fb86abd10e94abba7463655767a5e63</split:account>
+    </trn:split>
+    <trn:split>
+      <split:id type="guid">e10d19734a1f435e8c191f7c830f68ff</split:id>
+      <split:reconciled-state>n</split:reconciled-state>
+      <split:value>-20194/100</split:value>
+      <split:quantity>-20194/100</split:quantity>
+      <split:account type="guid">5ec7e02aafcf4e438cab06530ebaffd2</split:account>
+    </trn:split>
+  </trn:splits>
+</gnc:transaction>
+<gnc:GncCustomer version="2.0.0">
+  <cust:guid type="guid">2e8b6716d9cb4d70b8b7bea13315f939</cust:guid>
+  <cust:name>customer-name</cust:name>
+  <cust:id>000001</cust:id>
+  <cust:addr version="2.0.0">
+    <addr:name>billing-name</addr:name>
+    <addr:addr1>addr1</addr:addr1>
+    <addr:addr2>addr2</addr:addr2>
+    <addr:addr3>addr3</addr:addr3>
+    <addr:addr4>addr4</addr:addr4>
+    <addr:phone>cust-phone</addr:phone>
+    <addr:fax>cust-fax</addr:fax>
+    <addr:email>cust-email</addr:email>
+  </cust:addr>
+  <cust:shipaddr version="2.0.0"/>
+  <cust:notes>cust-notes</cust:notes>
+  <cust:taxincluded>USEGLOBAL</cust:taxincluded>
+  <cust:active>1</cust:active>
+  <cust:discount>0/1</cust:discount>
+  <cust:credit>0/1</cust:credit>
+  <cust:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </cust:currency>
+  <cust:use-tt>0</cust:use-tt>
+  <cust:slots>
+    <slot>
+      <slot:key>last-posted-to-acct</slot:key>
+      <slot:value type="guid">0656224544884e8e8196f23af8e67a1e</slot:value>
+    </slot>
+    <slot>
+      <slot:key>payment</slot:key>
+      <slot:value type="frame">
+        <slot>
+          <slot:key>last_acct</slot:key>
+          <slot:value type="guid">0d0c14d1661b46819c18319c21dc2666</slot:value>
+        </slot>
+      </slot:value>
+    </slot>
+  </cust:slots>
+</gnc:GncCustomer>
+<gnc:GncEntry version="2.0.0">
+  <entry:guid type="guid">81bb7b390a6240b4bab220f1bed4ebbb</entry:guid>
+  <entry:date>
+    <ts:date>2025-02-01 04:00:00 +0000</ts:date>
+  </entry:date>
+  <entry:entered>
+    <ts:date>2025-06-28 15:03:18 +0000</ts:date>
+  </entry:entered>
+  <entry:description>entry-desc</entry:description>
+  <entry:qty>1/1</entry:qty>
+  <entry:i-acct type="guid">5fb86abd10e94abba7463655767a5e63</entry:i-acct>
+  <entry:i-price>2/1</entry:i-price>
+  <entry:i-discount>3/1</entry:i-discount>
+  <entry:invoice type="guid">2d4e6dbfba6949abb6e4e1b0fe0ad54a</entry:invoice>
+  <entry:i-disc-type>PERCENT</entry:i-disc-type>
+  <entry:i-disc-how>PRETAX</entry:i-disc-how>
+  <entry:i-taxable>0</entry:i-taxable>
+  <entry:i-taxincluded>0</entry:i-taxincluded>
+</gnc:GncEntry>
+<gnc:GncInvoice version="2.0.0">
+  <invoice:guid type="guid">2d4e6dbfba6949abb6e4e1b0fe0ad54a</invoice:guid>
+  <invoice:id>000001</invoice:id>
+  <invoice:owner version="2.0.0">
+    <owner:type>gncCustomer</owner:type>
+    <owner:id type="guid">2e8b6716d9cb4d70b8b7bea13315f939</owner:id>
+  </invoice:owner>
+  <invoice:opened>
+    <ts:date>2025-02-01 10:59:00 +0000</ts:date>
+  </invoice:opened>
+  <invoice:posted>
+    <ts:date>2025-02-01 10:59:00 +0000</ts:date>
+  </invoice:posted>
+  <invoice:active>1</invoice:active>
+  <invoice:posttxn type="guid">372e0950eebc4bc0982768f6ad920c99</invoice:posttxn>
+  <invoice:postlot type="guid">73ab1ddf8d4943799c6705e1813ae582</invoice:postlot>
+  <invoice:postacc type="guid">0656224544884e8e8196f23af8e67a1e</invoice:postacc>
+  <invoice:currency>
+    <cmdty:space>CURRENCY</cmdty:space>
+    <cmdty:id>AUD</cmdty:id>
+  </invoice:currency>
+  <invoice:slots>
+    <slot>
+      <slot:key>credit-note</slot:key>
+      <slot:value type="integer">0</slot:value>
+    </slot>
+  </invoice:slots>
+</gnc:GncInvoice>
+</gnc:book>
+</gnc-v2>
+



Summary of changes:
 libgnucash/backend/xml/test/CMakeLists.txt         |   5 +
 libgnucash/backend/xml/test/gtest-xml-contents.cpp | 204 +++++++
 .../backend/xml/test/test-files/CMakeLists.txt     |   5 +-
 .../test/test-files/xml-contents/CMakeLists.txt    |   4 +
 .../test-files/xml-contents/xml-datafile.gnucash   | 595 +++++++++++++++++++++
 libgnucash/engine/Transaction.cpp                  |   9 +-
 6 files changed, 813 insertions(+), 9 deletions(-)
 create mode 100644 libgnucash/backend/xml/test/gtest-xml-contents.cpp
 create mode 100644 libgnucash/backend/xml/test/test-files/xml-contents/CMakeLists.txt
 create mode 100644 libgnucash/backend/xml/test/test-files/xml-contents/xml-datafile.gnucash



More information about the gnucash-changes mailing list