gnucash master: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Thu Sep 2 10:17:42 EDT 2021


Updated	 via  https://github.com/Gnucash/gnucash/commit/8d502fcd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/881d3dcd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9b30a232 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac5650a9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1eb67093 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/323182c9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8a37c4c0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9ec670f8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d4c8b36f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c3b50f0b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/595e126a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c5a6383b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/86c438dc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/52715fac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bda03a1c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f97821e0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/22e64905 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1a049f4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1a12489e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5d15f95f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/998e14c1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0c21ae35 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e13644df (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8a48ed97 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/001b3461 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a47bee97 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0ecbcb4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac2afc7e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3bf49ed8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e3af2f22 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/57f73d70 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6cb509ea (commit)
	from  https://github.com/Gnucash/gnucash/commit/c6d83338 (commit)



commit 8d502fcdd808d0f16e35833bbb87e032a34a8740
Merge: c6d833381 881d3dcd2
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Sep 2 22:17:22 2021 +0800

    Merge branch 'maint'


commit 881d3dcd2358b6180a1434e5f493e53c5af98c7b
Author: TianXing_Yi <ytx.cash at gmail.com>
Date:   Tue Aug 31 16:33:43 2021 +0200

    Translation update  by TianXing_Yi <ytx.cash at gmail.com> using Weblate
    
    po/zh_CN.po: 99.7% (5353 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Chinese (Simplified))
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/zh_Hans/
    
    Translation update  by TianXing_Yi <ytx.cash at gmail.com> using Weblate
    
    po/zh_CN.po: 99.7% (5353 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Chinese (Simplified))
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/zh_Hans/
    
    Co-authored-by: TianXing_Yi <ytx.cash at gmail.com>

diff --git a/po/zh_CN.po b/po/zh_CN.po
index 25697da26..e3f477f3c 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -23,7 +23,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: https://bugs.gnucash.org/enter_bug."
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
-"PO-Revision-Date: 2021-08-29 06:34+0000\n"
+"PO-Revision-Date: 2021-08-31 14:33+0000\n"
 "Last-Translator: TianXing_Yi <ytx.cash at gmail.com>\n"
 "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
 "gnucash/gnucash/zh_Hans/>\n"
@@ -6797,27 +6797,27 @@ msgstr "选择要比较的科目"
 
 #: gnucash/gnome-search/search-date.c:196
 msgid "is before"
-msgstr "之前"
+msgstr "早于"
 
 #: gnucash/gnome-search/search-date.c:197
 msgid "is before or on"
-msgstr "先于或刚好"
+msgstr "早于或刚好"
 
 #: gnucash/gnome-search/search-date.c:198
 msgid "is on"
-msgstr "刚好"
+msgstr "在"
 
 #: gnucash/gnome-search/search-date.c:199
 msgid "is not on"
-msgstr "不刚好"
+msgstr "不在"
 
 #: gnucash/gnome-search/search-date.c:200
 msgid "is after"
-msgstr "之后"
+msgstr "晚于"
 
 #: gnucash/gnome-search/search-date.c:201
 msgid "is on or after"
-msgstr "刚好或后于"
+msgstr "刚好或晚于"
 
 #: gnucash/gnome-search/search-double.c:185
 #: gnucash/gnome-search/search-int64.c:187
@@ -6882,15 +6882,15 @@ msgstr "大于等于"
 
 #: gnucash/gnome-search/search-numeric.c:250
 msgid "has credits or debits"
-msgstr "有贷方或借方"
+msgstr "借方或贷方"
 
 #: gnucash/gnome-search/search-numeric.c:251
 msgid "has debits"
-msgstr "有借方"
+msgstr "借方"
 
 #: gnucash/gnome-search/search-numeric.c:252
 msgid "has credits"
-msgstr "有贷方"
+msgstr "è´·æ–¹"
 
 #: gnucash/gnome-search/search-reconciled.c:221
 msgid "Not Cleared"
@@ -16161,7 +16161,7 @@ msgstr "主窗格显示垂直网格线。"
 
 #: gnucash/gtkbuilder/dialog-preferences.glade:1957
 msgid "<b>Linked Files</b>"
-msgstr "<b>关联的凭证</b>"
+msgstr "<b>凭证路径</b>"
 
 #. Preferences->Online Banking:Generic
 #: gnucash/gtkbuilder/dialog-preferences.glade:2052

commit 9b30a2327bb129bd2c032937573db8b247113e34
Merge: ac5650a9a 86c438dcf
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 31 12:54:53 2021 -0700

    Merge Chris Good's 'MaintGenTrnImpMatFixCrash' into maint.


commit ac5650a9a2b7b91761cabfbf06a41b0aa300e9ce
Merge: bda03a1c5 1eb670933
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 23:30:24 2021 +0800

    Merge branch 'maint-g-value-leaks' into maint #1124


commit 1eb67093316519ad4e43d20ba7c87e4b92bf37d6
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 00:27:30 2021 +0800

    [test-lots.cpp] add kvp getter/setter changes

diff --git a/libgnucash/engine/test/test-lots.cpp b/libgnucash/engine/test/test-lots.cpp
index ee404432b..c02c7ab49 100644
--- a/libgnucash/engine/test/test-lots.cpp
+++ b/libgnucash/engine/test/test-lots.cpp
@@ -32,6 +32,7 @@ extern "C"
 #include <ctype.h>
 #include "qof.h"
 #include "Account.h"
+#include "gnc-lot.h"
 #include "Scrub3.h"
 #include "cashobjects.h"
 #include "test-stuff.h"
@@ -42,6 +43,48 @@ extern "C"
 static gint transaction_num = 32;
 static gint	max_iterate = 1;
 
+
+static void
+test_lot_kvp ()
+{
+    QofSession *sess = get_random_session ();
+    QofBook *book = qof_session_get_book (sess);
+    GNCLot *lot = gnc_lot_new (book);
+
+    // title
+    g_assert_cmpstr (gnc_lot_get_title (lot), ==, NULL);
+
+    gnc_lot_set_title (lot, "");
+    g_assert_cmpstr (gnc_lot_get_title (lot), ==, "");
+
+    gnc_lot_set_title (lot, "doc");
+    g_assert_cmpstr (gnc_lot_get_title (lot), ==, "doc");
+
+    gnc_lot_set_title (lot, "unset");
+    g_assert_cmpstr (gnc_lot_get_title (lot), ==, "unset");
+
+    gnc_lot_set_title (lot, NULL);
+    g_assert_cmpstr (gnc_lot_get_title (lot), ==, NULL);
+
+    // notes
+    g_assert_cmpstr (gnc_lot_get_notes (lot), ==, NULL);
+
+    gnc_lot_set_notes (lot, "");
+    g_assert_cmpstr (gnc_lot_get_notes (lot), ==, "");
+
+    gnc_lot_set_notes (lot, "doc");
+    g_assert_cmpstr (gnc_lot_get_notes (lot), ==, "doc");
+
+    gnc_lot_set_notes (lot, "unset");
+    g_assert_cmpstr (gnc_lot_get_notes (lot), ==, "unset");
+
+    gnc_lot_set_notes (lot, NULL);
+    g_assert_cmpstr (gnc_lot_get_notes (lot), ==, NULL);
+
+    gnc_lot_destroy (lot);
+    qof_session_end (sess);
+}
+
 static void
 run_test (void)
 {
@@ -94,6 +137,9 @@ main (int argc, char **argv)
         fflush(stdout);
         run_test ();
     }
+
+    test_lot_kvp ();
+
     /* 'erase' the recurring tag line with dummy spaces. */
     fprintf(stdout, "Lots: Test series complete.\n");
     fflush(stdout);

commit 323182c9f772fcb07f348a83abcab5b04d87871e
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 11:32:54 2021 +0800

    [utest-Account.cpp] add kvp getter/setter changes

diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp
index 8deea5fcf..6cad13b4f 100644
--- a/libgnucash/engine/test/utest-Account.cpp
+++ b/libgnucash/engine/test/utest-Account.cpp
@@ -1067,6 +1067,105 @@ gnc_account_insert_split (Account *acc, Split *s)// C: 5 in 3
 
 Also tests gnc_account_remove_split ()
 */
+
+
+static void
+test_gnc_account_kvp_setters_getters (Fixture *fixture, gconstpointer pData)
+{
+    Account *account = xaccMallocAccount (gnc_account_get_book (fixture->acct));
+    xaccAccountSetType (account, ACCT_TYPE_EQUITY);
+
+    // equity_type getter/setter
+    g_assert (xaccAccountGetIsOpeningBalance (account) == FALSE);
+
+    xaccAccountSetIsOpeningBalance (account, TRUE);
+    g_assert (xaccAccountGetIsOpeningBalance (account) == TRUE);
+
+    xaccAccountSetIsOpeningBalance (account, FALSE);
+    g_assert (xaccAccountGetIsOpeningBalance (account) == FALSE);
+
+    // sortreversed getter/setter
+    g_assert (xaccAccountGetSortReversed (account) == FALSE);
+
+    xaccAccountSetSortReversed (account, TRUE);
+    g_assert (xaccAccountGetSortReversed (account) == TRUE);
+
+    xaccAccountSetSortReversed (account, FALSE);
+    g_assert (xaccAccountGetSortReversed (account) == FALSE);
+
+    // color getter/setter
+    g_assert_cmpstr (xaccAccountGetColor (account), ==, nullptr);
+
+    xaccAccountSetColor (account, "red");
+    g_assert_cmpstr (xaccAccountGetColor (account), ==, "red");
+
+    xaccAccountSetColor (account, "unset");
+    g_assert_cmpstr (xaccAccountGetColor (account), ==, "unset");
+
+    xaccAccountSetColor (account, "");
+    g_assert_cmpstr (xaccAccountGetColor (account), ==, nullptr);
+
+    xaccAccountSetColor (account, nullptr);
+    g_assert_cmpstr (xaccAccountGetColor (account), ==, nullptr);
+
+    // filter getter/setter
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, nullptr);
+
+    xaccAccountSetFilter (account, "bla");
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, "bla");
+
+    xaccAccountSetFilter (account, "unset");
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, "unset");
+
+    xaccAccountSetFilter (account, "   unset ");
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, "unset");
+
+    xaccAccountSetFilter (account, "");
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, nullptr);
+
+    xaccAccountSetFilter (account, nullptr);
+    g_assert_cmpstr (xaccAccountGetFilter (account), ==, nullptr);
+
+    // sortOrder getter/setter
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, nullptr);
+
+    xaccAccountSetSortOrder (account, "boo");
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, "boo");
+
+    xaccAccountSetSortOrder (account, "unset");
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, "unset");
+
+    xaccAccountSetSortOrder (account, "  unset ");
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, "unset");
+
+    xaccAccountSetSortOrder (account, "");
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, nullptr);
+
+    xaccAccountSetSortOrder (account, nullptr);
+    g_assert_cmpstr (xaccAccountGetSortOrder (account), ==, nullptr);
+
+    // Notes getter/setter
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, nullptr);
+
+    xaccAccountSetNotes (account, "boo");
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, "boo");
+
+    xaccAccountSetNotes (account, "unset");
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, "unset");
+
+    xaccAccountSetNotes (account, "    unset ");
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, "unset");
+
+    xaccAccountSetNotes (account, "");
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, nullptr);
+
+    xaccAccountSetNotes (account, nullptr);
+    g_assert_cmpstr (xaccAccountGetNotes (account), ==, nullptr);
+
+    xaccAccountBeginEdit (account);
+    xaccAccountDestroy (account);
+}
+
 static void
 test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
 {
@@ -2549,6 +2648,7 @@ test_suite_account (void)
     GNC_TEST_ADD (suitename, "xaccAccountCommitEdit", Fixture, &good_data, setup, test_xaccAccountCommitEdit,  NULL );
 // GNC_TEST_ADD (suitename, "xaccAcctChildrenEqual", Fixture, NULL, setup, test_xaccAcctChildrenEqual,  teardown );
 // GNC_TEST_ADD (suitename, "xaccAccountEqual", Fixture, NULL, setup, test_xaccAccountEqual,  teardown );
+    GNC_TEST_ADD (suitename, "gnc account kvp getters & setters", Fixture, NULL, setup, test_gnc_account_kvp_setters_getters,  teardown );
     GNC_TEST_ADD (suitename, "gnc account insert & remove split", Fixture, NULL, setup, test_gnc_account_insert_remove_split,  teardown );
     GNC_TEST_ADD (suitename, "xaccAccount Insert and Remove Lot", Fixture, &good_data, setup, test_xaccAccountInsertRemoveLot,  teardown );
     GNC_TEST_ADD (suitename, "xaccAccountRecomputeBalance", Fixture, &some_data, setup, test_xaccAccountRecomputeBalance,  teardown );

commit 8a37c4c019395dee61293de56eeb796b14f76f26
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 00:13:06 2021 +0800

    [utest-Invoice.c] add kvp getter/setter tests

diff --git a/libgnucash/engine/test/utest-Invoice.c b/libgnucash/engine/test/utest-Invoice.c
index 1b979bbd3..d8323a02d 100644
--- a/libgnucash/engine/test/utest-Invoice.c
+++ b/libgnucash/engine/test/utest-Invoice.c
@@ -175,6 +175,27 @@ test_invoice_post ( Fixture *fixture, gconstpointer pData )
     g_assert(!gncInvoiceIsPosted(fixture->invoice));
 }
 
