From 2774f8f88c7cb84b2ef3e2f2735bbca84f17a91a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 11 Sep 2022 10:35:43 -0700 Subject: [PATCH 1/9] Bug 798614 - Croatia to join the Euro Add HRK to the conversion array. --- libgnucash/app-utils/gnc-euro.c | 1 + libgnucash/engine/iso-4217-currencies.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/libgnucash/app-utils/gnc-euro.c b/libgnucash/app-utils/gnc-euro.c index b541100df0..86bf98e54a 100644 --- a/libgnucash/app-utils/gnc-euro.c +++ b/libgnucash/app-utils/gnc-euro.c @@ -59,6 +59,7 @@ static gnc_euro_rate_struct gnc_euro_rates[] = { "FRF", 6.55957 }, /* french franc */ { "GRD", 340.750 }, /* greek drachma */ { "HFL", 2.20371 }, /* netherland gulden */ + { "HRK", 7.53450 }, /* Croatian kuna */ { "IEP", .787564 }, /* irish pound */ { "IRP", .787564 }, /* irish pound */ { "ITL", 1936.27 }, /* italian lira */ diff --git a/libgnucash/engine/iso-4217-currencies.xml b/libgnucash/engine/iso-4217-currencies.xml index 93c22311f1..2aa62d1362 100644 --- a/libgnucash/engine/iso-4217-currencies.xml +++ b/libgnucash/engine/iso-4217-currencies.xml @@ -1195,6 +1195,7 @@ local-symbol="L" /> Date: Sun, 11 Sep 2022 16:29:57 -0700 Subject: [PATCH 2/9] Move gnc-euro.[ch] to engine and unit test it. --- libgnucash/app-utils/CMakeLists.txt | 2 - libgnucash/engine/CMakeLists.txt | 2 + libgnucash/{app-utils => engine}/gnc-euro.c | 13 +- libgnucash/{app-utils => engine}/gnc-euro.h | 0 libgnucash/engine/test/CMakeLists.txt | 4 + libgnucash/engine/test/gtest-gnc-euro.cpp | 228 ++++++++++++++++++++ po/POTFILES.in | 2 +- 7 files changed, 241 insertions(+), 10 deletions(-) rename libgnucash/{app-utils => engine}/gnc-euro.c (95%) rename libgnucash/{app-utils => engine}/gnc-euro.h (100%) create mode 100644 libgnucash/engine/test/gtest-gnc-euro.cpp diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 9a511ed35d..6a5714847c 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -23,7 +23,6 @@ set (app_utils_HEADERS gnc-accounting-period.h gnc-addr-quickfill.h gnc-entry-quickfill.h - gnc-euro.h gnc-exp-parser.h gnc-gsettings.h gnc-help-utils.h @@ -60,7 +59,6 @@ set (app_utils_SOURCES gnc-accounting-period.c gnc-addr-quickfill.c gnc-entry-quickfill.c - gnc-euro.c gnc-exp-parser.c gnc-gsettings.cpp gnc-helpers.c diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt index 8dde8461bc..33f9f3d2d8 100644 --- a/libgnucash/engine/CMakeLists.txt +++ b/libgnucash/engine/CMakeLists.txt @@ -49,6 +49,7 @@ set (engine_HEADERS gnc-date.h gnc-datetime.hpp gnc-engine.h + gnc-euro.h gnc-event.h gnc-features.h gnc-hooks.h @@ -144,6 +145,7 @@ set (engine_SOURCES gnc-date.cpp gnc-datetime.cpp gnc-engine.c + gnc-euro.c gnc-event.c gnc-features.c gnc-hooks.c diff --git a/libgnucash/app-utils/gnc-euro.c b/libgnucash/engine/gnc-euro.c similarity index 95% rename from libgnucash/app-utils/gnc-euro.c rename to libgnucash/engine/gnc-euro.c index 86bf98e54a..1a20e14597 100644 --- a/libgnucash/app-utils/gnc-euro.c +++ b/libgnucash/engine/gnc-euro.c @@ -27,8 +27,7 @@ #include #include #include - -#include "gnc-ui-util.h" +#include "gnc-session.h" /* local structs */ typedef struct @@ -39,7 +38,7 @@ typedef struct /* This array MUST be sorted ! */ -/* The rates are per EURO */ +/* The rates are per EURO and are converted to GncNumeric */ static gnc_euro_rate_struct gnc_euro_rates[] = { { "ATS", 13.7603 }, /* austrian schilling */ @@ -157,7 +156,8 @@ gnc_convert_to_euro(const gnc_commodity * currency, gnc_numeric value) rate = double_to_gnc_numeric (result->rate, 100000, GNC_HOW_RND_ROUND_HALF_UP); /* EC Regulation 1103/97 states we should use "Round half away from zero" - * See http://europa.eu/legislation_summaries/economic_and_monetary_affairs/institutional_and_economic_framework/l25025_en.htm */ + * See https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A31997R1103&qid=1662917247821 + */ return gnc_numeric_div (value, rate, 100, GNC_HOW_RND_ROUND_HALF_UP); } } @@ -227,9 +227,8 @@ gnc_euro_currency_get_rate (const gnc_commodity *currency) gnc_commodity * gnc_get_euro (void) { - gnc_commodity_table *table; - - table = gnc_commodity_table_get_table (gnc_get_current_book ()); + QofBook* book = qof_session_get_book (gnc_get_current_session ()); + gnc_commodity_table *table = gnc_commodity_table_get_table (book); return gnc_commodity_table_lookup (table, GNC_COMMODITY_NS_CURRENCY, "EUR"); } diff --git a/libgnucash/app-utils/gnc-euro.h b/libgnucash/engine/gnc-euro.h similarity index 100% rename from libgnucash/app-utils/gnc-euro.h rename to libgnucash/engine/gnc-euro.h diff --git a/libgnucash/engine/test/CMakeLists.txt b/libgnucash/engine/test/CMakeLists.txt index 8e44172ff6..d1029bb6b6 100644 --- a/libgnucash/engine/test/CMakeLists.txt +++ b/libgnucash/engine/test/CMakeLists.txt @@ -132,6 +132,9 @@ set(test_qofsession_SOURCES gnc_add_test(test-qofsession "${test_qofsession_SOURCES}" gtest_engine_INCLUDES gtest_old_engine_LIBS) +gnc_add_test(test-gnc-euro gtest-gnc-euro.cpp + gtest_engine_INCLUDES gtest_old_engine_LIBS) + set(test_gnc_int128_SOURCES ${MODULEPATH}/gnc-int128.cpp gtest-gnc-int128.cpp) @@ -197,6 +200,7 @@ gnc_add_test(test-qofquerycore "${test_qofquerycore_SOURCES}" set(test_engine_SOURCES_DIST dummy.cpp + gtest-gnc-euro.cpp gtest-gnc-int128.cpp gtest-gnc-rational.cpp gtest-gnc-numeric.cpp diff --git a/libgnucash/engine/test/gtest-gnc-euro.cpp b/libgnucash/engine/test/gtest-gnc-euro.cpp new file mode 100644 index 0000000000..4777d09aaf --- /dev/null +++ b/libgnucash/engine/test/gtest-gnc-euro.cpp @@ -0,0 +1,228 @@ +/******************************************************************** + * gtest-gnc-euro.cpp -- unit tests for Euro currency functions * + * Copyright 2022 John Ralls * + * * + * 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@gnu.org * +\********************************************************************/ + +#include +#include "../gnc-numeric.hpp" +extern "C" +{ +#include +#include "../gnc-euro.h" +#include "../gnc-commodity.h" +#include "../gnc-session.h" +} + +class Currencies : public ::testing::Test +{ +protected: + gnc_commodity_table* m_table; + Currencies() : m_table{gnc_commodity_table_new()} + { + QofBook* book = qof_session_get_book (gnc_get_current_session ()); + qof_book_set_data (book, GNC_COMMODITY_TABLE, m_table); + + if (!gnc_commodity_table_add_default_data(m_table, book)) + exit(-1); + } + + ~Currencies() { gnc_commodity_table_destroy(m_table); } +}; + +static const char* currency{"CURRENCY"}; + +TEST_F(Currencies, is_euro_currency) +{ + auto usd = gnc_commodity_table_lookup(m_table, currency, "USD"); + auto eur = gnc_commodity_table_lookup(m_table, currency, "EUR"); + auto pte = gnc_commodity_table_lookup(m_table, currency, "PTE"); + + ASSERT_NE(eur, nullptr); + ASSERT_NE(usd, nullptr); + ASSERT_NE(pte, nullptr); + + EXPECT_TRUE(gnc_is_euro_currency(pte)); + EXPECT_TRUE(gnc_is_euro_currency(eur)); + EXPECT_FALSE(gnc_is_euro_currency(usd)); +} + +TEST_F(Currencies, convert_to_euro) +{ + gnc_numeric value{314159, 100}; + gnc_numeric cyp_eur_amount{536776, 100}; // calc gets 5367.76 + auto cyp{gnc_commodity_table_lookup(m_table, currency, "CYP")}; + ASSERT_NE(cyp, nullptr); + auto amount{gnc_convert_to_euro(cyp, value)}; + EXPECT_EQ(cyp_eur_amount.num, amount.num); + EXPECT_EQ(cyp_eur_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(cyp_eur_amount, amount)); + + gnc_numeric grd_eur_amount{922, 100}; + auto grd{gnc_commodity_table_lookup(m_table, currency, "GRD")}; + amount = gnc_convert_to_euro(grd, value); + EXPECT_EQ(grd_eur_amount.num, amount.num); + EXPECT_EQ(grd_eur_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(grd_eur_amount, amount)); + + gnc_numeric itl_eur_amount{162, 100}; + auto itl{gnc_commodity_table_lookup(m_table, currency, "ITL")}; + amount = gnc_convert_to_euro(itl, value); + EXPECT_EQ(itl_eur_amount.num, amount.num); + EXPECT_EQ(itl_eur_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(itl_eur_amount, amount)); + + gnc_numeric nlg_eur_amount{142559, 100}; + auto nlg{gnc_commodity_table_lookup(m_table, currency, "NLG")}; + amount = gnc_convert_to_euro(nlg, value); + EXPECT_EQ(nlg_eur_amount.num, amount.num); + EXPECT_EQ(nlg_eur_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(nlg_eur_amount, amount)); + + auto eur{gnc_commodity_table_lookup(m_table, currency, "EUR")}; + ASSERT_NE(eur, nullptr); + EXPECT_TRUE(gnc_numeric_equal(value, + gnc_convert_to_euro(eur, value))); +} + +TEST_F(Currencies, convert_from_euro) +{ + gnc_numeric value{314159, 100}; //.787564 * 3141.59 = 2472.20 + gnc_numeric eur_iep_amount{247419, 100}; + auto iep{gnc_commodity_table_lookup(m_table, currency, "IEP")}; + ASSERT_NE(iep, nullptr); + auto amount{gnc_convert_from_euro(iep, value)}; + EXPECT_EQ(eur_iep_amount.num, amount.num); + EXPECT_EQ(eur_iep_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(eur_iep_amount, amount)); + + gnc_numeric eur_itl_amount{6082966, 1}; + auto itl{gnc_commodity_table_lookup(m_table, currency, "ITL")}; + amount = gnc_convert_from_euro(itl, value); + EXPECT_EQ(eur_itl_amount.num, amount.num); + EXPECT_EQ(eur_itl_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(eur_itl_amount, amount)); + + gnc_numeric eur_fim_amount{1867905, 100}; + auto fim{gnc_commodity_table_lookup(m_table, currency, "FIM")}; + EXPECT_NE(fim, nullptr); + amount = gnc_convert_from_euro(fim, value); + EXPECT_EQ(eur_fim_amount.num, amount.num); + EXPECT_EQ(eur_fim_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(eur_fim_amount, amount)); + + gnc_numeric eur_dm_amount{614442, 100}; + auto dm{gnc_commodity_table_lookup(m_table, currency, "DEM")}; + EXPECT_NE(dm, nullptr); + amount = gnc_convert_from_euro(dm, value); + EXPECT_EQ(eur_dm_amount.num, amount.num); + EXPECT_EQ(eur_dm_amount.denom, amount.denom); + EXPECT_TRUE(gnc_numeric_equal(eur_dm_amount, amount)); + + auto eur{gnc_commodity_table_lookup(m_table, currency, "EUR")}; + EXPECT_TRUE(gnc_numeric_equal(value, + gnc_convert_from_euro(eur, value))); +} + +TEST_F(Currencies, euro_currency_get_rate) +{ + auto ats{gnc_commodity_table_lookup(m_table, currency, "ATS")}; + ASSERT_NE(ats, nullptr); + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{137603, 10000}, + gnc_euro_currency_get_rate(ats))); + + auto bef{gnc_commodity_table_lookup(m_table, currency, "BEF")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{403399, 10000}, + gnc_euro_currency_get_rate(bef))); + + auto cyp{gnc_commodity_table_lookup(m_table, currency, "CYP")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{585274, 1000000}, + gnc_euro_currency_get_rate(cyp))); + + auto dem{gnc_commodity_table_lookup(m_table, currency, "DEM")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{195583, 100000}, + gnc_euro_currency_get_rate(dem))); + + auto eek{gnc_commodity_table_lookup(m_table, currency, "EEK")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{156466, 10000}, + gnc_euro_currency_get_rate(eek))); + + auto esp{gnc_commodity_table_lookup(m_table, currency, "ESP")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{166386, 1000}, + gnc_euro_currency_get_rate(esp))); + + auto eur{gnc_commodity_table_lookup(m_table, currency, "EUR")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{100000, 100000}, + gnc_euro_currency_get_rate(eur))); + + auto fim{gnc_commodity_table_lookup(m_table, currency, "FIM")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{594573, 100000}, + gnc_euro_currency_get_rate(fim))); + + + auto frf{gnc_commodity_table_lookup(m_table, currency, "FRF")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{655957, 100000}, + gnc_euro_currency_get_rate(frf))); + + auto grd{gnc_commodity_table_lookup(m_table, currency, "GRD")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{340750, 1000}, + gnc_euro_currency_get_rate(grd))); + + + auto hrk{gnc_commodity_table_lookup(m_table, currency, "HRK")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{753450, 100000}, + gnc_euro_currency_get_rate(hrk))); + + auto iep{gnc_commodity_table_lookup(m_table, currency, "IEP")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{787564, 1000000}, + gnc_euro_currency_get_rate(iep))); + + auto itl{gnc_commodity_table_lookup(m_table, currency, "ITL")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{193627, 100}, + gnc_euro_currency_get_rate(itl))); + + auto luf{gnc_commodity_table_lookup(m_table, currency, "LUF")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{403399, 10000}, + gnc_euro_currency_get_rate(luf))); + + auto lvl{gnc_commodity_table_lookup(m_table, currency, "LVL")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{702804, 1000000}, + gnc_euro_currency_get_rate(lvl))); + + auto mtl{gnc_commodity_table_lookup(m_table, currency, "MTL")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{429300, 1000000}, + gnc_euro_currency_get_rate(mtl))); + + auto nlg{gnc_commodity_table_lookup(m_table, currency, "NLG")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{220371, 100000}, + gnc_euro_currency_get_rate(nlg))); + + auto pte{gnc_commodity_table_lookup(m_table, currency, "PTE")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{200482, 1000}, + gnc_euro_currency_get_rate(pte))); + + auto sit{gnc_commodity_table_lookup(m_table, currency, "SIT")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{239640, 1000}, + gnc_euro_currency_get_rate(sit))); + + auto skk{gnc_commodity_table_lookup(m_table, currency, "SKK")}; + EXPECT_TRUE(gnc_numeric_equal(gnc_numeric{301260, 10000}, + gnc_euro_currency_get_rate(skk))); + +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 6c9be8e2c2..6795e6f2ba 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -534,7 +534,6 @@ libgnucash/app-utils/gnc-accounting-period.c libgnucash/app-utils/gnc-account-merge.c libgnucash/app-utils/gnc-addr-quickfill.c libgnucash/app-utils/gnc-entry-quickfill.c -libgnucash/app-utils/gnc-euro.c libgnucash/app-utils/gnc-exp-parser.c libgnucash/app-utils/gnc-gsettings.cpp libgnucash/app-utils/gnc-helpers.c @@ -640,6 +639,7 @@ libgnucash/engine/gnc-datetime.cpp libgnucash/engine/gncEmployee.c libgnucash/engine/gnc-engine.c libgnucash/engine/gncEntry.c +libgnucash/engine/gnc-euro.c libgnucash/engine/gnc-event.c libgnucash/engine/gnc-features.c libgnucash/engine/gnc-hooks.c From 8cb8f5067dbc7adfd920099de2b11aa1b92b7657 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 11 Sep 2022 16:36:52 -0700 Subject: [PATCH 3/9] Remove non-ISO4217 currency codes from gnc-euro table. They're not reachable because they don't have commodities associated with them. --- libgnucash/engine/gnc-euro.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/libgnucash/engine/gnc-euro.c b/libgnucash/engine/gnc-euro.c index 1a20e14597..411d6209fd 100644 --- a/libgnucash/engine/gnc-euro.c +++ b/libgnucash/engine/gnc-euro.c @@ -43,35 +43,22 @@ static gnc_euro_rate_struct gnc_euro_rates[] = { { "ATS", 13.7603 }, /* austrian schilling */ { "BEF", 40.3399 }, /* belgian franc */ - { "BFR", 40.3399 }, /* belgian franc */ { "CYP", .585274 }, /* cyprus pound */ { "DEM", 1.95583 }, /* german mark */ - { "DM", 1.95583 }, /* german mark */ { "EEK", 15.6466 }, /* Estonian Kroon */ - { "ESC", 200.482 }, /* portuguese escudo */ { "ESP", 166.386 }, /* spanish peseta */ { "EUR", 1.00000 }, /* euro */ - { "EURO", 1.00000 }, /* euro */ - { "FF", 6.55957 }, /* french franc */ { "FIM", 5.94573 }, /* finnmark */ - { "FMK", 5.94573 }, /* finnmark */ { "FRF", 6.55957 }, /* french franc */ { "GRD", 340.750 }, /* greek drachma */ - { "HFL", 2.20371 }, /* netherland gulden */ { "HRK", 7.53450 }, /* Croatian kuna */ { "IEP", .787564 }, /* irish pound */ - { "IRP", .787564 }, /* irish pound */ { "ITL", 1936.27 }, /* italian lira */ - { "LFR", 40.3399 }, /* luxembourg franc */ - { "LIT", 1936.27 }, /* italian lira */ { "LUF", 40.3399 }, /* luxembourg franc */ { "LVL", .702804 }, /* latvian lats */ { "MTL", .429300 }, /* maltese lira */ { "NLG", 2.20371 }, /* netherland gulden */ - { "PTA", 166.386 }, /* spanish peseta */ { "PTE", 200.482 }, /* portuguese escudo */ - { "S", 13.7603 }, /* austrian schilling */ - { "SCH", 13.7603 }, /* austrian schilling */ { "SIT", 239.640 }, /* slovenian tolar */ { "SKK", 30.1260 } /* slovak koruna */ }; From 8192deff37e4a5063ad62a90753bcd9a60b2f716 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Tue, 25 Oct 2022 00:17:30 +0800 Subject: [PATCH 4/9] [gnc-features.cpp] convert to cpp - don't need to create/destroy GHashTable for each feature query - plugs leak: g_hash_table_unref (features_used) not always called properly - to check 1 feature, don't need to traverse whole GHashTable --- libgnucash/engine/CMakeLists.txt | 3 +- .../{gnc-features.c => gnc-features.cpp} | 129 ++++-------------- libgnucash/engine/qofbook.cpp | 28 ++++ libgnucash/engine/qofbook.hpp | 36 +++++ po/POTFILES.in | 2 +- 5 files changed, 90 insertions(+), 108 deletions(-) rename libgnucash/engine/{gnc-features.c => gnc-features.cpp} (53%) create mode 100644 libgnucash/engine/qofbook.hpp diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt index 8dde8461bc..b9d11b8779 100644 --- a/libgnucash/engine/CMakeLists.txt +++ b/libgnucash/engine/CMakeLists.txt @@ -93,6 +93,7 @@ set (engine_HEADERS qof-backend.hpp qofbackend.h qofbook.h + qofbook.hpp qofbookslots.h qofchoice.h qofclass.h @@ -145,7 +146,7 @@ set (engine_SOURCES gnc-datetime.cpp gnc-engine.c gnc-event.c - gnc-features.c + gnc-features.cpp gnc-hooks.c gnc-int128.cpp gnc-lot.c diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.cpp similarity index 53% rename from libgnucash/engine/gnc-features.c rename to libgnucash/engine/gnc-features.cpp index f710677d2b..5e2823a3c3 100644 --- a/libgnucash/engine/gnc-features.c +++ b/libgnucash/engine/gnc-features.cpp @@ -19,28 +19,22 @@ * * \********************************************************************/ +#include +#include +#include + #include #include #include -#include -#include -#include -#include -#include - -#include "qof.h" -#include "gnc-features.h" -#include "gnc-glib-utils.h" +#include "qofbook.hpp" -typedef struct +extern "C" { - const gchar *key; - const gchar *desc; -} gncFeature; +#include "gnc-features.h" +} -static GHashTable *features_table = NULL; -static gncFeature known_features[] = +static const FeaturesTable features_table { { GNC_FEATURE_CREDIT_NOTES, "Customer and vendor credit notes (requires at least GnuCash 2.5.0)" }, { GNC_FEATURE_NUM_FIELD_SOURCE, "User specifies source of 'num' field'; either transaction number or split action (requires at least GnuCash 2.5.0)" }, @@ -53,7 +47,6 @@ static gncFeature known_features[] = { GNC_FEATURE_BUDGET_UNREVERSED, "Store budget amounts unreversed (i.e. natural) signs (requires at least Gnucash 3.8)"}, { GNC_FEATURE_BUDGET_SHOW_EXTRA_ACCOUNT_COLS, "Show extra account columns in the Budget View (requires at least Gnucash 3.8)"}, { GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE " (requires at least Gnucash 4.3)" }, - { NULL }, }; /* This static indicates the debugging module that this .o belongs to. */ @@ -62,40 +55,11 @@ static QofLogModule log_module = G_LOG_DOMAIN; /********************************************************************\ \********************************************************************/ -static void gnc_features_init () -{ - gint i; - - if (features_table) - return; - - features_table = g_hash_table_new (g_str_hash, g_str_equal); - for (i = 0; known_features[i].key; i++) - g_hash_table_insert (features_table, - g_strdup (known_features[i].key), - g_strdup (known_features[i].desc)); -} - -static void gnc_features_test_one(gpointer pkey, gpointer value, - gpointer data) -{ - const gchar *key = (const gchar*)pkey; - const gchar *feature_desc = (const gchar*)value; - GList **unknown_features; - - g_assert(data); - unknown_features = (GList**) data; - - /* Check if this feature is in the known features list. */ - if (g_hash_table_lookup_extended (features_table, key, NULL, NULL)) - return; - - /* It is unknown, so add the description to the unknown features list: */ - g_assert(feature_desc); - - *unknown_features = g_list_prepend(*unknown_features, - (gpointer)feature_desc); -} +static const char* +header = N_("This Dataset contains features not supported " + "by this version of GnuCash. You must use a " + "newer version of GnuCash in order to support " + "the following features:"); /* Check if the session requires features unknown to this version of GnuCash. * @@ -104,78 +68,31 @@ static void gnc_features_test_one(gpointer pkey, gpointer value, */ gchar *gnc_features_test_unknown (QofBook *book) { - - GList* features_list = NULL; - GHashTable *features_used = qof_book_get_features (book); - - /* Setup the known_features hash table */ - gnc_features_init(); - - /* Iterate over the members of this frame for unknown features */ - g_hash_table_foreach (features_used, &gnc_features_test_one, - &features_list); - if (features_list) - { - const char* sep = "\n* "; - const char* header = _("This Dataset contains features not supported " - "by this version of GnuCash. You must use a " - "newer version of GnuCash in order to support " - "the following features:"); - - char *features_str = gnc_g_list_stringjoin (features_list, sep); - char *msg = g_strconcat (header, sep, features_str, NULL); - g_free (features_str); - g_list_free(features_list); - return msg; - } - g_hash_table_unref (features_used); - return NULL; + auto unknowns {qof_book_get_unknown_features (book, features_table)}; + auto accum = [](const auto& a, const auto& b){ return a + "\n* " + b; }; + return unknowns.empty() ? nullptr : + g_strdup (std::accumulate (unknowns.begin(), unknowns.end(), + std::string (_(header)), accum).c_str()); } void gnc_features_set_used (QofBook *book, const gchar *feature) { - const gchar *description; - g_return_if_fail (book); g_return_if_fail (feature); - gnc_features_init(); - /* Can't set an unknown feature */ - description = g_hash_table_lookup (features_table, feature); - if (!description) + auto iter = features_table.find (feature); + if (iter == features_table.end ()) { PWARN("Tried to set unknown feature as used."); return; } - qof_book_set_feature (book, feature, description); -} - -struct CheckFeature -{ - gchar const * checked_feature; - gboolean found; -}; - -static void gnc_features_check_feature_cb (gpointer pkey, gpointer value, - gpointer data) -{ - const gchar *key = (const gchar*)pkey; - struct CheckFeature * check_data = data; - g_assert(data); - if (!g_strcmp0 (key, check_data->checked_feature)) - check_data->found = TRUE; + qof_book_set_feature (book, feature, iter->second.c_str()); } gboolean gnc_features_check_used (QofBook *book, const gchar * feature) { - GHashTable *features_used = qof_book_get_features (book); - struct CheckFeature check_data = {feature, FALSE}; - /* Setup the known_features hash table */ - gnc_features_init(); - g_hash_table_foreach (features_used, &gnc_features_check_feature_cb, &check_data); - g_hash_table_unref (features_used); - return check_data.found; + return qof_book_test_feature (book, feature); } diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index ab2b012319..da4e2c843a 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -61,6 +61,8 @@ extern "C" // For GNC_ID_ROOT_ACCOUNT: #include "AccountP.h" +#include "qofbook.hpp" + static QofLogModule log_module = QOF_MOD_ENGINE; #define AB_KEY "hbci" #define AB_TEMPLATES "template-list" @@ -1255,6 +1257,32 @@ qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr) } } +std::vector +qof_book_get_unknown_features (QofBook *book, const FeaturesTable& features) +{ + std::vector rv; + auto test_feature = [&](const KvpFrameImpl::map_type::value_type& feature) + { + if (features.find (feature.first) == features.end ()) + rv.push_back (feature.second->get()); + }; + auto frame = qof_instance_get_slots (QOF_INSTANCE (book)); + auto slot = frame->get_slot({GNC_FEATURES}); + if (slot != nullptr) + { + frame = slot->get(); + std::for_each (frame->begin (), frame->end (), test_feature); + } + return rv; +} + +bool +qof_book_test_feature (QofBook *book, const char *feature) +{ + auto frame = qof_instance_get_slots (QOF_INSTANCE (book)); + return (frame->get_slot({GNC_FEATURES, feature}) != nullptr); +} + void qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb) { diff --git a/libgnucash/engine/qofbook.hpp b/libgnucash/engine/qofbook.hpp new file mode 100644 index 0000000000..402de17119 --- /dev/null +++ b/libgnucash/engine/qofbook.hpp @@ -0,0 +1,36 @@ +/********************************************************************\ + * 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@gnu.org * + * * +\********************************************************************/ + +#ifndef __QOF_BOOK__HPP__ +#define __QOF_BOOK__HPP__ + +#include +#include +#include + +#include "qof.h" + +using FeaturesTable = std::unordered_map; + +std::vector +qof_book_get_unknown_features (QofBook *book, const FeaturesTable& features); +bool qof_book_test_feature (QofBook*, const char*); + +#endif /* QOF_BOOK_HPP */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 6c9be8e2c2..3d2a0e7b38 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -641,7 +641,7 @@ libgnucash/engine/gncEmployee.c libgnucash/engine/gnc-engine.c libgnucash/engine/gncEntry.c libgnucash/engine/gnc-event.c -libgnucash/engine/gnc-features.c +libgnucash/engine/gnc-features.cpp libgnucash/engine/gnc-hooks.c libgnucash/engine/gncIDSearch.c libgnucash/engine/gnc-int128.cpp From 093aa81ed1f4f638775212af4ee61d9e5d63fa07 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Fri, 28 Oct 2022 21:57:15 +0800 Subject: [PATCH 5/9] [qofbook.cpp] deprecate qof_book_get_features --- libgnucash/engine/qofbook.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index da4e2c843a..cbed9cb2d5 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -1228,6 +1228,8 @@ qof_book_get_features (QofBook *book) GHashTable *features = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + PWARN ("qof_book_get_features is now deprecated."); + auto slot = frame->get_slot({GNC_FEATURES}); if (slot != nullptr) { From f5099b3d87a693991dfb3160f848ad218254b760 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 28 Oct 2022 11:37:18 -0700 Subject: [PATCH 6/9] Bug 798649 - Crash when closing Edit Style Sheets dialog while... Style Sheet Properties dialog is still open. Because the style_sheet_dialog is already destroyed and nulled. --- gnucash/gnome/dialog-report-style-sheet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.c index a4f8d75272..1a4d4afc12 100644 --- a/gnucash/gnome/dialog-report-style-sheet.c +++ b/gnucash/gnome/dialog-report-style-sheet.c @@ -138,7 +138,7 @@ gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox, ss_info * ssi = user_data; GtkTreeIter iter; - if (gtk_tree_row_reference_valid (ssi->row_ref)) + if (gnc_style_sheet_dialog && gtk_tree_row_reference_valid (ssi->row_ref)) { StyleSheetDialog * ss = gnc_style_sheet_dialog; GtkTreePath *path = gtk_tree_row_reference_get_path (ssi->row_ref); From a078921a33e3ea1e2f3427565fe8d348205b3f6a Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Thu, 2 Dec 2021 22:12:12 +0800 Subject: [PATCH 7/9] [qofbook.cpp] backport qof_book_unset_feature from master --- libgnucash/engine/qofbook.cpp | 16 ++++++++++++++++ libgnucash/engine/qofbook.h | 1 + 2 files changed, 17 insertions(+) diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index cbed9cb2d5..d963aa18c9 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -1285,6 +1285,22 @@ qof_book_test_feature (QofBook *book, const char *feature) return (frame->get_slot({GNC_FEATURES, feature}) != nullptr); } +void +qof_book_unset_feature (QofBook *book, const gchar *key) +{ + KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book)); + auto feature_slot = frame->get_slot({GNC_FEATURES, key}); + if (!feature_slot) + { + PWARN ("no feature %s. bail out.", key); + return; + } + qof_book_begin_edit (book); + delete frame->set_path({GNC_FEATURES, key}, nullptr); + qof_instance_set_dirty (QOF_INSTANCE (book)); + qof_book_commit_edit (book); +} + void qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb) { diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h index fbaf8a0202..47104bbf4b 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -382,6 +382,7 @@ void qof_book_option_frame_delete (QofBook *book, const char* opt_name); /** Access functions for reading and setting the used-features on this book. */ GHashTable *qof_book_get_features (QofBook *book); +void qof_book_unset_feature (QofBook *book, const gchar *key); void qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr); void qof_book_begin_edit(QofBook *book); From 1516bb18b070d6fbc0388791edb8cb1040ad0d6e Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Thu, 2 Dec 2021 22:12:29 +0800 Subject: [PATCH 8/9] [gnc-features.cpp] backport gnc_features_set_unused from master --- libgnucash/engine/gnc-features.cpp | 17 +++++++++++++++++ libgnucash/engine/gnc-features.h | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/libgnucash/engine/gnc-features.cpp b/libgnucash/engine/gnc-features.cpp index 5e2823a3c3..07d3e59fe2 100644 --- a/libgnucash/engine/gnc-features.cpp +++ b/libgnucash/engine/gnc-features.cpp @@ -91,6 +91,23 @@ void gnc_features_set_used (QofBook *book, const gchar *feature) qof_book_set_feature (book, feature, iter->second.c_str()); } + +void gnc_features_set_unused (QofBook *book, const gchar *feature) +{ + g_return_if_fail (book); + g_return_if_fail (feature); + + /* Can't set an unknown feature */ + auto iter = features_table.find (feature); + if (iter == features_table.end ()) + { + PWARN("Tried to set unknown feature as unused."); + return; + } + + qof_book_unset_feature (book, feature); +} + gboolean gnc_features_check_used (QofBook *book, const gchar * feature) { return qof_book_test_feature (book, feature); diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h index 030c02531e..b5960ac7ad 100644 --- a/libgnucash/engine/gnc-features.h +++ b/libgnucash/engine/gnc-features.h @@ -67,6 +67,13 @@ extern "C" { */ gchar *gnc_features_test_unknown (QofBook *book); +/** + * Indicate that the current book does not use the given feature. This + * will allow older versions of GnuCash that don't support this + * feature to load this book. + */ +void gnc_features_set_unused (QofBook *book, const gchar *feature); + /** * Indicate that the current book uses the given feature. This will prevent * older versions of GnuCash that don't support this feature to refuse to load From f2354d6b2af329c5cc849fc9d7908884c4b7fdc2 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Sat, 29 Oct 2022 13:47:33 +0800 Subject: [PATCH 9/9] [test-qofbook.c] add test for gnc_features_set_unused --- libgnucash/engine/test/test-qofbook.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libgnucash/engine/test/test-qofbook.c b/libgnucash/engine/test/test-qofbook.c index a771c3d5ef..4c6fe444e9 100644 --- a/libgnucash/engine/test/test-qofbook.c +++ b/libgnucash/engine/test/test-qofbook.c @@ -782,6 +782,10 @@ test_book_features (Fixture *fixture, gconstpointer pData) g_assert_null (gnc_features_test_unknown (fixture->book)); g_assert_true (gnc_features_check_used (fixture->book, "Credit Notes")); + gnc_features_set_unused (fixture->book, "Credit Notes"); + g_assert_null (gnc_features_test_unknown (fixture->book)); + g_assert_false (gnc_features_check_used (fixture->book, "Credit Notes")); + /* cannot set an unknown feature: it bails out. */ /* gnc_features_set_used (fixture->book, "Nanotech"); */ /* g_assert_nonnull (gnc_features_test_unknown (fixture->book)); */