GnuCash  5.6-150-g038405b370+
import-match-picker.cpp
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or *
3  * modify it under the terms of the GNU General Public License as *
4  * published by the Free Software Foundation; either version 2 of *
5  * the License, or (at your option) any later version. *
6  * *
7  * This program is distributed in the hope that it will be useful, *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
10  * GNU General Public License for more details. *
11  * *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact: *
14  * *
15  * Free Software Foundation Voice: +1-617-542-5942 *
16  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
17  * Boston, MA 02110-1301, USA gnu@gnu.org *
18 \********************************************************************/
29 #include <config.h>
30 
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 
34 #include "import-match-picker.h"
35 #include "qof.h"
36 #include "gnc-ui-util.h"
37 #include "dialog-utils.h"
38 #include "gnc-prefs.h"
39 
40 /********************************************************************\
41  * Constants *
42 \********************************************************************/
43 
44 #define GNC_PREFS_GROUP "dialogs.import.generic.match-picker"
45 #define GNC_PREF_DISPLAY_RECONCILED "display-reconciled"
46 
47 enum downloaded_cols
48 {
49  DOWNLOADED_COL_ACCOUNT = 0,
50  DOWNLOADED_COL_DATE,
51  DOWNLOADED_COL_AMOUNT,
52  DOWNLOADED_COL_DESCRIPTION,
53  DOWNLOADED_COL_MEMO,
54  DOWNLOADED_COL_BALANCED,
55  DOWNLOADED_COL_INFO_PTR,
56  NUM_DOWNLOADED_COLS
57 };
58 
59 enum matcher_cols
60 {
61  MATCHER_COL_CONFIDENCE = 0,
62  MATCHER_COL_CONFIDENCE_PIXBUF,
63  MATCHER_COL_DATE,
64  MATCHER_COL_AMOUNT,
65  MATCHER_COL_DESCRIPTION,
66  MATCHER_COL_MEMO,
67  MATCHER_COL_RECONCILED,
68  MATCHER_COL_PENDING,
69  MATCHER_COL_INFO_PTR,
70  NUM_MATCHER_COLS
71 };
72 
73 /* Needs to be commented in again if any DEBUG() macro is used here. */
74 /*static short module = MOD_IMPORT;*/
75 
76 /********************************************************************\
77  * Structures passed between the functions *
78 \********************************************************************/
79 
81 {
82  GtkWidget * transaction_matcher;
83  GtkTreeView * downloaded_view;
84  GtkTreeView * match_view;
85  GtkCheckButton * reconciled_chk;
86  GNCImportSettings * user_settings;
87  struct _transactioninfo * selected_trans_info;
88  GNCImportMatchInfo * selected_match_info;
89  GNCImportPendingMatches * pending_matches;
90 };
91 
92 
93 
94 static void
95 downloaded_transaction_append(GNCImportMatchPicker * matcher,
96  GNCImportTransInfo * transaction_info)
97 {
98  g_return_if_fail (matcher);
99  g_return_if_fail (transaction_info);
100 
101  auto found = false;
102  auto store = GTK_LIST_STORE(gtk_tree_view_get_model(matcher->downloaded_view));
103  auto split = gnc_import_TransInfo_get_fsplit(transaction_info);
104  auto trans = gnc_import_TransInfo_get_trans(transaction_info);
105 
106  /* Has the transaction already been added? */
107  GtkTreeIter iter;
108  if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
109  {
110  do
111  {
112  GNCImportTransInfo *local_info;
113  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
114  DOWNLOADED_COL_INFO_PTR, &local_info,
115  -1);
116  if (local_info == transaction_info)
117  {
118  found = TRUE;
119  break;
120  }
121  }
122  while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
123  }
124  if (!found)
125  gtk_list_store_append(store, &iter);
126 
127  auto account = xaccAccountGetName(xaccSplitGetAccount(split));
128  auto date = qof_print_date(xaccTransGetDate(trans));
129  auto amount = g_strdup (xaccPrintAmount(xaccSplitGetAmount(split), gnc_split_amount_print_info(split, TRUE)));
130  auto desc = xaccTransGetDescription(trans);
131  auto memo = xaccSplitGetMemo(split);
132 
133  /*Imbalance*/
134  /* Assume that the importer won't create a transaction that involves two or more
135  currencies and no non-currency commodity. In that case can use the simpler
136  value imbalance check. */
137  auto imbalance = g_strdup (xaccPrintAmount (xaccTransGetImbalanceValue(trans),
138  gnc_commodity_print_info (xaccTransGetCurrency (trans), TRUE)));
139 
140  gtk_list_store_set (store, &iter,
141  DOWNLOADED_COL_ACCOUNT, account,
142  DOWNLOADED_COL_DATE, date,
143  DOWNLOADED_COL_AMOUNT, amount,
144  DOWNLOADED_COL_DESCRIPTION, desc,
145  DOWNLOADED_COL_MEMO, memo,
146  DOWNLOADED_COL_BALANCED, imbalance,
147  DOWNLOADED_COL_INFO_PTR, transaction_info,
148  -1);
149 
150  gtk_tree_selection_select_iter (gtk_tree_view_get_selection(matcher->downloaded_view), &iter);
151 
152  g_free (date);
153  g_free (amount);
154  g_free (imbalance);
155 }
156 
157 static void
158 match_update_match_model (GNCImportMatchPicker *matcher)
159 {
160  g_return_if_fail (matcher);
161 
162  auto show_reconciled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(matcher->reconciled_chk));
163 
164  /* Now rewrite the "match" model based on that trans. */
165  auto match_store = GTK_LIST_STORE(gtk_tree_view_get_model(matcher->match_view));
166  gtk_list_store_clear(match_store);
167 
168  for (auto n = gnc_import_TransInfo_get_match_list (matcher->selected_trans_info); n; n = g_list_next (n))
169  {
170  auto match_info = static_cast<GNCImportMatchInfo*>(n->data);
171  auto split = gnc_import_MatchInfo_get_split (match_info);
172  auto reconciled = xaccSplitGetReconcile (split);
173 
174  /* Skip this match if reconciled and we're not showing those */
175  if (show_reconciled == FALSE && reconciled != NREC)
176  continue;
177 
178  auto probability = gnc_import_MatchInfo_get_probability (match_info);
179  auto trans = xaccSplitGetParent (split);
180  auto match_type = gnc_import_PendingMatches_get_match_type (matcher->pending_matches, match_info);
181 
182  /* Print fields. */
183  auto confidence = g_strdup_printf ("%d", probability);
184  auto date = qof_print_date (xaccTransGetDate (trans));
185  auto amount = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info (split, true));
186  auto description = xaccTransGetDescription (trans);
187  auto memo = xaccSplitGetMemo (split);
188  auto pixbuf = probability ? gen_probability_pixbuf (probability, matcher->user_settings,
189  GTK_WIDGET(matcher->match_view)) : nullptr;
190  auto pending_str = (match_type == GNCImportPending_MANUAL || match_type == GNCImportPending_AUTO)
191  ? g_strdup_printf ("%s (%s)", gnc_get_reconcile_str (CREC), gnc_import_PendingMatches_get_type_str (match_type))
192  : nullptr;
193 
194  GtkTreeIter iter;
195  gtk_list_store_append (match_store, &iter);
196  gtk_list_store_set (match_store, &iter,
197  MATCHER_COL_DATE, date,
198  MATCHER_COL_CONFIDENCE, confidence,
199  MATCHER_COL_CONFIDENCE_PIXBUF, pixbuf,
200  MATCHER_COL_AMOUNT, amount,
201  MATCHER_COL_DESCRIPTION, description,
202  MATCHER_COL_MEMO, memo,
203  MATCHER_COL_RECONCILED, gnc_get_reconcile_str (reconciled),
204  MATCHER_COL_INFO_PTR, match_info,
205  MATCHER_COL_PENDING, pending_str,
206  -1);
207 
208  if (match_info == gnc_import_TransInfo_get_selected_match (matcher->selected_trans_info))
209  gtk_tree_selection_select_iter (gtk_tree_view_get_selection (matcher->match_view), &iter);
210 
211  g_free (confidence);
212  g_free (date);
213  g_free (pending_str);
214  }
215 }
216 
217 /********************************************************************\
218  * *
219  * GUI callbacks *
220  * *
221 \********************************************************************/
222 
223 static void
224 downloaded_transaction_changed_cb (GtkTreeSelection *selection,
225  GNCImportMatchPicker *matcher)
226 {
227  GtkTreeModel *dl_model;
228  GtkTreeIter iter;
229  /*DEBUG("row: %d%s%d",row,", column: ",column);*/
230 
231  /* Get the transaction info from the "downloaded" model. */
232  if (!gtk_tree_selection_get_selected(selection, &dl_model, &iter))
233  {
234  matcher->selected_trans_info = NULL;
235  return;
236  }
237  gtk_tree_model_get(dl_model, &iter,
238  DOWNLOADED_COL_INFO_PTR, &matcher->selected_trans_info,
239  -1);
240 
241  match_update_match_model (matcher);
242 }
243 
244 static void
245 match_show_reconciled_changed_cb (GtkCheckButton* checkbox,
246  GNCImportMatchPicker *matcher)
247 {
248  match_update_match_model (matcher);
249 }
250 
251 static void
252 match_transaction_changed_cb (GtkTreeSelection *selection,
253  GNCImportMatchPicker *matcher)
254 {
255  GtkTreeModel *model;
256  GtkTreeIter iter;
257 
258  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
259  {
260  matcher->selected_match_info = NULL;
261  return;
262  }
263 
264  gtk_tree_model_get(model, &iter,
265  MATCHER_COL_INFO_PTR, &matcher->selected_match_info,
266  -1);
267 }
268 
269 static void
270 match_transaction_row_activated_cb (GtkTreeView *view, GtkTreePath *path,
271  GtkTreeViewColumn *column,
272  GNCImportMatchPicker *matcher)
273 {
274  g_return_if_fail (matcher && matcher->transaction_matcher);
275 
276  gtk_dialog_response (GTK_DIALOG (matcher->transaction_matcher),
277  GTK_RESPONSE_OK);
278 }
279 
280 static void
281 add_column(GtkTreeView *view, const gchar *title, int col_num)
282 {
283  GtkCellRenderer *renderer;
284  GtkTreeViewColumn *column;
285 
286  renderer = gtk_cell_renderer_text_new();
287  column = gtk_tree_view_column_new_with_attributes(title, renderer,
288  "text", col_num,
289  NULL);
290  gtk_tree_view_append_column(view, column);
291  g_object_set(G_OBJECT(column),
292  "reorderable", TRUE,
293  "resizable", TRUE,
294  NULL);
295 }
296 
297 static void
298 gnc_import_match_picker_init_downloaded_view (GNCImportMatchPicker * matcher)
299 {
300  GtkTreeView *view;
301  GtkListStore *store;
302  GtkTreeSelection *selection;
303 
304  view = matcher->downloaded_view;
305  store = gtk_list_store_new(NUM_DOWNLOADED_COLS,
306  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
307  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
308  G_TYPE_POINTER);
309  gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
310  g_object_unref(store);
311 
312  add_column(view, _("Account"), DOWNLOADED_COL_ACCOUNT);
313  add_column(view, _("Date"), DOWNLOADED_COL_DATE);
314  add_column(view, _("Amount"), DOWNLOADED_COL_AMOUNT);
315  add_column(view, _("Description"), DOWNLOADED_COL_DESCRIPTION);
316  add_column(view, _("Memo"), DOWNLOADED_COL_MEMO);
317  add_column(view, _("Balanced"), DOWNLOADED_COL_BALANCED);
318 
319  selection = gtk_tree_view_get_selection(view);
320  g_signal_connect(selection, "changed",
321  G_CALLBACK(downloaded_transaction_changed_cb), matcher);
322 }
323 
324 static void
325 gnc_import_match_picker_init_match_view (GNCImportMatchPicker * matcher)
326 {
327  GtkTreeView *view;
328  GtkListStore *store;
329  GtkCellRenderer *renderer;
330  GtkTreeViewColumn *column;
331  GtkTreeSelection *selection;
332 
333  view = matcher->match_view;
334  store = gtk_list_store_new(NUM_MATCHER_COLS,
335  G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
336  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
337  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
338  gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
339  g_object_unref(store);
340 
341  renderer = gtk_cell_renderer_pixbuf_new();
342  g_object_set(renderer, "xalign", 0.0, NULL);
343  column = gtk_tree_view_column_new_with_attributes(_("Confidence"), renderer,
344  "pixbuf", MATCHER_COL_CONFIDENCE_PIXBUF,
345  NULL);
346  renderer = gtk_cell_renderer_text_new();
347  gtk_tree_view_column_pack_start(column, renderer, TRUE);
348  gtk_tree_view_column_set_attributes(column, renderer,
349  "text", MATCHER_COL_CONFIDENCE,
350  NULL);
351  gtk_tree_view_append_column(view, column);
352 
353  add_column(view, _("Date"), MATCHER_COL_DATE);
354  add_column(view, _("Amount"), MATCHER_COL_AMOUNT);
355  add_column(view, _("Description"), MATCHER_COL_DESCRIPTION);
356  add_column(view, _("Memo"), MATCHER_COL_MEMO);
357  add_column(view, _("Reconciled"), MATCHER_COL_RECONCILED);
358  add_column(view, _("Pending Action"), MATCHER_COL_PENDING);
359 
360  selection = gtk_tree_view_get_selection(view);
361  g_signal_connect(selection, "changed",
362  G_CALLBACK(match_transaction_changed_cb), matcher);
363  g_signal_connect(view, "row-activated",
364  G_CALLBACK(match_transaction_row_activated_cb), matcher);
365 }
366 
367 /********************************************************************\
368  * init_match_picker_gui()
369  * -- GUI initialization for the Match_Picker Dialog
370 \********************************************************************/
371 static void
372 init_match_picker_gui(GtkWidget *parent, GNCImportMatchPicker * matcher)
373 {
374  GtkBuilder *builder;
375 
376  /* DEBUG("Begin..."); */
377 
378  /* Initialize user Settings. */
379  matcher->user_settings = gnc_import_Settings_new ();
380 
381  /* load the interface */
382  builder = gtk_builder_new();
383  gnc_builder_add_from_file (builder, "dialog-import.glade", "match_picker_dialog");
384  g_return_if_fail (builder != NULL);
385 
386  matcher->transaction_matcher = GTK_WIDGET(gtk_builder_get_object (builder, "match_picker_dialog"));
387  matcher->downloaded_view = (GtkTreeView *)GTK_WIDGET(gtk_builder_get_object (builder, "download_view"));
388  matcher->match_view = (GtkTreeView *)GTK_WIDGET(gtk_builder_get_object (builder, "matched_view"));
389  matcher->reconciled_chk = (GtkCheckButton *)GTK_WIDGET(gtk_builder_get_object(builder, "hide_reconciled_check1"));
390 
391  // Set the name for this dialog so it can be easily manipulated with css
392  gtk_widget_set_name (GTK_WIDGET(matcher->transaction_matcher), "gnc-id-import-matcher-picker");
393  gnc_widget_style_context_add_class (GTK_WIDGET(matcher->transaction_matcher), "gnc-class-imports");
394 
395  gtk_window_set_transient_for (GTK_WINDOW (matcher->transaction_matcher), GTK_WINDOW(parent));
396 
397  gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_DISPLAY_RECONCILED, nullptr,
398  matcher->reconciled_chk, "active");
399 
400  gnc_import_match_picker_init_downloaded_view(matcher);
401  gnc_import_match_picker_init_match_view(matcher);
402 
403  /* DEBUG("User prefs:%s%d%s%d%s%d%s%d%s%d",
404  " action_replace_enabled:",matcher->action_replace_enabled,
405  ", action_skip_enabled:",matcher->action_skip_enabled,
406  ", clear_threshold:",matcher->clear_threshold,
407  ", add_threshold:",matcher->add_threshold,
408  ", display_threshold:",matcher->display_threshold); */
409 
410  /* now that we've bound the checkbox appropriately we can hook up the
411  * change callback */
412  g_signal_connect ((GObject *)matcher->reconciled_chk, "toggled",
413  G_CALLBACK(match_show_reconciled_changed_cb), matcher);
414 
415  /* now that we've bound the checkbox appropriately we can hook up the change callback */
416  g_signal_connect((GObject *)matcher->reconciled_chk, "toggled", G_CALLBACK(match_show_reconciled_changed_cb), matcher);
417 
418  gnc_restore_window_size(GNC_PREFS_GROUP,
419  GTK_WINDOW (matcher->transaction_matcher), GTK_WINDOW(parent));
420  gtk_widget_show(matcher->transaction_matcher);
421 
422  g_object_unref(G_OBJECT(builder));
423 
424 }/* end init_match_picker_gui */
425 
431 void
432 gnc_import_match_picker_run_and_close (GtkWidget *parent, GNCImportTransInfo *transaction_info,
433  GNCImportPendingMatches *pending_matches)
434 {
435  GNCImportMatchPicker *matcher;
436  gint response;
437  GNCImportMatchInfo *old;
438  gboolean old_selected_manually;
439  g_assert (transaction_info);
440 
441  /* Create a new match_picker, even though it's stored in a
442  transmatcher struct :-) */
443  matcher = g_new0(GNCImportMatchPicker, 1);
444 
445  matcher->pending_matches = pending_matches;
446 
447  /* DEBUG("Init match_picker"); */
448  init_match_picker_gui(parent, matcher);
449 
450  /* Append this single transaction to the view and select it */
451  downloaded_transaction_append(matcher, transaction_info);
452 
453  old = gnc_import_TransInfo_get_selected_match (transaction_info);
454  old_selected_manually =
456 
457  /* Let this dialog run and close. */
458  /*DEBUG("Right before run and close");*/
459  gtk_window_set_modal(GTK_WINDOW(matcher->transaction_matcher), TRUE);
460  response = gtk_dialog_run (GTK_DIALOG (matcher->transaction_matcher));
461 
462  gnc_save_window_size(GNC_PREFS_GROUP,
463  GTK_WINDOW (matcher->transaction_matcher));
464  gtk_widget_destroy (matcher->transaction_matcher);
465  /*DEBUG("Right after run and close");*/
466  /* DEBUG("Response was %d.", response); */
467  if (response == GTK_RESPONSE_OK && matcher->selected_match_info != old)
468  {
469  /* OK was pressed */
471  matcher->selected_match_info,
472  TRUE);
473 
474  gnc_import_PendingMatches_remove_match (pending_matches,
475  old,
476  old_selected_manually);
477  gnc_import_PendingMatches_add_match (pending_matches,
478  matcher->selected_match_info,
479  TRUE);
480  }
481  gnc_import_Settings_delete (matcher->user_settings);
482  g_free (matcher);
483 }
484 
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void gnc_import_TransInfo_set_selected_match_info(GNCImportTransInfo *info, GNCImportMatchInfo *match, gboolean selected_manually)
Sets the currently selected match in this TransInfo.
utility functions for the GnuCash UI
GNCImportSettings * gnc_import_Settings_new(void)
Allocates a new GNCImportSettings object, and initialize it with the appropriate user prefs...
GdkPixbuf * gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget *widget)
This function generates a new pixmap representing a match score.
void gnc_import_Settings_delete(GNCImportSettings *settings)
Destructor.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Split * gnc_import_TransInfo_get_fsplit(const GNCImportTransInfo *info)
Returns the first split of the transaction of this TransInfo.
Transaction * gnc_import_TransInfo_get_trans(const GNCImportTransInfo *info)
Returns the transaction of this TransInfo.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
Split * gnc_import_MatchInfo_get_split(const GNCImportMatchInfo *info)
Get the split (&#39;this-side split&#39;) of this MatchInfo.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:609
gint gnc_import_MatchInfo_get_probability(const GNCImportMatchInfo *info)
Get the probability (confidence level) of this MatchInfo.
The transaction match picker dialog interface.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
void gnc_prefs_bind(const gchar *group, const gchar *pref_name, const gchar *pref_value, gpointer object, const gchar *property)
Bind a setting to a g_object property.
Definition: gnc-prefs.c:181
#define CREC
The Split has been cleared.
Definition: Split.h:73
Generic api to store and retrieve preferences.
gboolean gnc_import_TransInfo_get_match_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected match was selected by the user.
void gnc_import_match_picker_run_and_close(GtkWidget *parent, GNCImportTransInfo *transaction_info, GNCImportPendingMatches *pending_matches)
Run a match_picker dialog so that the selected-MatchInfo in the given trans_info is updated according...
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3259
#define NREC
not reconciled or cleared
Definition: Split.h:76
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69