+
+static void
+test_invoice_doclink ( Fixture *fixture, gconstpointer pData )
+{
+    GncInvoice* inv = fixture->invoice;
+
+    g_assert_cmpstr (gncInvoiceGetDocLink (inv), ==, NULL);
+
+    gncInvoiceSetDocLink (inv, "doc");
+    g_assert_cmpstr (gncInvoiceGetDocLink (inv), ==, "doc");
+
+    gncInvoiceSetDocLink (inv, "unset");
+    g_assert_cmpstr (gncInvoiceGetDocLink (inv), ==, "unset");
+
+    gncInvoiceSetDocLink (inv, "");
+    g_assert_cmpstr (gncInvoiceGetDocLink (inv), ==, NULL);
+
+    gncInvoiceSetDocLink (inv, NULL);
+    g_assert_cmpstr (gncInvoiceGetDocLink (inv), ==, NULL);
+}
+
 static gboolean account_has_one_split (const Account *acc)
 {
     GList *splits = xaccAccountGetSplitList (acc);
@@ -214,6 +235,7 @@ test_suite_gncInvoice ( void )
     static InvoiceData pData = { FALSE, FALSE, { 1000, 100 }, { 2000, 100 } };  // Vendor bill
     GNC_TEST_ADD( suitename, "post/unpost", Fixture, &pData, setup, test_invoice_post, teardown );
 
+    GNC_TEST_ADD( suitename, "doclink", Fixture, &pData, setup, test_invoice_doclink, teardown );
     GNC_TEST_ADD( suitename, "post trans - vendor bill", Fixture, &pData, setup_with_invoice, test_invoice_posted_trans, teardown_with_invoice );
     pData.is_cn = TRUE;   // Vendor credit note
     GNC_TEST_ADD( suitename, "post trans - vendor credit note", Fixture, &pData, setup_with_invoice, test_invoice_posted_trans, teardown_with_invoice );

commit 9ec670f8286ab5f3b568f07bd79cc7509d6d2052
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 00:06:38 2021 +0800

    [test-commodities.cpp] add kvp setter/getter test

diff --git a/libgnucash/engine/test/test-commodities.cpp b/libgnucash/engine/test/test-commodities.cpp
index efd942158..24118277f 100644
--- a/libgnucash/engine/test/test-commodities.cpp
+++ b/libgnucash/engine/test/test-commodities.cpp
@@ -122,6 +122,11 @@ test_commodity(void)
             gnc_commodity_get_fraction(com) == fraction,
             "reset fraction code equal test");
 
+        g_assert_cmpstr (gnc_commodity_get_user_symbol(com), ==, NULL);
+
+        gnc_commodity_set_user_symbol (com, "CA$");
+        g_assert_cmpstr (gnc_commodity_get_user_symbol(com), ==, "CA$");
+
         com2 = gnc_commodity_new(book, fullname, name_space, mnemonic,
                                  cusip, fraction);
         do_test(

commit d4c8b36f9f2d0e19506019a18cb2605f3bd0fba8
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 30 23:58:41 2021 +0800

    [utest-Transaction.cpp] add kvp setter/getter tests
    
    wasn't tested before

diff --git a/libgnucash/engine/test/utest-Transaction.cpp b/libgnucash/engine/test/utest-Transaction.cpp
index beba27bab..620983d39 100644
--- a/libgnucash/engine/test/utest-Transaction.cpp
+++ b/libgnucash/engine/test/utest-Transaction.cpp
@@ -1843,6 +1843,26 @@ void
 xaccTransUnvoid (Transaction *trans)// C: 1  Local: 0:0:0
 */
 
+static void
+test_xaccTransSetDocLink (Fixture *fixture, gconstpointer pData)
+{
+    auto trans = fixture->txn;
+
+    g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL);
+
+    xaccTransSetDocLink (trans, "doclink");
+    g_assert_cmpstr (xaccTransGetDocLink (trans), ==, "doclink");
+
+    xaccTransSetDocLink (trans, "unset");
+    g_assert_cmpstr (xaccTransGetDocLink (trans), ==, "unset");
+
+    xaccTransSetDocLink (trans, "");
+    g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL);
+
+    xaccTransSetDocLink (trans, NULL);
+    g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL);
+}
+
 static void
 test_xaccTransVoid (Fixture *fixture, gconstpointer pData)
 {
@@ -2045,6 +2065,7 @@ test_suite_transaction (void)
     GNC_TEST_ADD (suitename, "xaccTransRollbackEdit - Backend Errors", Fixture, NULL, setup, test_xaccTransRollbackEdit_BackendErrors, teardown);
     GNC_TEST_ADD (suitename, "xaccTransOrder_num_action", Fixture, NULL, setup, test_xaccTransOrder_num_action, teardown);
     GNC_TEST_ADD (suitename, "xaccTransGetTxnType", Fixture, NULL, setup, test_xaccTransGetTxnType, teardown);
+    GNC_TEST_ADD (suitename, "xaccTransSetDocLink", Fixture, NULL, setup, test_xaccTransSetDocLink, teardown);
     GNC_TEST_ADD (suitename, "xaccTransVoid", Fixture, NULL, setup, test_xaccTransVoid, teardown);
     GNC_TEST_ADD (suitename, "xaccTransReverse", Fixture, NULL, setup, test_xaccTransReverse, teardown);
     GNC_TEST_ADD (suitename, "xaccTransScrubGainsDate_no_dirty", GainsFixture, NULL, setup_with_gains, test_xaccTransScrubGainsDate_no_dirty, teardown_with_gains);

commit c3b50f0b5838b396407cd0c06536fcaa7d9f6863
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 00:27:16 2021 +0800

    [gnc-lot.c] fix - cache value properly

diff --git a/libgnucash/engine/gnc-lot.c b/libgnucash/engine/gnc-lot.c
index a8eaf7902..7302ef136 100644
--- a/libgnucash/engine/gnc-lot.c
+++ b/libgnucash/engine/gnc-lot.c
@@ -498,6 +498,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
+    priv->title = g_strdup (str);
     qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "title");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);
@@ -516,6 +517,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
+    priv->notes = g_strdup (str);
     qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);

commit 595e126a34d557bcc07c675e8cff2f3e84674471
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 11:51:37 2021 +0800

    [account.cpp] modify set_kvp_string_tag to accept strstrip or nullptr
    
    - will remove a second call to g_strstrip (g_strdup (str))

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 90a3456cb..39cb5b88e 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -2483,6 +2483,8 @@ stripdup_or_null (const char *value)
     return nullptr;
 }
 
+// note the *value argument is expected to be either a strstripped
+// char* or nullptr, as returned by stripdup_or_null above.
 static void
 set_kvp_string_tag (Account *acc, const char *tag, const char *value)
 {
@@ -2491,18 +2493,11 @@ set_kvp_string_tag (Account *acc, const char *tag, const char *value)
     xaccAccountBeginEdit(acc);
     if (value)
     {
-        gchar *tmp = g_strstrip(g_strdup(value));
-        if (strlen (tmp))
-        {
-            GValue v = G_VALUE_INIT;
-            g_value_init (&v, G_TYPE_STRING);
-            g_value_set_string (&v, tmp);
-            qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {tag});
-            g_value_unset (&v);
-        }
-        else
-            qof_instance_set_path_kvp (QOF_INSTANCE (acc), NULL, {tag});
-        g_free(tmp);
+        GValue v = G_VALUE_INIT;
+        g_value_init (&v, G_TYPE_STRING);
+        g_value_set_string (&v, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {tag});
+        g_value_unset (&v);
     }
     else
     {
@@ -2530,7 +2525,7 @@ xaccAccountSetColor (Account *acc, const char *str)
     if (priv->color != is_unset)
         g_free (priv->color);
     priv->color = stripdup_or_null (str);
-    set_kvp_string_tag (acc, "color", str);
+    set_kvp_string_tag (acc, "color", priv->color);
 }
 
 void
@@ -2540,7 +2535,7 @@ xaccAccountSetFilter (Account *acc, const char *str)
     if (priv->filter != is_unset)
         g_free (priv->filter);
     priv->filter = stripdup_or_null (str);
-    set_kvp_string_tag (acc, "filter", str);
+    set_kvp_string_tag (acc, "filter", priv->filter);
 }
 
 void
@@ -2550,7 +2545,7 @@ xaccAccountSetSortOrder (Account *acc, const char *str)
     if (priv->sort_order != is_unset)
         g_free (priv->sort_order);
     priv->sort_order = stripdup_or_null (str);
-    set_kvp_string_tag (acc, "sort-order", str);
+    set_kvp_string_tag (acc, "sort-order", priv->sort_order);
 }
 
 void
@@ -2586,7 +2581,7 @@ xaccAccountSetNotes (Account *acc, const char *str)
     if (priv->notes != is_unset)
         g_free (priv->notes);
     priv->notes = stripdup_or_null (str);
-    set_kvp_string_tag (acc, "notes", str);
+    set_kvp_string_tag (acc, "notes", priv->notes);
 }
 
 void
@@ -4236,7 +4231,7 @@ xaccAccountSetIsOpeningBalance (Account *acc, gboolean val)
         return;
     auto priv = GET_PRIVATE (acc);
     priv->equity_type = val ? TriState::True : TriState::False;
