diff --git a/bindings/guile/gnc-engine-guile.c b/bindings/guile/gnc-engine-guile.c index 36915cd64c..45a69ec243 100644 --- a/bindings/guile/gnc-engine-guile.c +++ b/bindings/guile/gnc-engine-guile.c @@ -1116,6 +1116,7 @@ gnc_scm2query_and_terms (SCM and_terms, query_version_t vers) if (q_and) { q_new = qof_query_merge (q, q_and, QOF_QUERY_AND); + qof_query_destroy (q_and); if (q_new) { @@ -1158,6 +1159,7 @@ gnc_scm2query_or_terms (SCM or_terms, query_version_t vers) if (q_or) { q_new = qof_query_merge (q, q_or, QOF_QUERY_OR); + qof_query_destroy (q_or); if (q_new) { diff --git a/bindings/python/tests/test_transaction.py b/bindings/python/tests/test_transaction.py index 78e792156d..446a7b8e41 100644 --- a/bindings/python/tests/test_transaction.py +++ b/bindings/python/tests/test_transaction.py @@ -2,6 +2,7 @@ from unittest import main from gnucash import Transaction, Book, Account, Split from unittest_support import * +from datetime import datetime, timezone from test_book import BookSession @@ -138,5 +139,12 @@ class TestTransaction(TransactionSession): self.trans.SetNotes(NOTE) self.assertEqual( NOTE, self.trans.GetNotes() ) + def test_date(self): + ZERODATE=datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + DATE=datetime(2020, 2, 20, 10, 59, 0, tzinfo=timezone.utc) + self.assertEqual(ZERODATE, self.trans.GetDate().astimezone(timezone.utc)) + self.trans.SetDate(DATE.day, DATE.month, DATE.year) + self.assertEqual(DATE, self.trans.GetDate().astimezone(timezone.utc)) + if __name__ == '__main__': main() diff --git a/gnucash/gnome-utils/gnc-tree-view-account.c b/gnucash/gnome-utils/gnc-tree-view-account.c index 35c33f2e85..03425bf056 100644 --- a/gnucash/gnome-utils/gnc-tree-view-account.c +++ b/gnucash/gnome-utils/gnc-tree-view-account.c @@ -372,6 +372,7 @@ sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn, gpointer user_data) { const Account *account_a, *account_b; + const gnc_commodity *cur = gnc_default_currency(); gnc_numeric balance_a, balance_b; gint result; @@ -379,8 +380,8 @@ sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn, sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b); /* Get balances */ - balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, NULL); - balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, NULL); + balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, cur); + balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, cur); result = gnc_numeric_compare(balance_a, balance_b); if (result != 0) diff --git a/gnucash/gnome/dialog-customer.c b/gnucash/gnome/dialog-customer.c index f356bfa172..560b54060c 100644 --- a/gnucash/gnome/dialog-customer.c +++ b/gnucash/gnome/dialog-customer.c @@ -930,6 +930,8 @@ gnc_customer_search (GtkWindow *parent, GncCustomer *start, QofBook *book) /* Build the column list in reverse order */ if (columns == NULL) { + columns = gnc_search_param_prepend (columns, _("Shipping Contact"), NULL, type, + CUSTOMER_SHIPADDR, ADDRESS_NAME, NULL); columns = gnc_search_param_prepend (columns, _("Contact"), NULL, type, CUSTOMER_ADDR, ADDRESS_NAME, NULL); columns = gnc_search_param_prepend (columns, _("Company"), NULL, type, diff --git a/gnucash/gnome/gnc-plugin-report-system.c b/gnucash/gnome/gnc-plugin-report-system.c index 3016f99373..621f001317 100644 --- a/gnucash/gnome/gnc-plugin-report-system.c +++ b/gnucash/gnome/gnc-plugin-report-system.c @@ -136,10 +136,10 @@ gnc_report_system_file_stream_cb (const char *location, char ** data, int *len) static gboolean gnc_report_system_report_stream_cb (const char *location, char ** data, int *len) { - gboolean ok; - gchar *captured_str; - - ok = gnc_run_report_id_string_with_error_handling (location, data, &captured_str); + gchar *captured_str = NULL; + gboolean ok = + gnc_run_report_id_string_with_error_handling (location, data, + &captured_str); if (!ok) { diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c index 4177072281..9ed645d1b1 100644 --- a/gnucash/gnome/window-reconcile.c +++ b/gnucash/gnome/window-reconcile.c @@ -2120,6 +2120,9 @@ recn_destroy_cb (GtkWidget *w, gpointer data) if (recnData->delete_refresh) gnc_resume_gui_refresh (); + //Disable the actions, the handlers try to access recnData + gtk_action_group_set_sensitive(recnData->action_group, FALSE); + g_free (recnData); } diff --git a/gnucash/gnucash-commands.cpp b/gnucash/gnucash-commands.cpp index 488a93cb99..3a5edb8f36 100644 --- a/gnucash/gnucash-commands.cpp +++ b/gnucash/gnucash-commands.cpp @@ -30,6 +30,7 @@ #endif #include "gnucash-commands.hpp" +#include "gnucash-core-app.hpp" extern "C" { #include @@ -173,8 +174,7 @@ scm_run_report (void *data, scm_c_use_module ("gnucash reports"); gnc_report_init (); - // load_system_config(); - // load_user_config(); + Gnucash::gnc_load_scm_config(); gnc_prefs_init (); qof_event_suspend (); @@ -303,6 +303,7 @@ scm_report_show (void *data, scm_c_use_module ("gnucash app-utils"); scm_c_use_module ("gnucash reports"); gnc_report_init (); + Gnucash::gnc_load_scm_config(); if (!args->file_to_load.empty()) { @@ -334,6 +335,7 @@ scm_report_list ([[maybe_unused]] void *data, scm_c_use_module ("gnucash app-utils"); scm_c_use_module ("gnucash reports"); gnc_report_init (); + Gnucash::gnc_load_scm_config(); scm_call_1 (scm_c_eval_string ("gnc:cmdline-report-list"), scm_current_output_port ()); diff --git a/gnucash/gnucash-commands.hpp b/gnucash/gnucash-commands.hpp index 01511d5847..ae3607147b 100644 --- a/gnucash/gnucash-commands.hpp +++ b/gnucash/gnucash-commands.hpp @@ -40,5 +40,9 @@ namespace Gnucash { int report_list (void); int report_show (const bo_str& file_to_load, const bo_str& run_report); + + // A helper function to load scm config files (SYSCONFIGDIR/config + // and USERCONFIGDIR/config-user.scm) on demand + void gnc_load_scm_config(void); } #endif diff --git a/gnucash/gnucash-core-app.cpp b/gnucash/gnucash-core-app.cpp index 8207d85bf3..3269fbf17c 100644 --- a/gnucash/gnucash-core-app.cpp +++ b/gnucash/gnucash-core-app.cpp @@ -86,25 +86,6 @@ gnc_print_unstable_message(void) << bl::format (bl::translate ("To find the last stable version, please refer to {1}")) % PACKAGE_URL << "\n"; } -static gboolean -try_load_config_array(const gchar *fns[]) -{ - gchar *filename; - int i; - - for (i = 0; fns[i]; i++) - { - filename = gnc_build_userdata_path(fns[i]); - if (gfec_try_load(filename)) - { - g_free(filename); - return TRUE; - } - g_free(filename); - } - return FALSE; -} - static void update_message(const gchar *msg) { @@ -112,52 +93,30 @@ update_message(const gchar *msg) g_message("%s", msg); } -static void -load_system_config(void) +void +Gnucash::gnc_load_scm_config (void) { - static int is_system_config_loaded = FALSE; - gchar *system_config_dir; - gchar *system_config; - - if (is_system_config_loaded) return; - - update_message("loading system configuration"); - system_config_dir = gnc_path_get_pkgsysconfdir(); - system_config = g_build_filename(system_config_dir, "config", nullptr); - is_system_config_loaded = gfec_try_load(system_config); - g_free(system_config_dir); - g_free(system_config); -} - -static void -load_user_config(void) -{ - /* Don't continue adding to this list. When 3.0 rolls around bump - the 2.4 files off the list. */ - static const gchar *saved_report_files[] = + static auto is_system_config_loaded = false; + if (!is_system_config_loaded) { - SAVED_REPORTS_FILE, SAVED_REPORTS_FILE_OLD_REV, NULL - }; - static const gchar *stylesheet_files[] = { "stylesheets-2.0", NULL}; - static int is_user_config_loaded = FALSE; - - if (is_user_config_loaded) - return; - else is_user_config_loaded = TRUE; - - update_message("loading user configuration"); - { - gchar *config_filename; - config_filename = g_build_filename (gnc_userconfig_dir (), - "config-user.scm", (char *)NULL); - gfec_try_load(config_filename); - g_free(config_filename); + auto msg = bl::translate ("Loading system scm configuration...").str (gnc_get_boost_locale()); + update_message (msg.c_str()); + auto system_config_dir = gnc_path_get_pkgsysconfdir (); + auto system_config = g_build_filename (system_config_dir, "config", nullptr); + is_system_config_loaded = gfec_try_load (system_config); + g_free (system_config_dir); + g_free (system_config); } - update_message("loading saved reports"); - try_load_config_array(saved_report_files); - update_message("loading stylesheets"); - try_load_config_array(stylesheet_files); + static auto is_user_config_loaded = false; + if (!is_user_config_loaded) + { + auto msg = bl::translate ("Loading user scm configuration...").str (gnc_get_boost_locale()); + update_message (msg.c_str()); + auto config_filename = g_build_filename (gnc_userconfig_dir (), "config-user.scm", nullptr); + is_user_config_loaded = gfec_try_load (config_filename); + g_free (config_filename); + } } static void diff --git a/gnucash/gnucash-core-app.hpp b/gnucash/gnucash-core-app.hpp index ed61af1f28..9ff91af336 100644 --- a/gnucash/gnucash-core-app.hpp +++ b/gnucash/gnucash-core-app.hpp @@ -70,5 +70,6 @@ private: char *sys_locale = nullptr; }; +void gnc_load_scm_config(void); } #endif diff --git a/gnucash/gnucash.cpp b/gnucash/gnucash.cpp index d7a76466a0..2ab37cb00f 100644 --- a/gnucash/gnucash.cpp +++ b/gnucash/gnucash.cpp @@ -73,56 +73,6 @@ namespace bl = boost::locale; static QofLogModule log_module = GNC_MOD_GUI; static gchar *userdata_migration_msg = NULL; -static void -update_message(const gchar *msg) -{ - gnc_update_splash_screen(msg, GNC_SPLASH_PERCENTAGE_UNKNOWN); - g_message("%s", msg); -} - -static void -load_system_config(void) -{ - static int is_system_config_loaded = FALSE; - gchar *system_config_dir; - gchar *system_config; - - if (is_system_config_loaded) return; - - update_message("loading system configuration"); - system_config_dir = gnc_path_get_pkgsysconfdir(); - system_config = g_build_filename(system_config_dir, "config", nullptr); - is_system_config_loaded = gfec_try_load(system_config); - g_free(system_config_dir); - g_free(system_config); -} - -static void -load_user_config(void) -{ - /* Don't continue adding to this list. When 3.0 rolls around bump - the 2.4 files off the list. */ - static const gchar *saved_report_files[] = - { - SAVED_REPORTS_FILE, SAVED_REPORTS_FILE_OLD_REV, NULL - }; - static const gchar *stylesheet_files[] = { "stylesheets-2.0", NULL}; - static int is_user_config_loaded = FALSE; - - if (is_user_config_loaded) - return; - else is_user_config_loaded = TRUE; - - update_message("loading user configuration"); - { - gchar *config_filename; - config_filename = g_build_filename (gnc_userconfig_dir (), - "config-user.scm", (char *)NULL); - gfec_try_load(config_filename); - g_free(config_filename); - } -} - static void load_gnucash_plugins() { @@ -207,11 +157,10 @@ scm_run_gnucash (void *data, [[maybe_unused]] int argc, [[maybe_unused]] char ** load_gnucash_plugins(); load_gnucash_modules(); - /* Load the config before starting up the gui. This insures that + /* Load the scm config files before starting up the gui. This ensures that * custom reports have been read into memory before the Reports * menu is created. */ - load_system_config(); - load_user_config(); + Gnucash::gnc_load_scm_config(); /* Setting-up the report menu must come after the module loading but before the gui initializat*ion. */ diff --git a/gnucash/gtkbuilder/dialog-sx.glade b/gnucash/gtkbuilder/dialog-sx.glade index 7f14ca0bb9..796a20b291 100644 --- a/gnucash/gtkbuilder/dialog-sx.glade +++ b/gnucash/gtkbuilder/dialog-sx.glade @@ -721,11 +721,11 @@ - Set 'Re_view Created Transactions' as default + Re_view created transactions True True False - Set 'Review Created Transactions' as the default in the 'Since Last Run' dialog. + Set 'Review created transactions' as the default in the "since last run" dialog. start True True diff --git a/gnucash/import-export/import-backend.c b/gnucash/import-export/import-backend.c index 60b01459bc..d9f86f6cce 100644 --- a/gnucash/import-export/import-backend.c +++ b/gnucash/import-export/import-backend.c @@ -1048,6 +1048,93 @@ gnc_import_process_trans_item (GncImportMatchMap *matchmap, return FALSE; } +/********************************************************************\ + * check_trans_online_id() Callback function used by + * gnc_import_exists_online_id. Takes pointers to transaction and split, + * returns 0 if their online_ids do NOT match, or if the split + * belongs to the transaction +\********************************************************************/ +static gint check_trans_online_id(Transaction *trans1, void *user_data) +{ + Account *account; + Split *split1; + Split *split2 = user_data; + const gchar *online_id1; + const gchar *online_id2; + + account = xaccSplitGetAccount(split2); + split1 = xaccTransFindSplitByAccount(trans1, account); + if (split1 == split2) + return 0; + + /* hack - we really want to iterate over the _splits_ of the account + instead of the transactions */ + g_assert(split1 != NULL); + + if (gnc_import_split_has_online_id(split1)) + online_id1 = gnc_import_get_split_online_id(split1); + else + online_id1 = gnc_import_get_trans_online_id(trans1); + + online_id2 = gnc_import_get_split_online_id(split2); + + if ((online_id1 == NULL) || + (online_id2 == NULL) || + (strcmp(online_id1, online_id2) != 0)) + { + return 0; + } + else + { + /*printf("test_trans_online_id(): Duplicate found\n");*/ + return 1; + } +} + +/** Checks whether the given transaction's online_id already exists in + its parent account. */ +gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash) +{ + gboolean online_id_exists = FALSE; + Account *dest_acct; + Split *source_split; + + /* Look for an online_id in the first split */ + source_split = xaccTransGetSplit(trans, 0); + g_assert(source_split); + + // No online id, no point in continuing. We'd crash if we tried. + if (!gnc_import_get_split_online_id (source_split)) + return FALSE; + // Create a hash per account of a hash of all split IDs. Then the test below will be fast if + // we have many transactions to import. + dest_acct = xaccSplitGetAccount (source_split); + if (!g_hash_table_contains (acct_id_hash, dest_acct)) + { + GHashTable* new_hash = g_hash_table_new (g_str_hash, g_str_equal); + GList* split_list = xaccAccountGetSplitList(dest_acct); + g_hash_table_insert (acct_id_hash, dest_acct, new_hash); + for (;split_list;split_list=split_list->next) + { + if (gnc_import_split_has_online_id (split_list->data)) + g_hash_table_add (new_hash, (void*) gnc_import_get_split_online_id (split_list->data)); + } + } + online_id_exists = g_hash_table_contains (g_hash_table_lookup (acct_id_hash, dest_acct), + gnc_import_get_split_online_id (source_split)); + + /* If it does, abort the process for this transaction, since it is + already in the system. */ + if (online_id_exists == TRUE) + { + DEBUG("%s", "Transaction with same online ID exists, destroying current transaction"); + xaccTransDestroy(trans); + xaccTransCommitEdit(trans); + } + return online_id_exists; +} + + /* ****************************************************************** */ diff --git a/gnucash/import-export/import-backend.h b/gnucash/import-export/import-backend.h index 69cd2bc995..b524d18359 100644 --- a/gnucash/import-export/import-backend.h +++ b/gnucash/import-export/import-backend.h @@ -57,6 +57,15 @@ typedef enum _action /** @name Non-GUI Functions */ /*@{*/ +/** Checks whether the given transaction's online_id already exists in + * its parent account. The given transaction has to be open for + * editing. If a matching online_id exists, the transaction is + * destroyed (!) and TRUE is returned, otherwise FALSE is returned. + * + * @param trans The transaction for which to check for an existing + * online_id. */ +gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_hash); + /** Evaluates the match between trans_info and split using the provided parameters. * * @param trans_info The TransInfo for the imported transaction diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c index ff779eeaee..e6c9df3611 100644 --- a/gnucash/import-export/import-main-matcher.c +++ b/gnucash/import-export/import-main-matcher.c @@ -76,6 +76,7 @@ struct _main_matcher_info gboolean add_toggled; // flag to indicate that add has been toggled to stop selection gint id; GSList* temp_trans_list; // Temporary list of imported transactions + GHashTable* acct_id_hash; // Hash table, per account, of list of transaction IDs. GSList* edited_accounts; // List of accounts currently edited. }; @@ -143,6 +144,14 @@ static gboolean query_tooltip_tree_view_cb (GtkWidget *widget, gint x, gint y, gpointer user_data); /* end local prototypes */ +static +gboolean delete_hash (gpointer key, gpointer value, gpointer user_data) +{ + // Value is a hash table that needs to be destroyed. + g_hash_table_destroy (value); + return TRUE; +} + static void update_all_balances (GNCImportMainMatcher *info) { @@ -209,6 +218,8 @@ gnc_gen_trans_list_delete (GNCImportMainMatcher *info) // We've deferred balance computations on many accounts. Let's do it now that we're done. update_all_balances (info); + g_hash_table_foreach_remove (info->acct_id_hash, delete_hash, NULL); + info->acct_id_hash = NULL; g_free (info); } @@ -1133,6 +1144,8 @@ gnc_gen_trans_init_view (GNCImportMainMatcher *info, G_CALLBACK(gnc_gen_trans_onButtonPressed_cb), info); g_signal_connect (view, "popup-menu", G_CALLBACK(gnc_gen_trans_onPopupMenu_cb), info); + + info->acct_id_hash = g_hash_table_new (g_direct_hash, g_direct_equal); } static void @@ -1713,11 +1726,16 @@ gnc_gen_trans_list_add_trans_with_ref_id (GNCImportMainMatcher *gui, Transaction g_assert (gui); g_assert (trans); - transaction_info = gnc_import_TransInfo_new (trans, NULL); - gnc_import_TransInfo_set_ref_id (transaction_info, ref_id); - // It's much faster to gather the imported transactions into a GSList than directly into the - // treeview. - gui->temp_trans_list = g_slist_prepend (gui->temp_trans_list, transaction_info); + if (gnc_import_exists_online_id (trans, gui->acct_id_hash)) + return; + else + { + transaction_info = gnc_import_TransInfo_new (trans, NULL); + gnc_import_TransInfo_set_ref_id (transaction_info, ref_id); + // It's much faster to gather the imported transactions into a GSList than directly into the + // treeview. + gui->temp_trans_list = g_slist_prepend (gui->temp_trans_list, transaction_info); + } return; } @@ -1781,6 +1799,8 @@ create_hash_of_potential_matches (GList *candidate_txns, { Account* split_account; GSList* split_list; + if (gnc_import_split_has_online_id (candidate->data)) + continue; split_account = xaccSplitGetAccount (candidate->data); /* g_hash_table_steal_extended would do the two calls in one shot but is * not available until GLib 2.58. diff --git a/gnucash/import-export/import-utilities.c b/gnucash/import-export/import-utilities.c index 4be49b323f..8d672d4e0e 100644 --- a/gnucash/import-export/import-utilities.c +++ b/gnucash/import-export/import-utilities.c @@ -57,6 +57,29 @@ void gnc_import_set_acc_online_id (Account *account, const gchar *id) xaccAccountCommitEdit (account); } +const gchar * gnc_import_get_trans_online_id (Transaction * transaction) +{ + gchar *id = NULL; + qof_instance_get (QOF_INSTANCE (transaction), "online-id", &id, NULL); + return id; +} +/* Not actually used */ +void gnc_import_set_trans_online_id (Transaction *transaction, + const gchar *id) +{ + g_return_if_fail (transaction != NULL); + xaccTransBeginEdit (transaction); + qof_instance_set (QOF_INSTANCE (transaction), "online-id", id, NULL); + xaccTransCommitEdit (transaction); +} + +gboolean gnc_import_trans_has_online_id(Transaction * transaction) +{ + const gchar * online_id; + online_id = gnc_import_get_trans_online_id(transaction); + return (online_id != NULL && strlen(online_id) > 0); +} + const gchar * gnc_import_get_split_online_id (Split * split) { gchar *id = NULL; diff --git a/gnucash/import-export/import-utilities.h b/gnucash/import-export/import-utilities.h index a39d641571..1ef733d8b5 100644 --- a/gnucash/import-export/import-utilities.h +++ b/gnucash/import-export/import-utilities.h @@ -49,6 +49,17 @@ const gchar * gnc_import_get_acc_online_id(Account * account); void gnc_import_set_acc_online_id(Account * account, const gchar * string_value); /** @} */ +/** @name Setter-getters + Setter and getter functions for the online_id field for + Transactions. + @{ +*/ +const gchar * gnc_import_get_trans_online_id(Transaction * transaction); +void gnc_import_set_trans_online_id(Transaction * transaction, + const gchar * string_value); +/** @} */ + +gboolean gnc_import_trans_has_online_id(Transaction * transaction); /** @name Setter-getters Setter and getter functions for the online_id field for diff --git a/gnucash/report/reports/example/hello-world.scm b/gnucash/report/reports/example/hello-world.scm index 77f8d55525..2313fbb637 100644 --- a/gnucash/report/reports/example/hello-world.scm +++ b/gnucash/report/reports/example/hello-world.scm @@ -94,15 +94,6 @@ (lambda () (cons 'absolute (current-time))) #f 'absolute #f )) - ;; This is another date option, but the user can also select - ;; the time. - (add-option - (gnc:make-date-option - (N_ "Hello, World!") (N_ "Time and Date Option") - "e" (N_ "This is a date option with time.") - (lambda () (cons 'absolute (current-time))) - #t 'absolute #f )) - (add-option (gnc:make-date-option (N_ "Hello, World!") (N_ "Combo Date Option") @@ -234,8 +225,6 @@ option like this.") (string-val (op-value "Hello, World!" "String Option")) (date-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Just a Date Option"))) - (date2-val (gnc:date-option-absolute-time - (op-value "Hello, World!" "Time and Date Option"))) (rel-date-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Relative Date Option"))) (combo-date-val (gnc:date-option-absolute-time @@ -258,7 +247,6 @@ option like this.") ;; qof-print-date (let ((time-string (gnc-print-time64 (current-time) "%X")) (date-string (gnc-print-time64 date-val "%x")) - (date-string2 (gnc-print-time64 date2-val "%x %X")) (rel-date-string (gnc-print-time64 rel-date-val "%x")) (combo-date-string (gnc-print-time64 combo-date-val "%x"))) @@ -369,11 +357,6 @@ new, totally cool report, consult the mailing list ~a.") (G_ "The date option is ~a.") (gnc:html-markup-b date-string))) - (gnc:html-markup-p - (gnc:html-markup/format - (G_ "The date and time option is ~a.") - (gnc:html-markup-b date-string2))) - (gnc:html-markup-p (gnc:html-markup/format (G_ "The relative date option is ~a.") diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 5befd47437..8481572cbb 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -719,10 +719,14 @@ the option '~a'.")) (begin (rpterror-earlier "date" item (car full-list)) 0))) - (let* ((value (default-getter)) + (if show-time + (issue-deprecation-warning + (format #f "Date options with time of day values are deprecated and will be removed in GnuCash 5."))) + + (let* ((value (default-getter)) (value->string (lambda () (string-append "'" (gnc:value->string value))))) - (gnc:make-option + (gnc:make-option section name sort-tag 'date documentation-string (lambda () value) (lambda (date) diff --git a/libgnucash/backend/xml/gnc-xml-backend.cpp b/libgnucash/backend/xml/gnc-xml-backend.cpp index 236afa80b4..8b8a3059f1 100644 --- a/libgnucash/backend/xml/gnc-xml-backend.cpp +++ b/libgnucash/backend/xml/gnc-xml-backend.cpp @@ -171,8 +171,11 @@ GncXmlBackend::session_end() if (!m_linkfile.empty()) g_unlink (m_linkfile.c_str()); - if (m_lockfd > 0) + if (m_lockfd != -1) + { close (m_lockfd); + m_lockfd = -1; + } if (!m_lockfile.empty()) { @@ -638,12 +641,13 @@ GncXmlBackend::get_file_lock () { /* oops .. file is locked by another user .. */ set_error(ERR_BACKEND_LOCKED); + m_lockfile.clear(); return false; } m_lockfd = g_open (m_lockfile.c_str(), O_RDWR | O_CREAT | O_EXCL , S_IRUSR | S_IWUSR); - if (m_lockfd < 0) + if (m_lockfd == -1) { /* oops .. we can't create the lockfile .. */ switch (errno) @@ -661,87 +665,11 @@ GncXmlBackend::get_file_lock () PWARN ("Unable to create the lockfile %s: %s", m_lockfile.c_str(), strerror(errno)); set_error(be_err); + m_lockfile.clear(); return false; } - /* OK, now work around some NFS atomic lock race condition - * mumbo-jumbo. We do this by linking a unique file, and - * then examining the link count. At least that's what the - * NFS programmers guide suggests. - * Note: the "unique filename" must be unique for the - * triplet filename-host-process, otherwise accidental - * aliases can occur. - */ - - /* apparently, even this code may not work for some NFS - * implementations. In the long run, I am told that - * ftp.debian.org - * /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz - * provides a better long-term solution. - */ - -#ifndef G_OS_WIN32 - auto path = m_lockfile.find_last_of('.'); - std::stringstream linkfile; - if (path != std::string::npos) - linkfile << m_lockfile.substr(0, path); - else - linkfile << m_lockfile; - linkfile << "." << gethostid() << "." << getpid() << ".LNK"; - rc = link (m_lockfile.c_str(), linkfile.str().c_str()); - if (rc) - { - /* If hard links aren't supported, just allow the lock. */ - if (errno == EPERM || errno == ENOSYS -# ifdef EOPNOTSUPP - || errno == EOPNOTSUPP -# endif -# ifdef ENOTSUP - || errno == ENOTSUP -# endif - ) - { - return true; - } - - /* Otherwise, something else is wrong. */ - set_error(ERR_BACKEND_LOCKED); - g_unlink (linkfile.str().c_str()); - close (m_lockfd); - g_unlink (m_lockfile.c_str()); - return false; - } - - rc = g_stat (m_lockfile.c_str(), &statbuf); - if (rc) - { - /* oops .. stat failed! This can't happen! */ - set_error(ERR_BACKEND_LOCKED); - std::string msg{"Failed to stat lockfile "}; - set_message(msg + m_lockfile); - g_unlink (linkfile.str().c_str()); - close (m_lockfd); - g_unlink (m_lockfile.c_str()); - return false; - } - - if (statbuf.st_nlink != 2) - { - set_error(ERR_BACKEND_LOCKED); - g_unlink (linkfile.str().c_str()); - close (m_lockfd); - g_unlink (m_lockfile.c_str()); - return false; - } - - m_linkfile = linkfile.str(); return true; - -#else /* ifndef G_OS_WIN32 */ - /* On windows, there is no NFS and the open(,O_CREAT | O_EXCL) - is sufficient for locking. */ - return true; -#endif /* ifndef G_OS_WIN32 */ } bool diff --git a/libgnucash/backend/xml/gnc-xml-backend.hpp b/libgnucash/backend/xml/gnc-xml-backend.hpp index 1e6ff76e81..ab98a7b612 100644 --- a/libgnucash/backend/xml/gnc-xml-backend.hpp +++ b/libgnucash/backend/xml/gnc-xml-backend.hpp @@ -60,7 +60,7 @@ private: std::string m_dirname; std::string m_lockfile; std::string m_linkfile; - int m_lockfd; + int m_lockfd = -1; QofBook* m_book = nullptr; /* The primary, main open book */ }; diff --git a/libgnucash/engine/SX-book.c b/libgnucash/engine/SX-book.c index e15cb57f0b..d8d8c27db6 100644 --- a/libgnucash/engine/SX-book.c +++ b/libgnucash/engine/SX-book.c @@ -127,7 +127,7 @@ sxtg_book_begin (QofBook *book) static void sxtg_book_end (QofBook *book) { -// gnc_book_set_template_root (book, NULL); + gnc_book_set_template_root (book, NULL); } static gboolean diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c index 888378cdee..de14940e4c 100644 --- a/libgnucash/engine/Scrub.c +++ b/libgnucash/engine/Scrub.c @@ -612,7 +612,7 @@ gnc_transaction_get_commodity_imbalance (Transaction *trans, /* GFunc wrapper for xaccSplitDestroy */ static void -destroy_split (void* ptr, void* data) +destroy_split (void* ptr) { Split *split = GNC_SPLIT (ptr); if (split) @@ -642,7 +642,10 @@ xaccTransClearTradingSplits (Transaction *trans) return; xaccTransBeginEdit (trans); - g_list_foreach (trading_splits, destroy_split, NULL); + /* destroy_splits doesn't actually free the splits but this gets + * the list ifself freed. + */ + g_list_free_full (trading_splits, destroy_split); xaccTransCommitEdit (trans); } diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c index d771e1f8bf..31ede94c61 100644 --- a/libgnucash/engine/Split.c +++ b/libgnucash/engine/Split.c @@ -2107,6 +2107,7 @@ xaccSplitGetOtherSplit (const Split *split) { Split *s = n->data; if ((s == split) || + (!xaccTransStillHasSplit(trans, s)) || (xaccAccountGetType (xaccSplitGetAccount (s)) == ACCT_TYPE_TRADING) || (qof_instance_has_slot (QOF_INSTANCE (s), "lot-split"))) continue; diff --git a/libgnucash/engine/cashobjects.c b/libgnucash/engine/cashobjects.c index 903705d342..fd3ae34260 100644 --- a/libgnucash/engine/cashobjects.c +++ b/libgnucash/engine/cashobjects.c @@ -73,8 +73,8 @@ cashobjects_register(void) g_return_val_if_fail(xaccAccountRegister(), FALSE); g_return_val_if_fail ( xaccTransRegister(), FALSE); g_return_val_if_fail ( xaccSplitRegister(), FALSE); - g_return_val_if_fail ( SXRegister (), FALSE); g_return_val_if_fail ( gnc_sxtt_register(), FALSE); + g_return_val_if_fail ( SXRegister (), FALSE); g_return_val_if_fail(gnc_pricedb_register(), FALSE); g_return_val_if_fail (gnc_budget_register(), FALSE); g_return_val_if_fail ( gnc_lot_register (), FALSE); diff --git a/libgnucash/engine/gncTaxTable.c b/libgnucash/engine/gncTaxTable.c index 3cd689db5c..2e0cbfa907 100644 --- a/libgnucash/engine/gncTaxTable.c +++ b/libgnucash/engine/gncTaxTable.c @@ -704,7 +704,7 @@ GncTaxTableList * gncTaxTableGetTables (QofBook *book) if (!book) return NULL; bi = qof_book_get_data (book, _GNC_MOD_NAME); - return bi->tables; + return bi ? bi->tables : NULL; } const char *gncTaxTableGetName (const GncTaxTable *table) diff --git a/libgnucash/engine/mocks/gmock-Account.cpp b/libgnucash/engine/mocks/gmock-Account.cpp index b4dd7d28fc..aea26ed818 100644 --- a/libgnucash/engine/mocks/gmock-Account.cpp +++ b/libgnucash/engine/mocks/gmock-Account.cpp @@ -60,6 +60,15 @@ xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, return mockaccount ? mockaccount->for_each_transaction(proc, data) : 0; } +SplitList * +xaccAccountGetSplitList (const Account *account) +{ + SCOPED_TRACE(""); + auto mockaccount = gnc_mockaccount(account); + return mockaccount ? mockaccount->xaccAccountGetSplitList() : nullptr; +} + + GncImportMatchMap * gnc_account_imap_create_imap (Account *acc) { diff --git a/libgnucash/engine/mocks/gmock-Account.h b/libgnucash/engine/mocks/gmock-Account.h index 6b7bbaefa3..07a249b1e2 100644 --- a/libgnucash/engine/mocks/gmock-Account.h +++ b/libgnucash/engine/mocks/gmock-Account.h @@ -43,6 +43,7 @@ public: MOCK_METHOD0(commit_edit, void()); MOCK_CONST_METHOD0(get_book, QofBook*()); MOCK_CONST_METHOD2(for_each_transaction, gint(TransactionCallback, void*)); + MOCK_CONST_METHOD0(xaccAccountGetSplitList, SplitList*()); MOCK_METHOD0(create_imap, GncImportMatchMap*()); protected: diff --git a/libgnucash/quotes/gnc-fq-update.in b/libgnucash/quotes/gnc-fq-update.in index 797a6d1894..dbbcf675a0 100755 --- a/libgnucash/quotes/gnc-fq-update.in +++ b/libgnucash/quotes/gnc-fq-update.in @@ -36,6 +36,7 @@ if ($( != 0) { exit 0 if ($input ne "y"); } +CPAN::Shell->install('Test2'); #Required by an F::Q dependency but cpan doesn't notice. CPAN::Shell->install('Date::Manip'); #Required by gnc-fq-helper CPAN::Shell->install('Finance::Quote'); diff --git a/po/de.po b/po/de.po index a06d46b216..42e40c6b67 100644 --- a/po/de.po +++ b/po/de.po @@ -36,7 +36,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-16 20:35+0000\n" +"PO-Revision-Date: 2021-09-05 21:35+0000\n" "Last-Translator: Christian Wehling \n" "Language-Team: German \n" @@ -45,7 +45,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.8-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: bindings/guile/commodity-table.scm:44 msgid "ALL NON-CURRENCY" @@ -17134,7 +17134,7 @@ msgstr "Zahl, Datum, Zeit" # erreichbar. Können wir das vereinheitlichen? #: gnucash/gtkbuilder/dialog-preferences.glade:1417 msgid "Perform account list _setup on new file" -msgstr "Bei neuer Datei erstellen: Konten_einrichtung anzeigen" +msgstr "Bei neuer Datei erstellen: Konten_einrichtung starten" #: gnucash/gtkbuilder/dialog-preferences.glade:1423 msgid "Present the new account list dialog when you choose File->New File." @@ -18328,7 +18328,7 @@ msgstr "Voreinstellungen Buchungseditor" #: gnucash/gtkbuilder/dialog-sx.glade:545 msgid "_Run when data file opened" -msgstr "Seit-Letztem-Aufruf Fenster starten, wenn eine _Datei geöffnet wird" +msgstr "Ausführen, wenn eine _Datei geöffnet wird" #: gnucash/gtkbuilder/dialog-sx.glade:549 msgid "Run the \"since last run\" process when a file is opened." @@ -18388,10 +18388,8 @@ msgid "R_emind in advance" msgstr "Im Voraus er_innern" #: gnucash/gtkbuilder/dialog-sx.glade:724 -#, fuzzy -#| msgid "_Review created transactions" msgid "Set 'Re_view Created Transactions' as default" -msgstr "Erzeugte Buchungen _durchsehen" +msgstr "'Erstellte _Buchungen durchsehen' als Standard festlegen" #: gnucash/gtkbuilder/dialog-sx.glade:728 msgid "" diff --git a/po/zh_CN.po b/po/zh_CN.po index e3f477f3c1..6674dc8e9f 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-31 14:33+0000\n" +"PO-Revision-Date: 2021-09-10 16:33+0000\n" "Last-Translator: TianXing_Yi \n" "Language-Team: Chinese (Simplified) \n" @@ -32,7 +32,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.8.1-dev\n" +"X-Generator: Weblate 4.9-dev\n" "X-Bugs: Report translation errors to the Language-Team address.\n" #: bindings/guile/commodity-table.scm:44 @@ -2162,7 +2162,7 @@ msgstr "入账日期" #: gnucash/gnome/dialog-invoice.c:872 msgid "Post to Account" -msgstr "入账到科目" +msgstr "入账科目" #: gnucash/gnome/dialog-invoice.c:873 msgid "Accumulate Splits?" @@ -3643,7 +3643,7 @@ msgstr "打开查找项目对话框" #: gnucash/gnome/gnc-plugin-business.c:238 #: gnucash/gnome/gnc-plugin-business.c:271 msgid "_Process Payment..." -msgstr "处理收付(_P)..." +msgstr "收付(_P)..." #: gnucash/gnome/gnc-plugin-business.c:196 #: gnucash/gnome/gnc-plugin-business.c:239 @@ -4946,7 +4946,7 @@ msgstr "显示员工报表" #: gnucash/gnome/gnc-plugin-page-owner-tree.c:293 msgid "New Voucher" -msgstr "新建支出凭证" +msgstr "新建报销" #: gnucash/gnome/gnc-plugin-page-owner-tree.c:446 msgid "Owners" @@ -9239,7 +9239,7 @@ msgstr "重新计算(_R)" #: gnucash/report/trep-engine.scm:168 gnucash/report/trep-engine.scm:956 #: gnucash/report/trep-engine.scm:1045 msgid "Account Name" -msgstr "科目名称" +msgstr "名称" #: gnucash/gnome-utils/gnc-tree-view-account.c:816 #: gnucash/gnome-utils/gnc-tree-view-split-reg.c:2919 @@ -10877,13 +10877,13 @@ msgstr "" msgid "" "Set \"Review Created Transactions\" as the default for the \"since last run" "\" dialog." -msgstr "" +msgstr "将 \"审查创建的交易 \"设为 \"自上次运行以来 \"对话框的默认值。" #: gnucash/gschemas/org.gnucash.dialogs.sxs.gschema.xml.in:28 msgid "" "This setting controls whether as default the \"review created transactions\" " "is set for the \"since last run\" dialog." -msgstr "" +msgstr "此设置控制 \"自上次运行以来 \"对话框是否默认设置 \"审查已创建的交易\"。" #: gnucash/gschemas/org.gnucash.dialogs.sxs.gschema.xml.in:35 msgid "Set the \"auto create\" flag by default" @@ -11238,7 +11238,7 @@ msgstr "新打开的标签页前置,而非最后" msgid "" "If active, new tabs are opened adjacent to current tab. If inactive, the new " "tabs are opened instead at the end." -msgstr "" +msgstr "如果激活,新标签会在当前标签的旁边打开。如果不激活,新标签将在最后打开。" #: gnucash/gschemas/org.gnucash.gschema.xml.in:140 #: gnucash/gtkbuilder/dialog-preferences.glade:940 @@ -11330,7 +11330,7 @@ msgstr "最大的几个月又回到过去。" msgid "" "Dates will be completed so that they are close to the current date. Enter " "the maximum number of months to go backwards in time when completing dates." -msgstr "录入月份和本月差值在限额内(小于等于),即本年;超出(大于)为明年。" +msgstr "相隔月数小于等于设定值为本年,大于设定值为明年。" #: gnucash/gschemas/org.gnucash.gschema.xml.in:180 msgid "Show Horizontal Grid Lines" @@ -15973,7 +15973,7 @@ msgstr "日期补全" #: gnucash/gtkbuilder/dialog-preferences.glade:1112 msgid "When a date is entered without year, it should be taken" -msgstr "输入的日期没有年份时,其应属于" +msgstr "没有年份的日期,其应属于" #: gnucash/gtkbuilder/dialog-preferences.glade:1128 msgid "" @@ -15984,9 +15984,7 @@ msgstr "日期年份补全为本年。" msgid "" "In a sliding 12-month window starting this\n" "many months before the current month" -msgstr "" -"从现在开始这个次数,它追溯到过去,\n" -"它被认为是那个月的12个月内的日期" +msgstr "相隔月数定年份" #: gnucash/gtkbuilder/dialog-preferences.glade:1165 msgid "Enter number of months." @@ -16257,13 +16255,13 @@ msgstr "不可能的匹配日阈值 (_U)" msgid "" "A transaction whose date is within the threshold is likely to be a match. " "Default is 4 days." -msgstr "" +msgstr "日期在阈值内的交易可能是匹配的。默认为4天。" #: gnucash/gtkbuilder/dialog-preferences.glade:2340 msgid "" "A transaction whose date is outside the threshold is unlikely to be a match. " "Default is 14 days." -msgstr "" +msgstr "日期在阈值之外的交易不太可能是匹配的。默认为14天。" #: gnucash/gtkbuilder/dialog-preferences.glade:2385 msgid "Checks" @@ -17203,7 +17201,7 @@ msgstr "默认复核已创建交易(_R)" msgid "" "Set 'Review Created Transactions' as the default in the 'Since Last Run' " "dialog." -msgstr "" +msgstr "在 \"自上次运行 \"对话框中,将 \"审查创建的交易 \"设置为默认值。" #: gnucash/gtkbuilder/dialog-sx.glade:765 msgid "Edit Scheduled Transaction" @@ -17356,7 +17354,7 @@ msgstr "GnuCash 每日提示" #: gnucash/gtkbuilder/dialog-totd.glade:26 msgid "_Previous" -msgstr "去年末(_P)" +msgstr "上一个(_P)" #: gnucash/gtkbuilder/dialog-totd.glade:41 msgid "_Next" @@ -21349,7 +21347,7 @@ msgstr "小计" #: gnucash/report/reports/standard/owner-report.scm:56 #: libgnucash/app-utils/business-options.scm:78 msgid "Tax" -msgstr "税务" +msgstr "税额" #: gnucash/register/ledger-core/gncEntryLedgerModel.c:127 msgid "Billable?" @@ -21968,7 +21966,7 @@ msgstr "价格信息的来源。" #: gnucash/report/options-utilities.scm:144 msgid "Average cost of purchases weighted by volume" -msgstr "" +msgstr "按数量加权的平均采购成本" #: gnucash/report/options-utilities.scm:145 msgid "Weighted average of all transactions in the past" @@ -22103,7 +22101,7 @@ msgstr "收入支出(_I)" #: gnucash/report/report-core.scm:154 msgid "_Taxes" -msgstr "税务(_U)" +msgstr "税额(_U)" #: gnucash/report/report-core.scm:155 msgid "E_xamples" @@ -24537,7 +24535,7 @@ msgstr "以逐条求和计算么?" #: gnucash/report/reports/standard/budget-barchart.scm:142 msgid "Select which chart type to use." -msgstr "" +msgstr "选择要使用的图表类型。" #: gnucash/report/reports/standard/budget-barchart.scm:144 #: gnucash/report/reports/standard/category-barchart.scm:156