GnuCash c935c2f+
Loading...
Searching...
No Matches
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
47enum 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
59enum 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
94static void
95downloaded_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
157static void
158match_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
223static void
224downloaded_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
244static void
245match_show_reconciled_changed_cb (GtkCheckButton* checkbox,
246 GNCImportMatchPicker *matcher)
247{
248 match_update_match_model (matcher);
249}
250
251static void
252match_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
269static void
270match_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
280static void
281add_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
297static void
298gnc_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
324static void
325gnc_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\********************************************************************/
371static void
372init_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
431void
432gnc_import_match_picker_run_and_close (GtkWidget *parent, GNCImportTransInfo *transaction_info,
433 GNCImportPendingMatches *pending_matches)
434{
435 GNCImportMatchPicker *matcher;
436 gint response;
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
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
The xaccTransGetImbalanceValue() method returns the total value of the transaction.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
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...
GList * gnc_import_TransInfo_get_match_list(const GNCImportTransInfo *info)
Returns the stored list of possible matches.
gboolean gnc_import_TransInfo_get_match_selected_manually(const GNCImportTransInfo *info)
Returns if the currently selected match was selected by the user.
Split * gnc_import_TransInfo_get_fsplit(const GNCImportTransInfo *info)
Returns the first split of the transaction of this TransInfo.
GNCImportSettings * gnc_import_Settings_new(void)
Allocates a new GNCImportSettings object, and initialize it with the appropriate user prefs.
Transaction * gnc_import_TransInfo_get_trans(const GNCImportTransInfo *info)
Returns the transaction of this TransInfo.
GdkPixbuf * gen_probability_pixbuf(gint score_original, GNCImportSettings *settings, GtkWidget *widget)
This function generates a new pixmap representing a match score.
void gnc_import_TransInfo_set_selected_match_info(GNCImportTransInfo *info, GNCImportMatchInfo *match, gboolean selected_manually)
Sets the currently selected match in this TransInfo.
void gnc_import_Settings_delete(GNCImportSettings *settings)
Destructor.
gint gnc_import_MatchInfo_get_probability(const GNCImportMatchInfo *info)
Get the probability (confidence level) of this MatchInfo.
Split * gnc_import_MatchInfo_get_split(const GNCImportMatchInfo *info)
Get the split ('this-side split') of this MatchInfo.
GNCImportMatchInfo * gnc_import_TransInfo_get_selected_match(const GNCImportTransInfo *info)
Returns the currently selected match in this TransInfo.
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.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
#define NREC
not reconciled or cleared
Definition Split.h:76
#define CREC
The Split has been cleared
Definition Split.h:73
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
The transaction match picker dialog interface.