-    set_kvp_string_tag(acc, "equity-type", val ? "opening-balance" : "");
+    set_kvp_string_tag(acc, "equity-type", val ? "opening-balance" : nullptr);
 }
 
 GNCPlaceholderType

commit c5a6383b431b04e8d487cb4a2394970000a8657c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 11:32:23 2021 +0800

    [account.cpp] fix - incomplete #1117
    
    - init struct members properly to: is_unset and TriState::Unset
    - test acct kvp "equity-type" for "opening-balance" instead of "true"
    - cached account->color/sort_order/notes/filter gets strstripped
      similarly to previous behavior

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 14e4dfd4b..90a3456cb 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -325,12 +325,12 @@ gnc_account_init(Account* acc)
     priv->starting_reconciled_balance = gnc_numeric_zero();
     priv->balance_dirty = FALSE;
 
-    priv->color == is_unset;
-    priv->sort_order == is_unset;
-    priv->notes == is_unset;
-    priv->filter == is_unset;
-    priv->equity_type == TriState::Unset;
-    priv->sort_reversed == TriState::Unset;
+    priv->color = (char*) is_unset;
+    priv->sort_order = (char*) is_unset;
+    priv->notes = (char*) is_unset;
+    priv->filter = (char*) is_unset;
+    priv->equity_type = TriState::Unset;
+    priv->sort_reversed = TriState::Unset;
 
     priv->splits = NULL;
     priv->sort_dirty = FALSE;
@@ -2470,6 +2470,19 @@ xaccAccountSetDescription (Account *acc, const char *str)
     xaccAccountCommitEdit(acc);
 }
 
+static char*
+stripdup_or_null (const char *value)
+{
+    if (value)
+    {
+        auto temp = g_strstrip (g_strdup (value));
+        if (*temp)
+            return temp;
+        g_free (temp);
+    }
+    return nullptr;
+}
+
 static void
 set_kvp_string_tag (Account *acc, const char *tag, const char *value)
 {
@@ -2516,7 +2529,7 @@ xaccAccountSetColor (Account *acc, const char *str)
     auto priv = GET_PRIVATE (acc);
     if (priv->color != is_unset)
         g_free (priv->color);
-    priv->color = g_strdup (str);
+    priv->color = stripdup_or_null (str);
     set_kvp_string_tag (acc, "color", str);
 }
 
@@ -2526,7 +2539,7 @@ xaccAccountSetFilter (Account *acc, const char *str)
     auto priv = GET_PRIVATE (acc);
     if (priv->filter != is_unset)
         g_free (priv->filter);
-    priv->filter = g_strdup (str);
+    priv->filter = stripdup_or_null (str);
     set_kvp_string_tag (acc, "filter", str);
 }
 
@@ -2536,7 +2549,7 @@ xaccAccountSetSortOrder (Account *acc, const char *str)
     auto priv = GET_PRIVATE (acc);
     if (priv->sort_order != is_unset)
         g_free (priv->sort_order);
-    priv->sort_order = g_strdup (str);
+    priv->sort_order = stripdup_or_null (str);
     set_kvp_string_tag (acc, "sort-order", str);
 }
 
@@ -2572,7 +2585,7 @@ xaccAccountSetNotes (Account *acc, const char *str)
     auto priv = GET_PRIVATE (acc);
     if (priv->notes != is_unset)
         g_free (priv->notes);
-    priv->notes = g_strdup (str);
+    priv->notes = stripdup_or_null (str);
     set_kvp_string_tag (acc, "notes", str);
 }
 
@@ -4209,7 +4222,7 @@ xaccAccountGetIsOpeningBalance (const Account *acc)
     if (priv->equity_type == TriState::Unset)
     {
         auto equity_type = get_kvp_string_tag (acc, "equity-type");
-        priv->equity_type = g_strcmp0 (equity_type, "true") ?
+        priv->equity_type = g_strcmp0 (equity_type, "opening-balance") ?
             TriState::False : TriState::True;
         g_free (equity_type);
     }

commit 86c438dcf4ba9492af0a32fac953b5b6b865f598
Author: goodvibes2 <goodchris96 at gmail.com>
Date:   Tue Aug 31 10:06:42 2021 +1000

    Fix unneeded double call to get_action_for_path()
    
    in gnc_gen_trans_onButtonPressed_cb().

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 83e5a7f6e..ff779eeae 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -973,7 +973,6 @@ gnc_gen_trans_onButtonPressed_cb (GtkTreeView *treeview,
                 GList* selected;
                 GtkTreeModel *model;
                 selected = gtk_tree_selection_get_selected_rows (selection, &model);
-                get_action_for_path (selected->data, model);
                 if (get_action_for_path (selected->data, model) == GNCImport_ADD)
                     gnc_gen_trans_view_popup_menu (treeview, event, info);
                 g_list_free_full (selected, (GDestroyNotify)gtk_tree_path_free);

commit 52715face7808f7f3bd3ab590a5feaa2c8516a0b
Author: goodvibes2 <goodchris96 at gmail.com>
Date:   Tue Aug 31 09:53:38 2021 +1000

    Fix 2 crashes in Generic Transaction Matcher
    
    Both dumped core in gnc_import_TransInfo_get_action() because info
    is null
    1. After double clicking on a potential match line, then right clicking
    on the the parent transaction of the potential match
    2. After rubber banding a group of transactions which also includes an
    expanded potential match. Sometimes also need to right click the
    selection to trigger the core dump.

diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index dca451efb..83e5a7f6e 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -867,6 +867,10 @@ get_action_for_path (GtkTreePath* path, GtkTreeModel *model)
     GtkTreeIter iter;
     gtk_tree_model_get_iter (model, &iter, path);
     gtk_tree_model_get (model, &iter, DOWNLOADED_COL_DATA, &trans_info, -1);
+    if (!trans_info)
+          // selected row is a potential match  (depth 2)
+          // instead of an imported transaction (depth 1)
+          return GNCImport_INVALID_ACTION;
     return gnc_import_TransInfo_get_action (trans_info);
 }
 

commit bda03a1c5620860acc041269f011ffa906abd80b
Author: Giuseppe Foti <foti.giuseppe at gmail.com>
Date:   Mon Aug 30 17:32:28 2021 +0200

    Translation update  by Giuseppe Foti <foti.giuseppe at gmail.com> using Weblate
    
    po/it.po: 100.0% (5364 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Italian)
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/it/
    
    Co-authored-by: Giuseppe Foti <foti.giuseppe at gmail.com>

diff --git a/po/it.po b/po/it.po
index e58ce967f..9506fb766 100644
--- a/po/it.po
+++ b/po/it.po
@@ -55,7 +55,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: https://bugs.gnucash.org/enter_bug."
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
-"PO-Revision-Date: 2021-07-19 07:32+0000\n"
+"PO-Revision-Date: 2021-08-30 15:32+0000\n"
 "Last-Translator: Giuseppe Foti <foti.giuseppe at gmail.com>\n"
 "Language-Team: Italian <https://hosted.weblate.org/projects/gnucash/gnucash/"
 "it/>\n"
@@ -64,7 +64,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.7.2-dev\n"
+"X-Generator: Weblate 4.8.1-dev\n"
 
 #: bindings/guile/commodity-table.scm:44
 msgid "ALL NON-CURRENCY"

commit f97821e07281be96d4f21d9bf0263fddf02dd86e
Author: Avi Markovitz <avi.markovitz at gmail.com>
Date:   Mon Aug 30 17:32:27 2021 +0200

    Translation update  by Avi Markovitz <avi.markovitz at gmail.com> using Weblate
    
    po/he.po: 100.0% (5364 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Hebrew)
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/he/
    
    Co-authored-by: Avi Markovitz <avi.markovitz at gmail.com>

diff --git a/po/he.po b/po/he.po
index 31ee7e2cf..86307e691 100644
--- a/po/he.po
+++ b/po/he.po
@@ -11,8 +11,8 @@ msgstr ""
 "Report-Msgid-Bugs-To: https://bugs.gnucash.org/enter_bug."
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
-"PO-Revision-Date: 2021-08-30 09:19+0000\n"
-"Last-Translator: Yaron Shahrabani <sh.yaron at gmail.com>\n"
+"PO-Revision-Date: 2021-08-30 10:32+0000\n"
+"Last-Translator: Avi Markovitz <avi.markovitz at gmail.com>\n"
 "Language-Team: Hebrew <https://hosted.weblate.org/projects/gnucash/gnucash/"
 "he/>\n"
 "Language: he\n"
@@ -6337,7 +6337,7 @@ msgid ""
 "You would be deleting a transaction with reconciled splits! This is not a "
 "good idea as it will cause your reconciled balance to be off."
 msgstr ""
-"תנועה עם פיצולים מותאמים, תימחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
+"תנועה עם פיצולים מותאמים, תמחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
 "לצאת מאיזון."
 
 #: gnucash/gnome/gnc-split-reg.c:1716
@@ -6350,11 +6350,11 @@ msgid ""
 "Select OK to temporarily clear filter and proceed,\n"
 "otherwise the last active cell will be selected."
 msgstr ""
-"פיצול היעד ביומן זה, מוסתר כעת.\n"
+"פיצול היעד מוסתר כעת, ביומן זה.\n"
 "\n"
 "%s\n"
 "\n"
-"נא להקיש על 'בסדר' לניקוי זמני של המסנן ולהמשיך.\n"
+"נא להקיש על 'בסדר', לניקוי זמני של המסנן ולהמשיך.\n"
 "אחרת התא הפעיל האחרון יבחר."
 
 #: gnucash/gnome/gnc-split-reg.c:2371

commit 22e64905a161f7f793b2e594819d8e1203be3f07
Author: Yaron Shahrabani <sh.yaron at gmail.com>
Date:   Mon Aug 30 17:32:27 2021 +0200

    Translation update  by Yaron Shahrabani <sh.yaron at gmail.com> using Weblate
    
    po/he.po: 100.0% (5364 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Hebrew)
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/he/
    
    Co-authored-by: Yaron Shahrabani <sh.yaron at gmail.com>

diff --git a/po/he.po b/po/he.po
index c1cbf168e..31ee7e2cf 100644
--- a/po/he.po
+++ b/po/he.po
@@ -12,7 +12,7 @@ msgstr ""
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
 "PO-Revision-Date: 2021-08-30 09:19+0000\n"
-"Last-Translator: Avi Markovitz <avi.markovitz at gmail.com>\n"
+"Last-Translator: Yaron Shahrabani <sh.yaron at gmail.com>\n"
 "Language-Team: Hebrew <https://hosted.weblate.org/projects/gnucash/gnucash/"
 "he/>\n"
 "Language: he\n"
@@ -6337,7 +6337,7 @@ msgid ""
 "You would be deleting a transaction with reconciled splits! This is not a "
 "good idea as it will cause your reconciled balance to be off."
 msgstr ""
-"תנועה עם פיצולים מותאמים, תמחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
+"תנועה עם פיצולים מותאמים, תימחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
 "לצאת מאיזון."
 
 #: gnucash/gnome/gnc-split-reg.c:1716

