From 0de4ce1106e2247e4f6d221833a7c2b8459f2533 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 17 Jun 2024 13:36:54 +0100 Subject: [PATCH 1/3] Bug 799047 - AutoComplete Only Considers Visible Transactions In the past, all transactions were used on the first load of the register and this is when the autocomplete would be populated, on subsequent loads it would be a filtered list. With the change to delay loading, the first load is empty and the autocomplete is now being populated by the filtered list. To fix this, make a copy of the original query and compare this query to the query used for refresh. When different, pass a second list of splits to the register to populate the autocomplete lists. --- gnucash/gnome/dialog-sx-editor.c | 2 +- .../register/ledger-core/gnc-ledger-display.c | 14 +++++- .../ledger-core/split-register-load.c | 46 ++++++++++++++++--- gnucash/register/ledger-core/split-register.h | 4 +- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c index 4c1a54ee91..f8144effed 100644 --- a/gnucash/gnome/dialog-sx-editor.c +++ b/gnucash/gnome/dialog-sx-editor.c @@ -1509,7 +1509,7 @@ schedXact_editor_populate (GncSxEditorDialog *sxed) if (splitList) { splitReg = gnc_ledger_display_get_split_register (sxed->ledger); - gnc_split_register_load (splitReg, splitList, NULL); + gnc_split_register_load (splitReg, splitList, NULL, NULL); } /* otherwise, use the existing stuff. */ g_list_free (splitList); } diff --git a/gnucash/register/ledger-core/gnc-ledger-display.c b/gnucash/register/ledger-core/gnc-ledger-display.c index bf0f48a183..2c0dbf6de2 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display.c +++ b/gnucash/register/ledger-core/gnc-ledger-display.c @@ -59,6 +59,7 @@ struct gnc_ledger_display GncGUID leader; Query* query; + Query* pre_filter_query; GNCLedgerDisplayType ld_type; @@ -655,6 +656,9 @@ close_handler (gpointer user_data) qof_query_destroy (ld->query); ld->query = NULL; + qof_query_destroy (ld->pre_filter_query); + ld->pre_filter_query = NULL; + g_free (ld); } @@ -834,6 +838,8 @@ gnc_ledger_display_internal (Account* lead_account, Query* q, else gnc_ledger_display_make_query (ld, limit, reg_type); + ld->pre_filter_query = qof_query_copy (ld->query); + ld->component_id = gnc_register_gui_component (klass, refresh_handler, close_handler, ld); @@ -854,7 +860,7 @@ gnc_ledger_display_internal (Account* lead_account, Query* q, * the query when we're not in focus yet. */ ld->loading = TRUE; - gnc_split_register_load (ld->reg, NULL, gnc_ledger_display_leader (ld)); + gnc_split_register_load (ld->reg, NULL, NULL, gnc_ledger_display_leader (ld)); ld->loading = FALSE; return ld; } @@ -888,6 +894,7 @@ static void gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld) { GList* splits; + GList* pre_filter_splits = NULL; if (ld->loading) return; @@ -899,6 +906,9 @@ gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld) */ splits = qof_query_run (ld->query); + if (!qof_query_equal (ld->query, ld->pre_filter_query)) + pre_filter_splits = qof_query_run (ld->pre_filter_query); + gnc_ledger_display_set_watches (ld, splits); if (!gnc_split_register_full_refresh_ok (ld->reg)) @@ -906,7 +916,7 @@ gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld) ld->loading = TRUE; - gnc_split_register_load (ld->reg, splits, + gnc_split_register_load (ld->reg, splits, pre_filter_splits, gnc_ledger_display_leader (ld)); ld->needs_refresh = FALSE; diff --git a/gnucash/register/ledger-core/split-register-load.c b/gnucash/register/ledger-core/split-register-load.c index 1f756e07b0..d6963ed316 100644 --- a/gnucash/register/ledger-core/split-register-load.c +++ b/gnucash/register/ledger-core/split-register-load.c @@ -256,8 +256,9 @@ _find_split_with_parent_txn (gconstpointer a, gconstpointer b) return xaccSplitGetParent (split) == txn ? 0 : 1; } -static void add_quickfill_completions (TableLayout* layout, Transaction* trans, - Split* split, gboolean has_last_num) +static void +add_quickfill_completions (TableLayout* layout, Transaction* trans, + Split* split, gboolean has_last_num) { gnc_quickfill_cell_add_completion ( (QuickFillCell*) gnc_table_layout_get_cell (layout, NOTES_CELL), @@ -365,9 +366,30 @@ update_info (SRInfo* info, SplitRegister* reg) info->reg_loaded = TRUE; } +static void +add_completions_from_pre_filter_slist (TableLayout* layout, GList *pre_filter_slist, + gboolean first_pass, gboolean quickfill_setup, + gboolean has_last_num) +{ + GList *node; + + for (node = pre_filter_slist; node; node = node->next) + { + Split *split = node->data; + Transaction *trans = xaccSplitGetParent (split); + + gnc_completion_cell_add_menu_item ( + (CompletionCell*) gnc_table_layout_get_cell (layout, DESC_CELL), + xaccTransGetDescription (trans)); + + if (!first_pass && !quickfill_setup) + add_quickfill_completions (layout, trans, split, has_last_num); + } +} + void gnc_split_register_load (SplitRegister* reg, GList* slist, - Account* default_account) + GList* pre_filter_slist, Account* default_account) { SRInfo* info; Transaction* pending_trans; @@ -635,6 +657,13 @@ gnc_split_register_load (SplitRegister* reg, GList* slist, (CompletionCell*) gnc_table_layout_get_cell (reg->table->layout, DESC_CELL), table->model->reverse_sort); + if (!info->first_pass && pre_filter_slist) + { + add_completions_from_pre_filter_slist (reg->table->layout, pre_filter_slist, + info->first_pass, info->quickfill_setup, + has_last_num); + } + /* populate the table */ for (node = slist; node; node = node->next) { @@ -752,12 +781,15 @@ gnc_split_register_load (SplitRegister* reg, GList* slist, /* On first load the split list is empty so fill up the quickfill cells * only on the next load. */ - if (!info->first_pass && !info->quickfill_setup) + if (!info->first_pass && !pre_filter_slist && !info->quickfill_setup) add_quickfill_completions (reg->table->layout, trans, split, has_last_num); - gnc_completion_cell_add_menu_item ( - (CompletionCell*) gnc_table_layout_get_cell (reg->table->layout, DESC_CELL), - xaccTransGetDescription (trans)); + if (!info->first_pass && !pre_filter_slist) + { + gnc_completion_cell_add_menu_item ( + (CompletionCell*) gnc_table_layout_get_cell (reg->table->layout, DESC_CELL), + xaccTransGetDescription (trans)); + } if (trans == find_trans) new_trans_row = vcell_loc.virt_row; diff --git a/gnucash/register/ledger-core/split-register.h b/gnucash/register/ledger-core/split-register.h index 5274c6aaec..99b2a79cd8 100644 --- a/gnucash/register/ledger-core/split-register.h +++ b/gnucash/register/ledger-core/split-register.h @@ -530,10 +530,12 @@ void gnc_split_register_cancel_cursor_trans_changes (SplitRegister* reg); * * @param slist a list of splits * + * @param pre_filter_slist the list of splits before applying filter + * * @param default_account an account to provide defaults for the blank split */ void gnc_split_register_load (SplitRegister* reg, GList* slist, - Account* default_account); + GList* pre_filter_slist, Account* default_account); /** Copy the contents of the current cursor to a split. The split and * transaction that are updated are the ones associated with the From 8bd7deb3dcccd150df6e1886312358cb95677df4 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 17 Jun 2024 14:31:53 +0100 Subject: [PATCH 2/3] Reapply filter when number of sub-accounts change When viewing a register that displays sub-accounts, if the number of sub-accounts is changed, the query is rebuilt from the remaining sub-accounts but when a filter is being used it is not applied after. Fixed by checking the original query for a filter and if so applying that after query rebuilt. --- .../register/ledger-core/gnc-ledger-display.c | 24 +++++++++++ libgnucash/engine/Query.cpp | 42 ++++++++++++++++++- libgnucash/engine/Query.h | 2 + libgnucash/engine/qofquerycore.cpp | 12 ++++++ libgnucash/engine/qofquerycore.h | 2 + 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/gnucash/register/ledger-core/gnc-ledger-display.c b/gnucash/register/ledger-core/gnc-ledger-display.c index 2c0dbf6de2..43e68677cf 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display.c +++ b/gnucash/register/ledger-core/gnc-ledger-display.c @@ -948,10 +948,30 @@ gnc_ledger_display_refresh (GNCLedgerDisplay* ld) GList* accounts = gnc_account_get_descendants (leader); if (g_list_length (accounts) != ld->number_of_subaccounts) + { + time64 start_time, end_time; + xaccQueryGetDateMatchTT (ld->query, &start_time, &end_time); + + cleared_match_t cleared_match = xaccQueryGetClearedMatch (ld->query); + gnc_ledger_display_make_query (ld, gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_MAX_TRANS), gnc_get_reg_type (leader, ld->ld_type)); + qof_query_destroy (ld->pre_filter_query); + ld->pre_filter_query = qof_query_copy (ld->query); + + if (cleared_match != CLEARED_ALL) + xaccQueryAddClearedMatch (ld->query, cleared_match, QOF_QUERY_AND); + + if (start_time || end_time) + { + xaccQueryAddDateMatchTT (ld->query, + start_time != 0, start_time, + end_time != 0, end_time, + QOF_QUERY_AND); + } + } g_list_free (accounts); } @@ -963,8 +983,12 @@ gnc_ledger_display_refresh (GNCLedgerDisplay* ld) * -- jsled */ // Exclude any template accounts for search register and gl if (!ld->reg->is_template && (ld->reg->type == SEARCH_LEDGER || ld->ld_type == LD_GL)) + { exclude_template_accounts (ld->query, ld->excluded_template_acc_hash); + qof_query_destroy (ld->pre_filter_query); + ld->pre_filter_query = qof_query_copy (ld->query); + } gnc_ledger_display_refresh_internal (ld); LEAVE (" "); } diff --git a/libgnucash/engine/Query.cpp b/libgnucash/engine/Query.cpp index 38e9f4f77a..a1e307b5b4 100644 --- a/libgnucash/engine/Query.cpp +++ b/libgnucash/engine/Query.cpp @@ -502,6 +502,44 @@ xaccQueryAddClearedMatch(QofQuery * q, cleared_match_t how, QofQueryOp op) qof_query_add_term (q, param_list, pred_data, op); } +cleared_match_t +xaccQueryGetClearedMatch(QofQuery * q) +{ + QofQueryPredData *term_data; + cleared_match_t cleared_match = CLEARED_ALL; + GSList *param_list; + GSList *terms, *tmp; + char *chars = nullptr; + + param_list = qof_query_build_param_list (SPLIT_RECONCILE, nullptr); + terms = qof_query_get_term_type (q, param_list); + g_slist_free (param_list); + + for (tmp = terms; tmp; tmp = g_slist_next (tmp)) + { + term_data = static_cast(tmp->data); + + if (qof_query_char_predicate_get_char (term_data, &chars)) + { + cleared_match = CLEARED_NONE; + + if (strstr (chars, "c")) + cleared_match = (cleared_match_t)(cleared_match | CLEARED_CLEARED); + if (strstr (chars, "y")) + cleared_match = (cleared_match_t)(cleared_match | CLEARED_RECONCILED); + if (strstr (chars, "f")) + cleared_match = (cleared_match_t)(cleared_match | CLEARED_FROZEN); + if (strstr (chars, "n")) + cleared_match = (cleared_match_t)(cleared_match | CLEARED_NO); + if (strstr (chars, "v")) + cleared_match = (cleared_match_t)(cleared_match | CLEARED_VOIDED); + } + } + g_slist_free (terms); + + return cleared_match; +} + void xaccQueryAddGUIDMatch(QofQuery * q, const GncGUID *guid, QofIdType id_type, QofQueryOp op) @@ -532,8 +570,8 @@ xaccQueryAddGUIDMatch(QofQuery * q, const GncGUID *guid, void xaccQueryAddClosingTransMatch(QofQuery *q, gboolean value, QofQueryOp op) { - GSList *param_list; - + GSList *param_list; + param_list = qof_query_build_param_list(SPLIT_TRANS, TRANS_IS_CLOSING, nullptr); qof_query_add_boolean_match(q, param_list, value, op); } diff --git a/libgnucash/engine/Query.h b/libgnucash/engine/Query.h index 096e4f6af8..e23ff604a6 100644 --- a/libgnucash/engine/Query.h +++ b/libgnucash/engine/Query.h @@ -180,6 +180,8 @@ typedef enum } cleared_match_t; void xaccQueryAddClearedMatch(QofQuery * q, cleared_match_t how, QofQueryOp op); +cleared_match_t xaccQueryGetClearedMatch(QofQuery * q); + void xaccQueryAddGUIDMatch(QofQuery * q, const GncGUID *guid, QofIdType id_type, QofQueryOp op); diff --git a/libgnucash/engine/qofquerycore.cpp b/libgnucash/engine/qofquerycore.cpp index 4b4aeec279..4d67271bcf 100644 --- a/libgnucash/engine/qofquerycore.cpp +++ b/libgnucash/engine/qofquerycore.cpp @@ -1205,6 +1205,18 @@ qof_query_char_predicate (QofCharMatch options, const char *chars) return ((QofQueryPredData*)pdata); } +gboolean +qof_query_char_predicate_get_char (const QofQueryPredData *pd, char **chars) +{ + const query_char_t pdata = (const query_char_t)pd; + + if (pdata->pd.type_name != query_char_type) + return FALSE; + + *chars = g_strdup (pdata->char_list); + return TRUE; +} + static char * char_to_string (gpointer object, QofParam *getter) { diff --git a/libgnucash/engine/qofquerycore.h b/libgnucash/engine/qofquerycore.h index 152fb0aa12..a92b47b1bc 100644 --- a/libgnucash/engine/qofquerycore.h +++ b/libgnucash/engine/qofquerycore.h @@ -184,6 +184,8 @@ void qof_query_core_predicate_free (QofQueryPredData *pdata); /** Retrieve a predicate. */ gboolean qof_query_date_predicate_get_date (const QofQueryPredData *pd, time64 *date); +gboolean qof_query_char_predicate_get_char (const QofQueryPredData *pd, char **chars); + /** Return a printable string for a core data object. Caller needs * to g_free() the returned string. */ From 3a562e5dbbbd2e4874a0a625f30d8f0f89d33c86 Mon Sep 17 00:00:00 2001 From: Robert Fewell <14uBobIT@gmail.com> Date: Mon, 17 Jun 2024 14:41:12 +0100 Subject: [PATCH 3/3] Incorrect default start date for General Ledger The default for the general ledger is 30 days so use a time64 and subtract from that instead of subtracting from tm.tm_mon. --- gnucash/register/ledger-core/gnc-ledger-display.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gnucash/register/ledger-core/gnc-ledger-display.c b/gnucash/register/ledger-core/gnc-ledger-display.c index 43e68677cf..ae632ef782 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display.c +++ b/gnucash/register/ledger-core/gnc-ledger-display.c @@ -445,7 +445,6 @@ gnc_ledger_display_gl (void) { Query* query; time64 start; - struct tm tm; GNCLedgerDisplay* ld; GHashTable *exclude_template_accounts_hash; @@ -464,11 +463,12 @@ gnc_ledger_display_gl (void) * See Gnome Bug 86302. * -- jsled */ // Exclude any template accounts for search register and gl - exclude_template_accounts (query, exclude_template_accounts_hash); + exclude_template_accounts (query, exclude_template_accounts_hash); + + // the default is to show last 30 days + static const time64 secs_per_thirty_days = 2592000; + start = gnc_time64_get_today_start () - secs_per_thirty_days; - gnc_tm_get_today_start (&tm); - tm.tm_mon--; /* Default the register to the last month's worth of transactions. */ - start = gnc_mktime (&tm); xaccQueryAddDateMatchTT (query, TRUE, start, FALSE, 0,