commit 1a049f4ef588403d1c29e2fe63e19c460e7ff1be
Author: Avi Markovitz <avi.markovitz at gmail.com>
Date:   Mon Aug 30 17:32:26 2021 +0200

    Translation update  by Avi Markovitz <avi.markovitz at gmail.com> using Weblate
    
    po/he.po: 100.0% (5364 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Hebrew)
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/he/
    
    Translation update  by Avi Markovitz <avi.markovitz at gmail.com> using Weblate
    
    po/he.po: 100.0% (5364 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Hebrew)
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/he/
    
    Co-authored-by: Avi Markovitz <avi.markovitz at gmail.com>

diff --git a/po/he.po b/po/he.po
index 29a0f59a1..c1cbf168e 100644
--- a/po/he.po
+++ b/po/he.po
@@ -11,7 +11,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: https://bugs.gnucash.org/enter_bug."
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
-"PO-Revision-Date: 2021-08-22 11:32+0000\n"
+"PO-Revision-Date: 2021-08-30 09:19+0000\n"
 "Last-Translator: Avi Markovitz <avi.markovitz at gmail.com>\n"
 "Language-Team: Hebrew <https://hosted.weblate.org/projects/gnucash/gnucash/"
 "he/>\n"
@@ -6250,7 +6250,7 @@ msgid ""
 "You would be removing a transaction with reconciled splits! This is not a "
 "good idea as it will cause your reconciled balance to be off."
 msgstr ""
-"תנועה עם פיצולים מותאמים תוסר! לא ממש רעיון טוב שכן זה יגרום ליתרה המותאמת "
+"תנועה עם פיצולים מותאמים, תוסר! לא ממש רעיון טוב שכן זה יגרום ליתרה המותאמת "
 "לצאת מאיזון."
 
 #: gnucash/gnome/gnc-split-reg.c:1014
@@ -6337,7 +6337,7 @@ msgid ""
 "You would be deleting a transaction with reconciled splits! This is not a "
 "good idea as it will cause your reconciled balance to be off."
 msgstr ""
-"תנועה עם פיצולים מותאמים תמחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
+"תנועה עם פיצולים מותאמים, תמחק! לא ממש רעיון טוב שכן זה יגרום ליתרה מותאמת "
 "לצאת מאיזון."
 
 #: gnucash/gnome/gnc-split-reg.c:1716
@@ -9006,7 +9006,7 @@ msgid ""
 "so might make future reconciliation difficult! Continue with this change?"
 msgstr ""
 "פעולה זו תשנה פיצול המקושר לפיצול שהותאם. ביצוע פעולה זו עלול להפוך התאמות "
-"העתידיות לקשה יותר! האם להמשיך בשינוי זה?"
+"העתידיות לקשה יותר! להמשיך בשינוי זה?"
 
 #: gnucash/gnome-utils/gnc-tree-control-split-reg.c:1934
 #: gnucash/register/ledger-core/split-register-model.c:2254
@@ -10105,8 +10105,8 @@ msgid ""
 "Set the prefix for gsettings schemas for gsettings queries. This can be "
 "useful to have a different settings tree while debugging."
 msgstr ""
-"הגדרת הקידומת בסכימות gsettings עבור שאילתות gsettings. עשוי להיות שימושי, "
-"שיהיה עץ הגדרות שונה, במהלך ניפוי תקלים."
+"הגדרת הקידומת בסכימות gsettings עבור שאילתות gsettings. בעת ניפוי תקלים, "
+"יותר נוח לעבוד מול עץ הגדרות שונה."
 
 #: gnucash/gnucash-core-app.cpp:344
 msgid "Hidden Options"
@@ -11194,7 +11194,7 @@ msgstr "אפשור דחיסת קובץ בעת כתיבה לקובץ נתונים
 
 #: gnucash/gschemas/org.gnucash.gschema.xml.in:35
 msgid "Show auto-save explanation"
-msgstr "הצגת הסבר על אודות שמירה אוטומטית"
+msgstr "הצגת הסבר על שמירה אוטומטית"
 
 #: gnucash/gschemas/org.gnucash.gschema.xml.in:36
 msgid ""
@@ -11377,8 +11377,8 @@ msgid ""
 "account's custom color if set. This can serve as a visual aid to quickly "
 "identify accounts."
 msgstr ""
-"במידה והאפשרות הופעלה, החשבון יוצג בתרשים החשבונות בצבע מותאם אישית, ככל "
-"שנקבע לחשבון צבע. יכול לשמש כעזר חזותי לזיהוי חשבונות במהירות."
+"אם האפשרות הופעלה, החשבון יוצג בתרשים החשבונות בצבע מותאם אישית, ככל שנקבע "
+"לחשבון צבע. יכול לשמש כעזר חזותי לזיהוי חשבונות במהירות."
 
 #: gnucash/gschemas/org.gnucash.gschema.xml.in:115
 msgid "Use account colors in the tabs of open account registers"
@@ -23275,7 +23275,7 @@ msgid ""
 "For help on writing reports, or to contribute your brand new, totally cool "
 "report, consult the mailing list ~a."
 msgstr ""
-"לעזרה אודות כתיבת דוחות, או לשיתוף הדוח החדש והמדליק שיצרתם, ניתן להיוועץ "
+"לעזרה על כתיבת דוחות, או לשיתוף הדוח החדש והמדליק שיצרתם, ניתן להיוועץ "
 "ברשימת הדיוור ~a."
 
 #: gnucash/report/reports/example/hello-world.scm:339
@@ -28117,11 +28117,11 @@ msgstr "הצגת תנועות סגירה בלבד"
 
 #: gnucash/report/trep-engine.scm:382
 msgid "Show All Transactions"
-msgstr "שמירת כל התנועות"
+msgstr "הצגת כל התנועות"
 
 #: gnucash/report/trep-engine.scm:386
 msgid "Unreconciled only"
-msgstr "לא־מותאמים בלבד"
+msgstr "פתוחים בלבד"
 
 #: gnucash/report/trep-engine.scm:390
 msgid "Cleared only"

commit 1a12489ef943ed92f541ec53403e0a744511ac02
Author: TianXing_Yi <ytx.cash at gmail.com>
Date:   Mon Aug 30 17:32:25 2021 +0200

    Translation update  by TianXing_Yi <ytx.cash at gmail.com> using Weblate
    
    po/zh_CN.po: 99.7% (5353 of 5364 strings; 0 fuzzy)
    0 failing checks (0.0%)
    Translation: GnuCash/Program (Chinese (Simplified))
    Translate-URL: https://hosted.weblate.org/projects/gnucash/gnucash/zh_Hans/
    
    Co-authored-by: TianXing_Yi <ytx.cash at gmail.com>

diff --git a/po/zh_CN.po b/po/zh_CN.po
index f61c5768f..25697da26 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -23,7 +23,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: https://bugs.gnucash.org/enter_bug."
 "cgi?product=GnuCash&component=Translations\n"
 "POT-Creation-Date: 2021-06-30 07:34+0200\n"
-"PO-Revision-Date: 2021-08-22 11:32+0000\n"
+"PO-Revision-Date: 2021-08-29 06:34+0000\n"
 "Last-Translator: TianXing_Yi <ytx.cash at gmail.com>\n"
 "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
 "gnucash/gnucash/zh_Hans/>\n"
@@ -13254,7 +13254,7 @@ msgstr "托管账户"
 
 #: gnucash/gtkbuilder/assistant-loan.glade:540
 msgid "Loan Repayment Options"
-msgstr "还款选项"
+msgstr "托管科目"
 
 #: gnucash/gtkbuilder/assistant-loan.glade:553
 msgid ""
@@ -13278,7 +13278,7 @@ msgstr "频率"
 
 #: gnucash/gtkbuilder/assistant-loan.glade:718
 msgid "Loan Repayment"
-msgstr "还款"
+msgstr "还款选项"
 
 #: gnucash/gtkbuilder/assistant-loan.glade:731
 msgid ""

commit 5d15f95f9e8761b9939c86ebe50e7a689cd1100e
Merge: 0c21ae352 998e14c10
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 31 00:38:01 2021 +0800

    Merge branch 'maint-leaks' into maint #1116


commit 998e14c1074e0caebf53782d51810c5874e89a6d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Aug 20 21:37:33 2021 +0800

    gtk_list_store_new returning a GObject must be g_object_unref
    
    GObjects were not consistently unrefed.

diff --git a/gnucash/gnome-search/gnc-general-search.c b/gnucash/gnome-search/gnc-general-search.c
index 8e4ab873a..0f1bc37c3 100644
--- a/gnucash/gnome-search/gnc-general-search.c
+++ b/gnucash/gnome-search/gnc-general-search.c
@@ -405,6 +405,7 @@ create_children (GNCGeneralSearch *gsl,
     g_signal_connect (G_OBJECT (gsl->entry), "focus-out-event",
                       G_CALLBACK (gnc_gsl_focus_out_cb), gsl);
 
+    g_object_unref (list_store);
     g_object_unref(completion);
     gtk_widget_show (gsl->entry);
 
diff --git a/gnucash/gnome-utils/dialog-object-references.c b/gnucash/gnome-utils/dialog-object-references.c
index c8b3c8787..0bc5d99b3 100644
--- a/gnucash/gnome-utils/dialog-object-references.c
+++ b/gnucash/gnome-utils/dialog-object-references.c
@@ -87,6 +87,7 @@ gnc_ui_object_references_show( const gchar* explanation_text, GList* objlist )
     gtk_widget_show_all( dialog );
     gtk_dialog_run( GTK_DIALOG(dialog) );
     g_object_unref(G_OBJECT(builder));
+    g_object_unref (store);
     gtk_widget_destroy( dialog );
 
     LEAVE("");
diff --git a/gnucash/gnome-utils/gnc-account-sel.c b/gnucash/gnome-utils/gnc-account-sel.c
index 25126c4a1..7c53461a4 100644
--- a/gnucash/gnome-utils/gnc-account-sel.c
+++ b/gnucash/gnome-utils/gnc-account-sel.c
@@ -165,7 +165,6 @@ gnc_account_sel_init (GNCAccountSel *gas)
     widget = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(gas->store));
     gas->combo = GTK_COMBO_BOX(widget);
     gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(widget), ACCT_COL_NAME);
-    g_object_unref (gas->store);
     g_signal_connect_swapped (gas->combo, "changed",
                               G_CALLBACK(combo_changed_cb), gas);
     gtk_container_add (GTK_CONTAINER(gas), widget);
@@ -417,6 +416,12 @@ gnc_account_sel_dispose (GObject *object)
 
     gas = GNC_ACCOUNT_SEL(object);
 
+    if (gas->store)
+    {
+        g_object_unref (gas->store);
+        gas->store = NULL;
+    }
+
     if (gas->eventHandlerId)
     {
         qof_event_unregister_handler (gas->eventHandlerId);
diff --git a/gnucash/gnome-utils/gnc-dense-cal.c b/gnucash/gnome-utils/gnc-dense-cal.c
index 1fd79247f..e2897889e 100644
--- a/gnucash/gnome-utils/gnc-dense-cal.c
+++ b/gnucash/gnome-utils/gnc-dense-cal.c
@@ -348,6 +348,7 @@ gnc_dense_cal_init(GncDenseCal *dcal)
         gtk_tree_view_insert_column_with_attributes(tree_view, -1, _("Frequency"), gtk_cell_renderer_text_new(), "text", 1, NULL);
         gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view)), GTK_SELECTION_NONE);
         g_object_set_data(G_OBJECT(dcal->transPopup), "model", tree_data);
+        g_object_unref (tree_data);
         gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(tree_view));
 
         gtk_container_add(GTK_CONTAINER(dcal->transPopup), vbox);
diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c
index 38f540ed4..54873226d 100644
--- a/gnucash/gnome/assistant-hierarchy.c
+++ b/gnucash/gnome/assistant-hierarchy.c
@@ -584,6 +584,8 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
     g_signal_connect (data->region_combo, "changed",
                       G_CALLBACK(region_combo_changed_cb), (gpointer)data);
 
+    g_object_unref (language_store);
+    g_object_unref (region_store);
     g_free (start_region);
 }
 
diff --git a/gnucash/gnome/dialog-print-check.c b/gnucash/gnome/dialog-print-check.c
index 5c85f8bfc..be56c3f0c 100644
--- a/gnucash/gnome/dialog-print-check.c
+++ b/gnucash/gnome/dialog-print-check.c
@@ -1600,6 +1600,7 @@ initialize_format_combobox (PrintCheckDialog *pcd)
                             GTK_TREE_MODEL(store));
     gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(pcd->format_combobox),
                                          format_is_a_separator, NULL, NULL);
+    g_object_unref (store);
 }
 
 
@@ -2608,6 +2609,7 @@ gnc_print_check_format_changed (GtkComboBox *widget,
     }
     gtk_list_store_append(GTK_LIST_STORE(p_store), &iter);
     gtk_list_store_set (GTK_LIST_STORE(p_store), &iter, 0, _("Custom"), -1);
+    g_object_unref (p_store);
 
     /* If there's only one thing in the position combobox, make it insensitive */
     sensitive = (pcd->position_max > 0);
diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.c
index 1df072a85..bf6fe65a0 100644
--- a/gnucash/gnome/dialog-report-style-sheet.c
+++ b/gnucash/gnome/dialog-report-style-sheet.c
@@ -458,6 +458,7 @@ gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_data)
 
     gnc_unregister_gui_component (ss->component_id);
 
+    g_object_unref (ss->list_store);
     if (ss->toplevel)
     {
         gtk_widget_destroy (ss->toplevel);
@@ -513,7 +514,6 @@ gnc_style_sheet_select_dialog_create (GtkWindow *parent)
     ss->list_view  = GTK_TREE_VIEW(gtk_builder_get_object (builder, "style_sheet_list_view"));
     ss->list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
     gtk_tree_view_set_model (ss->list_view, GTK_TREE_MODEL(ss->list_store));
-    g_object_unref (ss->list_store);
 
     renderer = gtk_cell_renderer_text_new ();
     gtk_tree_view_insert_column_with_attributes (ss->list_view, -1,
diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index c57221615..58575115e 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -516,6 +516,7 @@ gbv_create_widget (GncBudgetView *budget_view)
     gtk_tree_selection_set_mode (gtk_tree_view_get_selection (totals_tree_view), GTK_SELECTION_NONE);
     gtk_tree_view_set_headers_visible (totals_tree_view, FALSE);
     gtk_tree_view_set_model (totals_tree_view, GTK_TREE_MODEL(totals_tree_model));
+    g_object_unref (totals_tree_model);
 
     // add the totals title column
     totals_title_col = gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new (), "text", 0, NULL);
diff --git a/gnucash/import-export/csv-imp/assistant-csv-account-import.c b/gnucash/import-export/csv-imp/assistant-csv-account-import.c
index 469838cf9..953c8f66b 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-account-import.c
+++ b/gnucash/import-export/csv-imp/assistant-csv-account-import.c
@@ -619,6 +619,7 @@ csv_import_close_handler (gpointer user_data)
     g_free (info->starting_dir);
     g_free (info->file_name);
     g_string_free (info->regexp, TRUE);
+    g_object_unref (info->store);
 
     gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->assistant));
     gtk_widget_destroy (info->assistant);
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index a7c921bd6..49175fd1c 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -513,6 +513,7 @@ GtkTreeModel *get_model (bool all_commodity)
     }
     g_list_free (commodity_list);
     g_list_free (namespace_list);
+    g_object_unref (store);
 
     return model;
 }
@@ -572,6 +573,8 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
         // Add Settings combo
         auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
         settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
+        g_object_unref (settings_store);
+
         gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME);
         gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0);
 
@@ -1635,6 +1638,7 @@ void CsvImpPriceAssist::preview_refresh_table ()
     }
     gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
     gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR);
+    g_object_unref (store);
 
     /* Adjust treeview to go with the just created model. This consists of adding
      * or removing columns and resetting any parameters related to how
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 98d6f324a..243c832de 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -512,6 +512,7 @@ CsvImpTransAssist::CsvImpTransAssist ()
         // Add Settings combo
         auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
         settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
+        g_object_unref (settings_store);
         gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME);
         gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0);
 
@@ -1563,6 +1564,7 @@ void CsvImpTransAssist::preview_refresh_table ()
     }
     gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
     gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR);
+    g_object_unref (store);
 
     /* Adjust treeview to go with the just created model. This consists of adding
      * or removing columns and resetting any parameters related to how
diff --git a/gnucash/register/register-gnome/combocell-gnome.c b/gnucash/register/register-gnome/combocell-gnome.c
index 38d162b65..f0a73da02 100644
--- a/gnucash/register/register-gnome/combocell-gnome.c
+++ b/gnucash/register/register-gnome/combocell-gnome.c
@@ -299,6 +299,12 @@ gnc_combo_cell_gui_destroy (BasicCell* bcell)
             box->item_list = NULL;
         }
 
+        if (box && box->tmp_store)
+        {
+            g_object_unref (box->tmp_store);
+            box->tmp_store = NULL;
+        }
+
         /* allow the widget to be shown again */
         cell->cell.gui_realize = gnc_combo_cell_gui_realize;
         cell->cell.gui_move = NULL;

commit 0c21ae35221b49c640d1025d66ea024e24db15c2
Author: Mike Alexander <mta at umich.edu>
Date:   Mon Aug 30 03:09:23 2021 -0400

    Handle very large denominators in precies from Finance::Quote
    
    The OpenExchange exchange rate source in Finance::Quote produces
    some ridiculously precise prices like #e6.95253159056541e-5 which
    produce a denominator greater than INT64_MAX.  Use the rationalize
    function to bring them back to reality.

diff --git a/gnucash/price-quotes.scm b/gnucash/price-quotes.scm
index 2c6375585..8e3ff255f 100644
--- a/gnucash/price-quotes.scm
+++ b/gnucash/price-quotes.scm
@@ -346,7 +346,13 @@
           (cond
            ((assq-ref quote-data (car price-syms)) =>
             (lambda (p)
-              (set! price (gnc-scm-to-numeric p))
+              ;; The OpenExchange exchange rate source in Finance::Quote produces
+              ;; some ridiculously precise prices like #e6.95253159056541e-5 which 
+              ;; produce a denominator greater than INT64_MAX.  Use the rationalize
+              ;; function to bring them back to reality.  The precision parameter is
+              ;; chosen empirically to give the best results.
+              (set! price (gnc-scm-to-numeric 
+                            (rationalize p 1/100000000000000)))
               (set! price-type (car price-types))))
            (else (lp (cdr price-syms) (cdr price-types))))))
 

commit e13644dffc1375c568ffcd2f80efd2d9825be561
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Aug 28 22:05:37 2021 +0800

    free GtkTreePath after calling gtk_tree_view_get_path_at_pos

diff --git a/gnucash/gnome/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c
index f2af3b0ef..4badfe983 100644
--- a/gnucash/gnome/dialog-custom-report.c
+++ b/gnucash/gnome/dialog-custom-report.c
@@ -423,6 +423,7 @@ custom_report_list_view_clicked_cb(GtkTreeView *view, GdkEventButton *event, gpo
         {
             SCM guid = get_custom_report_selection(crd, _("You must select a report configuration to load."));
             custom_report_run_report (guid, crd);
+            gtk_tree_path_free (path);
             return TRUE;
         }
         else if (column == crd->editcol)
@@ -430,14 +431,17 @@ custom_report_list_view_clicked_cb(GtkTreeView *view, GdkEventButton *event, gpo
             g_object_set(G_OBJECT(crd->namerenderer), "editable", TRUE, NULL);
             gtk_tree_view_set_cursor_on_cell (view, path, crd->namecol,
                                               crd->namerenderer, TRUE);
+            gtk_tree_path_free (path);
             return TRUE;
         }
         else if (column == crd->delcol)
         {
             SCM guid = get_custom_report_selection(crd, _("You must select a report configuration to delete."));
             custom_report_delete (guid, crd);
+            gtk_tree_path_free (path);
             return TRUE;
         }
+        gtk_tree_path_free (path);
     }
     return FALSE;
 }
@@ -489,10 +493,12 @@ custom_report_query_tooltip_cb (GtkTreeView  *view,
                 gtk_tooltip_set_text (tooltip, _("Edit report configuration name"));
             else if (column == crd->delcol)
                 gtk_tooltip_set_text (tooltip, _("Delete report configuration"));
+            gtk_tree_path_free (path);
             return TRUE;
         }
         else
             gtk_tooltip_set_text (tooltip, NULL);
+        gtk_tree_path_free (path);
     }
     return FALSE;
 }
diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 924db4c25..c57221615 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -945,19 +945,31 @@ query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
 
     if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
                                                         &column, NULL, NULL))
+    {
+        gtk_tree_path_free (path);
         return FALSE;
+    }
 
     if (!column)
+    {
+        gtk_tree_path_free (path);
         return FALSE;
+    }
 
     period_num = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(column), "period_num"));
     if (!period_num && priv->period_col_list->data != column)
+    {
+        gtk_tree_path_free (path);
         return FALSE;
+    }
     account = gnc_tree_view_account_get_account_from_path (
                   GNC_TREE_VIEW_ACCOUNT(widget), path);
     note = gnc_budget_get_account_period_note (priv->budget, account, period_num);
     if (!note)
+    {
+        gtk_tree_path_free (path);
         return FALSE;
+    }
 
     gtk_tooltip_set_text (tooltip, note);
     gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, NULL);
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index ae4ae4d10..dca451efb 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -1898,7 +1898,10 @@ query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y,
     gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
     if (keyboard_tip || !gtk_tree_view_get_path_at_pos (tree_view, x, y, &path,
                                                         &column, NULL, NULL))
+    {
+        gtk_tree_path_free (path);
         return FALSE;
+    }
 
     // Get the iter pointing to our current column
     if (gtk_tree_model_get_iter(model, &iter, path) && column)

commit 8a48ed977750798dfc0fd49a243a593347d256c5
Merge: 57f73d70c 001b34616
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 30 08:43:13 2021 +0800

    Merge branch 'maint-cache-gvalue-string' into maint #1117
    
    Will store a copy of kvp data onto struct so that GValue can be
    freed (unset) cleanly.


commit 001b34616f0b0b9450ae55ca0effab4ae9f55c5a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Aug 24 21:38:16 2021 +0800

    [Transaction.c] GValue string must be unset

diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c
index 4890c2f01..7f12f80a2 100644
--- a/libgnucash/engine/Transaction.c
+++ b/libgnucash/engine/Transaction.c
@@ -255,6 +255,9 @@ void gen_event_trans (Transaction *trans)
     }
 }
 
+static const char*
+is_unset = "unset";
+
 /* GObject Initialization */
 G_DEFINE_TYPE(Transaction, gnc_transaction, QOF_TYPE_INSTANCE)
 
@@ -274,6 +277,9 @@ gnc_transaction_init(Transaction* trans)
     trans->readonly_reason = NULL;
     trans->reason_cache_valid = FALSE;
     trans->isClosingTxn_cached = -1;
+    trans->notes = (char*) is_unset;
+    trans->doclink = (char*) is_unset;
+    trans->void_reason = (char*) is_unset;
     LEAVE (" ");
 }
 
@@ -813,6 +819,12 @@ xaccFreeTransaction (Transaction *trans)
     CACHE_REMOVE(trans->num);
     CACHE_REMOVE(trans->description);
     g_free (trans->readonly_reason);
+    if (trans->doclink != is_unset)
+        g_free (trans->doclink);
+    if (trans->void_reason != is_unset)
+        g_free (trans->void_reason);
+    if (trans->notes != is_unset)
+        g_free (trans->notes);
 
     /* Just in case someone looks up freed memory ... */
     trans->num         = (char *) 1;
@@ -821,6 +833,9 @@ xaccFreeTransaction (Transaction *trans)
     trans->date_posted = 0;
     trans->readonly_reason = NULL;
     trans->reason_cache_valid = FALSE;
+    trans->doclink = NULL;
+    trans->notes = NULL;
+    trans->void_reason = NULL;
     if (trans->orig)
     {
         xaccFreeTransaction (trans->orig);
@@ -2189,12 +2204,24 @@ void
 xaccTransSetDocLink (Transaction *trans, const char *doclink)
 {
     if (!trans || !doclink) return;
+
+    if (trans->doclink != is_unset)
+    {
+        if (!g_strcmp0 (doclink, trans->doclink))
+            return;
+
+        g_free (trans->doclink);
+    }
     xaccTransBeginEdit(trans);
-    if (g_strcmp0 (doclink, "") == 0)
+    if (doclink[0] == '\0')
+    {
+        trans->doclink = NULL;
         qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, doclink_uri_str);
+    }
     else
     {
         GValue v = G_VALUE_INIT;
+        trans->doclink = g_strdup (doclink);
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, doclink);
         qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, doclink_uri_str);
@@ -2217,10 +2244,18 @@ xaccTransSetNotes (Transaction *trans, const char *notes)
 {
     GValue v = G_VALUE_INIT;
     if (!trans || !notes) return;
+    if (trans->notes != is_unset)
+    {
+        if (!g_strcmp0 (notes, trans->notes))
+            return;
+
+        g_free (trans->notes);
+    }
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, notes);
     xaccTransBeginEdit(trans);
 
+    trans->notes = g_strdup (notes);
     qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     g_value_unset (&v);
@@ -2382,23 +2417,31 @@ xaccTransGetDescription (const Transaction *trans)
 const char *
 xaccTransGetDocLink (const Transaction *trans)
 {
-    GValue v = G_VALUE_INIT;
-    if (!trans) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, doclink_uri_str);
-    if (G_VALUE_HOLDS_STRING (&v))
-         return g_value_get_string (&v);
-    return NULL;
+    g_return_val_if_fail (trans, NULL);
+    if (trans->doclink == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        Transaction *t = (Transaction*) trans;
+        qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, doclink_uri_str);
+        t->doclink = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return trans->doclink;
 }
 
 const char *
 xaccTransGetNotes (const Transaction *trans)
 {
-    GValue v = G_VALUE_INIT;
-    if (!trans) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
-    if (G_VALUE_HOLDS_STRING (&v))
-         return g_value_get_string (&v);
-    return NULL;
+    g_return_val_if_fail (trans, NULL);
+    if (trans->notes == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        Transaction *t = (Transaction*) trans;
+        qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+        t->notes = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return trans->notes;
 }
 
 gboolean
@@ -2746,6 +2789,9 @@ xaccTransVoid(Transaction *trans, const char *reason)
     qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     g_value_set_string (&v, reason);
     qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+    if (trans->void_reason != is_unset)
+        g_free (trans->void_reason);
+    trans->void_reason = g_strdup (reason);
 
     gnc_time64_to_iso8601_buff (gnc_time(NULL), iso8601_str);
     g_value_set_string (&v, iso8601_str);
@@ -2762,31 +2808,23 @@ xaccTransVoid(Transaction *trans, const char *reason)
 gboolean
 xaccTransGetVoidStatus(const Transaction *trans)
 {
-    const char *s = NULL;
-    GValue v = G_VALUE_INIT;
-    gboolean retval = FALSE;
-    g_return_val_if_fail(trans, FALSE);
-
-    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
-    if (G_VALUE_HOLDS_STRING (&v))
-    {
-         s = g_value_get_string (&v);
-         retval = (s && (s[0] != '\0'));
-    }
-    g_value_unset (&v);
-    return retval;
+    const char *s = xaccTransGetVoidReason (trans);
+    return (s && *s);
 }
 
 const char *
 xaccTransGetVoidReason(const Transaction *trans)
 {
-    GValue v = G_VALUE_INIT;
-    g_return_val_if_fail(trans, FALSE);
-
-    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
-    if (G_VALUE_HOLDS_STRING (&v))
-         return g_value_get_string (&v);
-    return NULL;
+    g_return_val_if_fail (trans, NULL);
+    if (trans->void_reason == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        Transaction *t = (Transaction*) trans;
+        qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+        t->void_reason = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return trans->void_reason;
 }
 
 time64
@@ -2815,10 +2853,7 @@ xaccTransUnvoid (Transaction *trans)
     const char *s = NULL;
     g_return_if_fail(trans);
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
-    if (G_VALUE_HOLDS_STRING (&v))
-        s = g_value_get_string (&v);
-    g_value_unset (&v);
+    s = xaccTransGetVoidReason (trans);
     if (s == NULL) return; /* Transaction isn't voided. Bail. */
     xaccTransBeginEdit(trans);
 
@@ -2829,6 +2864,8 @@ xaccTransUnvoid (Transaction *trans)
     qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, void_reason_str);
     qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, void_time_str);
     g_value_unset (&v);
+    g_free (trans->void_reason);
+    trans->void_reason = NULL;
 
     FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
 
diff --git a/libgnucash/engine/TransactionP.h b/libgnucash/engine/TransactionP.h
index c42f988c1..f7c455381 100644
--- a/libgnucash/engine/TransactionP.h
+++ b/libgnucash/engine/TransactionP.h
@@ -120,6 +120,10 @@ struct transaction_s
     char * readonly_reason;
     gboolean reason_cache_valid;
 
+    char * doclink;
+    char * void_reason;
+    char * notes;
+
     /* Cached bool value to indicate whether this is a closing txn. This is
      * cached from the KVP value because it is queried a lot. Tri-state value: -1
      * = uninitialized; 0 = FALSE, 1 = TRUE. */

commit a47bee97d634204a742e05e02c18c5ad5107ff00
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 23 23:22:06 2021 +0800

    [Split.c] GValue string must be unset

diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c
index 9392ddd33..d771e1f8b 100644
--- a/libgnucash/engine/Split.c
+++ b/libgnucash/engine/Split.c
@@ -97,6 +97,10 @@ enum
 
 };
 
+static const char * is_unset = "unset";
+static const char * split_type_normal = "normal";
+static const char * split_type_stock_split = "stock-split";
+
 /* GObject Initialization */
 G_DEFINE_TYPE(Split, gnc_split, QOF_TYPE_INSTANCE)
 
@@ -116,6 +120,7 @@ gnc_split_init(Split* split)
     split->value       = gnc_numeric_zero();
 
     split->date_reconciled  = 0;
+    split->split_type = is_unset;
 
     split->balance             = gnc_numeric_zero();
     split->cleared_balance     = gnc_numeric_zero();
@@ -714,6 +719,7 @@ xaccFreeSplit (Split *split)
     split->lot         = NULL;
     split->acc         = NULL;
     split->orig_acc    = NULL;
+    split->split_type  = NULL;
 
     split->date_reconciled = 0;
     G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split));
@@ -1968,14 +1974,26 @@ xaccSplitGetBook (const Split *split)
 const char *
 xaccSplitGetType(const Split *s)
 {
-    GValue v = G_VALUE_INIT;
-    const char *split_type = NULL;
-
     if (!s) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
-    if (G_VALUE_HOLDS_STRING (&v))
-        split_type = g_value_get_string (&v);
-    return split_type ? split_type : "normal";
+    if (s->split_type == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        Split *split = (Split*) s;
+        const char* type;
+        qof_instance_get_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
+        type = G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
+        if (!type || !g_strcmp0 (type, split_type_normal))
+            split->split_type = (char*) split_type_normal;
+        else if (!g_strcmp0 (type, split_type_stock_split))
+            split->split_type = (char*) split_type_stock_split;
+        else
+        {
+            PERR ("unexpected split-type %s, reset to normal.", type);
+            split->split_type = split_type_normal;
+        }
+        g_value_unset (&v);
+    }
+    return s->split_type;
 }
 
 /* reconfigure a split to be a stock split - after this, you shouldn't
@@ -1988,7 +2006,8 @@ xaccSplitMakeStockSplit(Split *s)
 
     s->value = gnc_numeric_zero();
     g_value_init (&v, G_TYPE_STRING);
-    g_value_set_string (&v, "stock-split");
+    g_value_set_static_string (&v, split_type_stock_split);
+    s->split_type = split_type_stock_split;
     qof_instance_set_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
     SET_GAINS_VDIRTY(s);
     mark_split(s);
diff --git a/libgnucash/engine/SplitP.h b/libgnucash/engine/SplitP.h
index 335db9174..4192cc5dd 100644
--- a/libgnucash/engine/SplitP.h
+++ b/libgnucash/engine/SplitP.h
@@ -115,6 +115,8 @@ struct split_s
     gnc_numeric  value;
     gnc_numeric  amount;
 
+    const gchar * split_type;
+
     /* -------------------------------------------------------------- */
     /* Below follow some 'temporary' fields */
 

commit 0ecbcb4ef1701732872a205779633e3e36160bbf
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 23 23:21:46 2021 +0800

    [gnc-lot.c] GValue string must be unset

diff --git a/libgnucash/engine/gnc-lot.c b/libgnucash/engine/gnc-lot.c
index d1b411782..a8eaf7902 100644
--- a/libgnucash/engine/gnc-lot.c
+++ b/libgnucash/engine/gnc-lot.c
@@ -86,6 +86,9 @@ typedef struct GNCLotPrivate
     /* List of splits that belong to this lot. */
     SplitList *splits;
 
+    char *title;
+    char *notes;
+
     GncInvoice *cached_invoice;
     /* Handy cached value to indicate if lot is closed. */
     /* If value is negative, then the cache is invalid. */
@@ -103,6 +106,9 @@ typedef struct GNCLotPrivate
 
 /* ============================================================= */
 
+static char*
+is_unset = "unset";
+
 /* GObject Initialization */
 G_DEFINE_TYPE_WITH_PRIVATE(GNCLot, gnc_lot, QOF_TYPE_INSTANCE)
 
@@ -116,6 +122,8 @@ gnc_lot_init(GNCLot* lot)
     priv->splits = NULL;
     priv->cached_invoice = NULL;
     priv->is_closed = LOT_CLOSED_UNKNOWN;
+    priv->title = is_unset;
+    priv->notes = is_unset;
     priv->marker = 0;
 }
 
@@ -295,6 +303,14 @@ gnc_lot_free(GNCLot* lot)
     if (priv->account && !qof_instance_get_destroying(priv->account))
         xaccAccountRemoveLot (priv->account, lot);
 
+    if (priv->notes != is_unset)
+        g_free (priv->notes);
+
+    if (priv->title != is_unset)
+        g_free (priv->title);
+
+    priv->notes = NULL;
+    priv->title = NULL;
     priv->account = NULL;
     priv->is_closed = TRUE;
     /* qof_instance_release (&lot->inst); */
@@ -439,30 +455,46 @@ gint gnc_lot_count_splits (const GNCLot *lot)
 const char *
 gnc_lot_get_title (const GNCLot *lot)
 {
-    GValue v = G_VALUE_INIT;
+    GNCLotPrivate* priv;
     if (!lot) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "title");
-    if (G_VALUE_HOLDS_STRING (&v))
-        return g_value_get_string (&v);
-    return NULL;
+    priv = GET_PRIVATE (lot);
+    if (priv->title == is_unset)
+    {
+        GNCLotPrivate* priv = GET_PRIVATE (lot);
+        GValue v = G_VALUE_INIT;
+        qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "title");
+        priv->title = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return priv->title;
 }
 
 const char *
 gnc_lot_get_notes (const GNCLot *lot)
 {
-    GValue v = G_VALUE_INIT;
+    GNCLotPrivate* priv;
     if (!lot) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
-    if (G_VALUE_HOLDS_STRING (&v))
-        return g_value_get_string (&v);
-    return NULL;
+    priv = GET_PRIVATE (lot);
+    if (priv->notes == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
+        priv->notes = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return priv->notes;
 }
 
 void
 gnc_lot_set_title (GNCLot *lot, const char *str)
 {
     GValue v = G_VALUE_INIT;
+    GNCLotPrivate* priv;
     if (!lot) return;
+    priv = GET_PRIVATE (lot);
+    if (priv->title != is_unset)
+        g_free (priv->title);
+
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
@@ -476,7 +508,11 @@ void
 gnc_lot_set_notes (GNCLot *lot, const char *str)
 {
     GValue v = G_VALUE_INIT;
+    GNCLotPrivate* priv;
     if (!lot) return;
+    priv = GET_PRIVATE (lot);
+    if (priv->notes != is_unset)
+        g_free (priv->notes);
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);

commit ac2afc7e13babd4c850e3831318a705f9996feef
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 23 18:44:23 2021 +0800

    [gncInvoice.c] GValue string must be unset

diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index 6ab155f81..f3a5c5db1 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -65,6 +65,8 @@ struct _gncInvoice
     time64        date_opened;
     time64        date_posted;
 
+    char          *doclink;
+
     gnc_numeric   to_charge_amount;
 
     gnc_commodity *currency;
@@ -282,6 +284,9 @@ impl_get_typed_referring_object_list (const QofInstance* inst, const QofInstance
     return qof_instance_get_referring_object_list_from_collection (qof_instance_get_collection (inst), ref);
 }
 
+static const char*
+is_unset = "unset";
+
 static void
 gnc_invoice_class_init (GncInvoiceClass *klass)
 {
@@ -327,6 +332,7 @@ GncInvoice *gncInvoiceCreate (QofBook *book)
     invoice->active = TRUE;
 
     invoice->to_charge_amount = gnc_numeric_zero ();
+    invoice->doclink = (char*) is_unset;
 
     qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL);
 
@@ -372,6 +378,8 @@ GncInvoice *gncInvoiceCopy (const GncInvoice *from)
     // Oops. Do not forget to copy the pointer to the correct currency here.
     invoice->currency = from->currency;
 
+    invoice->doclink = from->doclink;
+
     // Copy all invoice->entries
     for (node = from->entries; node; node = node->next)
     {
@@ -428,6 +436,9 @@ static void gncInvoiceFree (GncInvoice *invoice)
     if (invoice->terms)
         gncBillTermDecRef (invoice->terms);
 
+    if (invoice->doclink != is_unset)
+        g_free (invoice->doclink);
+
     /* qof_instance_release (&invoice->inst); */
     g_object_unref (invoice);
 }
@@ -538,15 +549,29 @@ void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
 void gncInvoiceSetDocLink (GncInvoice *invoice, const char *doclink)
 {
     if (!invoice || !doclink) return;
+
+    if (invoice->doclink != is_unset)
+    {
+        if (!g_strcmp0 (doclink, invoice->doclink))
+            return;
+
+        g_free (invoice->doclink);
+    }
+
     gncInvoiceBeginEdit (invoice);
-    if (g_strcmp0 (doclink, "") == 0)
+
+    if (doclink[0] == '\0')
+    {
+        invoice->doclink = NULL;
         qof_instance_set_kvp (QOF_INSTANCE (invoice), NULL, 1, GNC_INVOICE_DOCLINK);
+    }
     else
     {
         GValue v = G_VALUE_INIT;
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, doclink);
         qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_DOCLINK);
+        invoice->doclink = g_strdup (doclink);
         g_value_unset (&v);
     }
     qof_instance_set_dirty (QOF_INSTANCE(invoice));
@@ -864,12 +889,16 @@ const char * gncInvoiceGetNotes (const GncInvoice *invoice)
 
 const char * gncInvoiceGetDocLink (const GncInvoice *invoice)
 {
-    GValue v = G_VALUE_INIT;
     if (!invoice) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_DOCLINK);
-    if (G_VALUE_HOLDS_STRING(&v))
-         return g_value_get_string (&v);
-    return NULL;
+    if (invoice->doclink == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        GncInvoice *inv = (GncInvoice*) invoice;
+        qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_DOCLINK);
+        inv->doclink = G_VALUE_HOLDS_STRING(&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return invoice->doclink;
 }
 
 GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice)

commit 3bf49ed8d7c806a550d07fc2d803b64c2e78f196
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Aug 23 18:05:08 2021 +0800

    [Account.cpp] GValue string must be unset

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index ba0942758..14e4dfd4b 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -286,6 +286,8 @@ mark_account (Account *acc)
 /********************************************************************\
 \********************************************************************/
 
+static constexpr const char* is_unset {"unset"};
+
 /* GObject Initialization */
 G_DEFINE_TYPE_WITH_PRIVATE(Account, gnc_account, QOF_TYPE_INSTANCE)
 
@@ -323,6 +325,13 @@ gnc_account_init(Account* acc)
     priv->starting_reconciled_balance = gnc_numeric_zero();
     priv->balance_dirty = FALSE;
 
+    priv->color == is_unset;
+    priv->sort_order == is_unset;
+    priv->notes == is_unset;
+    priv->filter == is_unset;
+    priv->equity_type == TriState::Unset;
+    priv->sort_reversed == TriState::Unset;
+
     priv->splits = NULL;
     priv->sort_dirty = FALSE;
 }
@@ -1365,9 +1374,23 @@ xaccFreeAccount (Account *acc)
     qof_string_cache_remove(priv->description);
     priv->accountName = priv->accountCode = priv->description = nullptr;
 
+    if (priv->color != is_unset)
+        g_free (priv->color);
+    if (priv->sort_order != is_unset)
+        g_free (priv->sort_order);
+    if (priv->notes != is_unset)
+        g_free (priv->notes);
+    if (priv->filter != is_unset)
+        g_free (priv->filter);
+
     /* zero out values, just in case stray
      * pointers are pointing here. */
 
+    priv->color == nullptr;
+    priv->sort_order == nullptr;
+    priv->notes == nullptr;
+    priv->filter == nullptr;
+
     priv->parent = nullptr;
     priv->children = nullptr;
 
@@ -2476,36 +2499,52 @@ set_kvp_string_tag (Account *acc, const char *tag, const char *value)
     xaccAccountCommitEdit(acc);
 }
 
-static const char*
+static char*
 get_kvp_string_tag (const Account *acc, const char *tag)
 {
     GValue v = G_VALUE_INIT;
     if (acc == NULL || tag == NULL) return NULL;
     qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v, {tag});
-    return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
+    auto retval = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+    g_value_unset (&v);
+    return retval;
 }
 
 void
 xaccAccountSetColor (Account *acc, const char *str)
 {
+    auto priv = GET_PRIVATE (acc);
+    if (priv->color != is_unset)
+        g_free (priv->color);
+    priv->color = g_strdup (str);
     set_kvp_string_tag (acc, "color", str);
 }
 
 void
 xaccAccountSetFilter (Account *acc, const char *str)
 {
+    auto priv = GET_PRIVATE (acc);
+    if (priv->filter != is_unset)
+        g_free (priv->filter);
+    priv->filter = g_strdup (str);
     set_kvp_string_tag (acc, "filter", str);
 }
 
 void
 xaccAccountSetSortOrder (Account *acc, const char *str)
 {
+    auto priv = GET_PRIVATE (acc);
+    if (priv->sort_order != is_unset)
+        g_free (priv->sort_order);
+    priv->sort_order = g_strdup (str);
     set_kvp_string_tag (acc, "sort-order", str);
 }
 
 void
 xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
 {
+    auto priv = GET_PRIVATE (acc);
+    priv->sort_reversed = sortreversed ? TriState::True : TriState::False;
     set_kvp_string_tag (acc, "sort-reversed", sortreversed ? "true" : NULL);
 }
 
@@ -2530,6 +2569,10 @@ qofAccountSetParent (Account *acc, QofInstance *parent)
 void
 xaccAccountSetNotes (Account *acc, const char *str)
 {
+    auto priv = GET_PRIVATE (acc);
+    if (priv->notes != is_unset)
+        g_free (priv->notes);
+    priv->notes = g_strdup (str);
     set_kvp_string_tag (acc, "notes", str);
 }
 
@@ -3250,21 +3293,30 @@ const char *
 xaccAccountGetColor (const Account *acc)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
-    return get_kvp_string_tag (acc, "color");
+    auto priv = GET_PRIVATE (acc);
+    if (priv->color == is_unset)
+        priv->color = get_kvp_string_tag (acc, "color");
+    return priv->color;
 }
 
 const char *
 xaccAccountGetFilter (const Account *acc)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
-    return get_kvp_string_tag (acc, "filter");
+    auto priv = GET_PRIVATE (acc);
+    if (priv->filter == is_unset)
+        priv->filter = get_kvp_string_tag (acc, "filter");
+    return priv->filter;
 }
 
 const char *
 xaccAccountGetSortOrder (const Account *acc)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
-    return get_kvp_string_tag (acc, "sort-order");
+    auto priv = GET_PRIVATE (acc);
+    if (priv->sort_order == is_unset)
+        priv->sort_order = get_kvp_string_tag (acc, "sort-order");
+    return priv->sort_order;
 }
 
 gboolean
@@ -3272,14 +3324,25 @@ xaccAccountGetSortReversed (const Account *acc)
 {
 
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    return g_strcmp0 (get_kvp_string_tag (acc, "sort-reversed"), "true") == 0;
+    auto priv = GET_PRIVATE (acc);
+    if (priv->sort_reversed == TriState::Unset)
+    {
+        auto sort_reversed = get_kvp_string_tag (acc, "sort-reversed");
+        priv->sort_reversed = g_strcmp0 (sort_reversed, "true") ?
+            TriState::False : TriState::True;
+        g_free (sort_reversed);
+    }
+    return (priv->sort_reversed == TriState::True);
 }
 
 const char *
 xaccAccountGetNotes (const Account *acc)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), NULL);
-    return get_kvp_string_tag (acc, "notes");
+    auto priv = GET_PRIVATE (acc);
+    if (priv->notes == is_unset)
+        priv->notes = get_kvp_string_tag (acc, "notes");
+    return priv->notes;
 }
 
 gnc_commodity *
@@ -4142,7 +4205,15 @@ xaccAccountGetIsOpeningBalance (const Account *acc)
 {
     if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
         return false;
-    return g_strcmp0(get_kvp_string_tag(acc, "equity-type"), "opening-balance") == 0;
+    auto priv = GET_PRIVATE(acc);
+    if (priv->equity_type == TriState::Unset)
+    {
+        auto equity_type = get_kvp_string_tag (acc, "equity-type");
+        priv->equity_type = g_strcmp0 (equity_type, "true") ?
+            TriState::False : TriState::True;
+        g_free (equity_type);
+    }
+    return (priv->equity_type == TriState::True);
 }
 
 void
@@ -4150,6 +4221,8 @@ xaccAccountSetIsOpeningBalance (Account *acc, gboolean val)
 {
     if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
         return;
+    auto priv = GET_PRIVATE (acc);
+    priv->equity_type = val ? TriState::True : TriState::False;
     set_kvp_string_tag(acc, "equity-type", val ? "opening-balance" : "");
 }
 
diff --git a/libgnucash/engine/AccountP.h b/libgnucash/engine/AccountP.h
index 746a1c504..250232206 100644
--- a/libgnucash/engine/AccountP.h
+++ b/libgnucash/engine/AccountP.h
@@ -55,6 +55,13 @@ extern "C" {
  * No one outside of the engine should ever include this file.
 */
 
+typedef enum
+{
+    Unset = -1,
+    False,
+    True
+} TriState;
+
 /** \struct Account */
 typedef struct AccountPrivate
 {
@@ -122,6 +129,13 @@ typedef struct AccountPrivate
     LotList   *lots;		/* list of lot pointers */
     GNCPolicy *policy;		/* Cached pointer to policy method */
 
+    TriState sort_reversed;
+    TriState equity_type;
+    char *notes;
+    char *color;
+    char *sort_order;
+    char *filter;
+
     /* The "mark" flag can be used by the user to mark this account
      * in any way desired.  Handy for specialty traversals of the
      * account tree. */

commit e3af2f22f9aec70131dc2da8d9c9eceade1a52ad
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Aug 22 11:17:28 2021 +0800

    [gnc-commodity.c] cache user_symbol into commodity struct
    
    Continuation of ff2ceb111 which introduced issue whereby user_symbol
    returned could become stale, leading to invalid read fixed with
    c398bef59. There are likely other user_symbol pointers becoming stale
    without this commit.
    
    This change will save the user_symbol into the commodity struct,
    avoids gchar* becoming stale.

diff --git a/libgnucash/engine/gnc-commodity.c b/libgnucash/engine/gnc-commodity.c
index 56763e7a0..745be68bc 100644
--- a/libgnucash/engine/gnc-commodity.c
+++ b/libgnucash/engine/gnc-commodity.c
@@ -76,6 +76,7 @@ typedef struct gnc_commodityPrivate
     const char *cusip;                /* CUSIP or other identifying code */
     int         fraction;
     char       *unique_name;
+    char       *user_symbol;
 
     gboolean    quote_flag;	      /* user wants price quotes */
     gnc_quote_source *quote_source;   /* current/old source of quotes */
@@ -89,6 +90,9 @@ typedef struct gnc_commodityPrivate
     const char *default_symbol;
 } gnc_commodityPrivate;
 
+static const char*
+is_unset = "unset";
+
 #define GET_PRIVATE(o) \
     ((gnc_commodityPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_COMMODITY))
 
@@ -667,6 +671,7 @@ gnc_commodity_init(gnc_commodity* com)
     priv->quote_flag = 0;
     priv->quote_source = NULL;
     priv->quote_tz = CACHE_INSERT("");
+    priv->user_symbol = (char*) is_unset;
 
     reset_printname(priv);
     reset_unique_name(priv);
@@ -951,6 +956,10 @@ commodity_free(gnc_commodity * cm)
     g_free(priv->unique_name);
     priv->unique_name = NULL;
 
+    if (priv->user_symbol != is_unset)
+        g_free (priv->user_symbol);
+    priv->user_symbol = NULL;
+
 #ifdef ACCOUNTS_CLEANED_UP
     /* Account objects are not actually cleaned up when a book is closed (in fact
      * a memory leak), but commodities are, so in currently this warning gets hit
@@ -1182,14 +1191,17 @@ gnc_commodity_get_quote_tz(const gnc_commodity *cm)
 const char*
 gnc_commodity_get_user_symbol(const gnc_commodity *cm)
 {
-    GValue v = G_VALUE_INIT;
-    static char* retval = NULL;
-    if (!cm) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
-    g_free (retval);
-    retval = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v): NULL;
-    g_value_unset (&v);
-    return retval;
+    gnc_commodityPrivate* priv;
+    g_return_val_if_fail (GNC_IS_COMMODITY (cm), NULL);
+    priv = GET_PRIVATE(cm);
+    if (priv->user_symbol == is_unset)
+    {
+        GValue v = G_VALUE_INIT;
+        qof_instance_get_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
+        priv->user_symbol = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
+        g_value_unset (&v);
+    }
+    return priv->user_symbol;
 }
 
 /********************************************************************
@@ -1477,13 +1489,13 @@ void
 gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
 {
     struct lconv *lc;
-    GValue v = G_VALUE_INIT;
+    gnc_commodityPrivate* priv;
+
     if (!cm) return;
+    priv = GET_PRIVATE(cm);
 
     ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
 
-    gnc_commodity_begin_edit(cm);
-
     lc = gnc_localeconv();
     if (!user_symbol || !*user_symbol)
 	user_symbol = NULL;
@@ -1494,15 +1506,33 @@ gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
 	user_symbol = NULL;
     else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
 	user_symbol = NULL;
+
+    if (priv->user_symbol != is_unset)
+    {
+        if (!g_strcmp0 (user_symbol, priv->user_symbol))
+        {
+            LEAVE ("gnc_commodity_set_user_symbol: no change");
+            return;
+        }
+        g_free (priv->user_symbol);
+    }
+
+    gnc_commodity_begin_edit (cm);
+
     if (user_symbol)
     {
+        GValue v = G_VALUE_INIT;
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, user_symbol);
         qof_instance_set_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
+        priv->user_symbol = g_strdup (user_symbol);
         g_value_unset (&v);
     }
     else
+    {
         qof_instance_set_kvp (QOF_INSTANCE(cm), NULL, 1, "user_symbol");
+        priv->user_symbol = NULL;
+    }
 
     mark_commodity_dirty(cm);
     gnc_commodity_commit_edit(cm);

commit 57f73d70c725652e6acaf67c2a4a2be8c2ef027a
Merge: b42052464 6cb509ea7
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 27 12:34:07 2021 -0700

    Merge Yordan Miladinov's denom_lcd into maint.


commit 6cb509ea7d247a565a057359f080fcd0a52a27c1
Author: Yordan Miladinov <jordanmiladinov at gmail.com>
Date:   Tue Aug 24 00:58:31 2021 +0300

    fix an uncaught exception: denom_lcd may throw an overflow exception if casting lcm to int64_t fails

diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp
index c1142a546..277e01e0f 100644
--- a/libgnucash/engine/gnc-numeric.cpp
+++ b/libgnucash/engine/gnc-numeric.cpp
@@ -756,9 +756,9 @@ gnc_numeric_add(gnc_numeric a, gnc_numeric b,
     {
         return gnc_numeric_error(GNC_ERROR_ARG);
     }
-    denom = denom_lcd(a, b, denom, how);
     try
     {
+        denom = denom_lcd(a, b, denom, how);
         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
         {
             GncNumeric an (a), bn (b);
@@ -810,9 +810,9 @@ gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
     {
         return gnc_numeric_error(GNC_ERROR_ARG);
     }
-    denom = denom_lcd(a, b, denom, how);
     try
     {
+        denom = denom_lcd(a, b, denom, how);
         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
         {
             GncNumeric an (a), bn (b);
@@ -863,9 +863,10 @@ gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
     {
         return gnc_numeric_error(GNC_ERROR_ARG);
     }
-    denom = denom_lcd(a, b, denom, how);
+
     try
     {
+        denom = denom_lcd(a, b, denom, how);
         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
         {
             GncNumeric an (a), bn (b);
@@ -917,9 +918,9 @@ gnc_numeric_div(gnc_numeric a, gnc_numeric b,
     {
         return gnc_numeric_error(GNC_ERROR_ARG);
     }
-    denom = denom_lcd(a, b, denom, how);
     try
     {
+        denom = denom_lcd(a, b, denom, how);
         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
         {
             GncNumeric an (a), bn (b);



Summary of changes:
 gnucash/gnome-search/gnc-general-search.c          |   1 +
 gnucash/gnome-utils/dialog-object-references.c     |   1 +
 gnucash/gnome-utils/gnc-account-sel.c              |   7 +-
 gnucash/gnome-utils/gnc-dense-cal.c                |   1 +
 gnucash/gnome/assistant-hierarchy.c                |   2 +
 gnucash/gnome/dialog-custom-report.c               |   6 +
 gnucash/gnome/dialog-print-check.c                 |   2 +
 gnucash/gnome/dialog-report-style-sheet.c          |   2 +-
 gnucash/gnome/gnc-budget-view.c                    |  13 ++
 .../csv-imp/assistant-csv-account-import.c         |   1 +
 .../csv-imp/assistant-csv-price-import.cpp         |   4 +
 .../csv-imp/assistant-csv-trans-import.cpp         |   2 +
 gnucash/import-export/import-main-matcher.c        |   8 +-
 gnucash/price-quotes.scm                           |   8 +-
 gnucash/register/register-gnome/combocell-gnome.c  |   6 +
 libgnucash/engine/Account.cpp                      | 131 +++++++++++++++++----
 libgnucash/engine/AccountP.h                       |  14 +++
 libgnucash/engine/Split.c                          |  35 ++++--
 libgnucash/engine/SplitP.h                         |   2 +
 libgnucash/engine/Transaction.c                    | 111 +++++++++++------
 libgnucash/engine/TransactionP.h                   |   4 +
 libgnucash/engine/gnc-commodity.c                  |  52 ++++++--
 libgnucash/engine/gnc-lot.c                        |  58 +++++++--
 libgnucash/engine/gnc-numeric.cpp                  |   9 +-
 libgnucash/engine/gncInvoice.c                     |  41 ++++++-
 libgnucash/engine/test/test-commodities.cpp        |   5 +
 libgnucash/engine/test/test-lots.cpp               |  46 ++++++++
 libgnucash/engine/test/utest-Account.cpp           | 100 ++++++++++++++++
 libgnucash/engine/test/utest-Invoice.c             |  22 ++++
 libgnucash/engine/test/utest-Transaction.cpp       |  21 ++++
 po/he.po                                           |  28 ++---
 po/it.po                                           |   4 +-
 po/zh_CN.po                                        |  26 ++--
 33 files changed, 639 insertions(+), 134 deletions(-)



More information about the gnucash-changes mailing list