1 /*
2 * assistant-ab-initial.c -- Initialise the AqBanking wizard
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, contact:
16 *
17 * Free Software Foundation Voice: +1-617-542-5942
18 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
19 * Boston, MA 02110-1301, USA gnu@gnu.org
20 */
21
22 /**
23 * @internal
24 * @file assistant-ab-initial.c
25 * @brief AqBanking setup functionality
26 * @author Copyright (C) 2002 Christian Stimming <stimming@tuhh.de>
27 * @author Copyright (C) 2006 David Hampton <hampton@employees.org>
28 * @author Copyright (C) 2008 Andreas Koehler <andi5.py@gmx.net>
29 * @author Copyright (C) 2011 Robert Fewell
30 * @author Copyright (C) 2020 Peter Zimmerer <pkzw@web.de>
31 */
32
33 #include <config.h>
34
35 #include <platform.h>
36 #if PLATFORM(WINDOWS)
37 #include <windows.h>
38 #endif
39
40 #include "gnc-ab-utils.h" /* For version macros */
41
42 #include <aqbanking/banking.h>
43 #ifdef AQBANKING6
44 #include <aqbanking/types/account_spec.h>
45 #include <gwenhywfar/gui.h>
46 #endif
47 #include <glib.h>
48 #include <glib/gi18n.h>
49 #include <glib/gstdio.h>
50 #include <gdk/gdkkeysyms.h>
51 #ifdef HAVE_SYS_WAIT_H
52 # include <sys/wait.h>
53 #endif
54 #include <fcntl.h>
55 #include <unistd.h>
56
57 #include "dialog-utils.h"
58 #include "assistant-ab-initial.h"
59 #include "gnc-ab-kvp.h"
60 #include "gnc-ab-utils.h"
61 #include "gnc-component-manager.h"
62 #include "gnc-glib-utils.h"
63 #include "gnc-ui.h"
64 #include "gnc-ui-util.h"
65 #include "gnc-session.h"
66 #include "import-account-matcher.h"
67 #include "import-utilities.h"
68 #ifndef AQBANKING6
69 # include <aqbanking/dlg_setup.h>
70 #endif
71 /* This static indicates the debugging module that this .o belongs to. */
72 static QofLogModule log_module = GNC_MOD_ASSISTANT;
73
74 #define GNC_PREFS_GROUP "dialogs.ab-initial"
75 #define ASSISTANT_AB_INITIAL_CM_CLASS "assistant-ab-initial"
76
77 typedef struct _ABInitialInfo ABInitialInfo;
78 typedef struct _DeferredInfo DeferredInfo;
79 typedef struct _AccCbData AccCbData;
80 typedef struct _RevLookupData RevLookupData;
81
82 void aai_on_prepare (GtkAssistant *assistant, GtkWidget *page,
83 gpointer user_data);
84
85 void aai_on_finish (GtkAssistant *gtkassistant, gpointer user_data);
86 void aai_on_cancel (GtkAssistant *assistant, gpointer user_data);
87 void aai_destroy_cb(GtkWidget *object, gpointer user_data);
88
89 gboolean aai_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
90
91 void aai_wizard_page_prepare (GtkAssistant *assistant, gpointer user_data);
92 void aai_wizard_button_clicked_cb(GtkButton *button, gpointer user_data);
93 void aai_match_delete_button_clicked_cb(GtkButton *button, gpointer user_data);
94
95 #ifdef AQBANKING6
96 static guint aai_ab_account_hash(gconstpointer v);
97 static gboolean aai_ab_account_equal(gconstpointer v1, gconstpointer v2);
98 #endif
99 void aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
100
101 static gboolean banking_has_accounts(AB_BANKING *banking);
102 static void hash_from_kvp_acc_cb(Account *gnc_acc, gpointer user_data);
103 static ABInitialInfo *single_info = NULL;
104 static gchar *ab_account_longname(const GNC_AB_ACCOUNT_SPEC *ab_acc);
105 static GNC_AB_ACCOUNT_SPEC *update_account_list_acc_cb(GNC_AB_ACCOUNT_SPEC *ab_acc, gpointer user_data);
106 static void update_account_list(ABInitialInfo *info);
107 static gboolean find_gnc_acc_cb(gpointer key, gpointer value, gpointer user_data);
108 static gboolean clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data);
109 static void account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path,
110 GtkTreeViewColumn *col, gpointer user_data);
111 static void delete_account_match(ABInitialInfo *info, RevLookupData *data);
112 static void delete_selected_match_cb(gpointer data, gpointer user_data);
113 static void insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash);
114 static void remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash);
115 static void clear_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data);
116 static void save_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data);
117 static void aai_close_handler(gpointer user_data);
118
119 struct _ABInitialInfo
120 {
121 GtkWidget *window;
122 GtkWidget *assistant;
123
124 /* account match page */
125 gboolean match_page_prepared;
126 GtkTreeView *account_view;
127 GtkListStore *account_store;
128
129 /* managed by child_exit_cb */
130 DeferredInfo *deferred_info;
131
132 /* AqBanking stuff */
133 AB_BANKING *api;
134 /* AB_ACCOUNT* -> Account* -- DO NOT DELETE THE KEYS! */
135 GHashTable *gnc_hash;
136 /* Reverse hash table for lookup of matched GnuCash accounts */
137 GHashTable *gnc_revhash;
138 };
139
140 struct _DeferredInfo
141 {
142 ABInitialInfo *initial_info;
143 gchar *wizard_path;
144 gboolean qt_probably_unavailable;
145 };
146
147 struct _AccCbData
148 {
149 AB_BANKING *api;
150 GHashTable *hash;
151 };
152
153 struct _RevLookupData
154 {
155 Account *gnc_acc;
156 GNC_AB_ACCOUNT_SPEC *ab_acc;
157 };
158
159 enum account_list_cols
160 {
161 ACCOUNT_LIST_COL_INDEX = 0,
162 ACCOUNT_LIST_COL_AB_NAME,
163 ACCOUNT_LIST_COL_AB_ACCT,
164 ACCOUNT_LIST_COL_GNC_NAME,
165 ACCOUNT_LIST_COL_CHECKED,
166 NUM_ACCOUNT_LIST_COLS
167 };
168
169 gboolean
aai_key_press_event_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)170 aai_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
171 {
172 if (event->keyval == GDK_KEY_Escape)
173 {
174 gtk_widget_destroy(widget);
175 return TRUE;
176 }
177 else
178 {
179 return FALSE;
180 }
181 }
182
183 void
aai_on_cancel(GtkAssistant * gtkassistant,gpointer user_data)184 aai_on_cancel (GtkAssistant *gtkassistant, gpointer user_data)
185 {
186 ABInitialInfo *info = user_data;
187
188 gtk_widget_destroy(info->window);
189 }
190
191 void
aai_destroy_cb(GtkWidget * object,gpointer user_data)192 aai_destroy_cb(GtkWidget *object, gpointer user_data)
193 {
194 ABInitialInfo *info = user_data;
195 g_return_if_fail (single_info && info == single_info);
196
197 gnc_unregister_gui_component_by_data(ASSISTANT_AB_INITIAL_CM_CLASS, info);
198
199 if (info->deferred_info)
200 {
201 PINFO("Online Banking assistant is being closed but the wizard is still "
202 "running. Inoring.");
203
204 /* Tell child_exit_cb() that there is no assistant anymore */
205 info->deferred_info->initial_info = NULL;
206 }
207
208 if (info->gnc_hash)
209 {
210 #ifndef AQBANKING6
211 AB_Banking_OnlineFini(info->api);
212 #endif
213 g_hash_table_destroy(info->gnc_hash);
214 info->gnc_hash = NULL;
215 }
216
217 if (info->gnc_revhash)
218 {
219 g_hash_table_destroy(info->gnc_revhash);
220 info->gnc_revhash = NULL;
221 }
222
223 if (info->api)
224 {
225 gnc_AB_BANKING_delete(info->api);
226 info->api = NULL;
227 }
228
229 gtk_widget_destroy(info->window);
230 info->window = NULL;
231
232 g_free(info);
233 single_info = NULL;
234 }
235
236 void
aai_wizard_page_prepare(GtkAssistant * assistant,gpointer user_data)237 aai_wizard_page_prepare (GtkAssistant *assistant, gpointer user_data)
238 {
239 ABInitialInfo *info = user_data;
240 gint num = gtk_assistant_get_current_page (assistant);
241 GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
242
243 g_return_if_fail(info->api);
244
245 /* Enable the Assistant Buttons if we accounts */
246 if (banking_has_accounts(info->api))
247 gtk_assistant_set_page_complete (assistant, page, TRUE);
248 else
249 gtk_assistant_set_page_complete (assistant, page, FALSE);
250 }
251
252 void
aai_wizard_button_clicked_cb(GtkButton * button,gpointer user_data)253 aai_wizard_button_clicked_cb(GtkButton *button, gpointer user_data)
254 {
255 ABInitialInfo *info = user_data;
256 gint num = gtk_assistant_get_current_page (GTK_ASSISTANT(info->window));
257 GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT(info->window), num);
258
259 AB_BANKING *banking = info->api;
260 g_return_if_fail(banking);
261
262 ENTER("user_data: %p", user_data);
263
264 if (info->deferred_info)
265 {
266 LEAVE("Wizard is still running");
267 return;
268 }
269
270 {
271 #ifdef AQBANKING6
272 GWEN_DIALOG *dlg = AB_Banking_CreateSetupDialog(banking);
273 #else
274 GWEN_DIALOG *dlg = AB_SetupDialog_new(banking);
275
276 if (AB_Banking_OnlineInit(banking) != 0)
277 {
278 PERR("Got error on AB_Banking_OnlineInit!");
279 }
280 #endif
281 if (!dlg)
282 {
283 PERR("Could not lookup Setup Dialog of aqbanking!");
284 }
285 else
286 {
287 int rv = GWEN_Gui_ExecDialog(dlg, 0);
288 if (rv <= 0)
289 {
290 /* Dialog was aborted/rejected */
291 PERR("Setup Dialog of aqbanking aborted/rejected, code %d", rv);
292 }
293 GWEN_Dialog_free(dlg);
294 }
295 #ifndef AQBANKING6
296 if (AB_Banking_OnlineFini(banking) != 0)
297 {
298 PERR("Got error on AB_Banking_OnlineFini!");
299 }
300 #endif
301 }
302
303 /* Enable the Assistant Buttons if we accounts */
304 if (banking_has_accounts(info->api))
305 gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), page, TRUE);
306 else
307 gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), page, FALSE);
308
309 LEAVE(" ");
310 }
311
delete_account_match(ABInitialInfo * info,RevLookupData * data)312 static void delete_account_match(ABInitialInfo *info, RevLookupData *data)
313 {
314 g_return_if_fail(info && info->gnc_hash &&
315 info->account_view && data && data->ab_acc);
316
317 g_hash_table_remove(info->gnc_hash, data->ab_acc);
318 gtk_tree_model_foreach(
319 GTK_TREE_MODEL(info->account_store),
320 (GtkTreeModelForeachFunc) clear_line_cb,
321 data);
322 }
323
324 static void
delete_selected_match_cb(gpointer data,gpointer user_data)325 delete_selected_match_cb(gpointer data, gpointer user_data)
326 {
327 GNC_AB_ACCOUNT_SPEC *ab_acc = NULL;
328 GtkTreeIter iter;
329 GtkTreeModel *model = NULL;
330 RevLookupData revLookupData = {NULL, NULL};
331
332 GtkTreePath *path = (GtkTreePath *) data;
333 ABInitialInfo *info = (ABInitialInfo *) user_data;
334 g_return_if_fail(path && info && info->account_view);
335
336 model = gtk_tree_view_get_model(info->account_view);
337 g_return_if_fail(model);
338
339 if (gtk_tree_model_get_iter(model, &iter, path))
340 {
341 gtk_tree_model_get(model, &iter, ACCOUNT_LIST_COL_AB_ACCT, &revLookupData.ab_acc, -1);
342 if (revLookupData.ab_acc)
343 delete_account_match(info, &revLookupData);
344 }
345 }
346
347 void
aai_match_delete_button_clicked_cb(GtkButton * button,gpointer user_data)348 aai_match_delete_button_clicked_cb(GtkButton *button, gpointer user_data)
349 {
350 GList *selected_matches = NULL;
351 GtkTreeSelection *selection = NULL;
352 ABInitialInfo *info = (ABInitialInfo *) user_data;
353
354 g_return_if_fail(info && info->api && info->account_view && info->gnc_hash);
355
356 PINFO("Selected account matches are deleted");
357
358 selection = gtk_tree_view_get_selection (info->account_view);
359 if (selection)
360 {
361 selected_matches = gtk_tree_selection_get_selected_rows (selection, NULL);
362 if (selected_matches)
363 {
364 g_list_foreach (selected_matches, delete_selected_match_cb, info);
365 g_list_free_full (
366 selected_matches,
367 (GDestroyNotify) gtk_tree_path_free);
368 }
369 }
370 }
371
372 #ifdef AQBANKING6
373 static guint
aai_ab_account_hash(gconstpointer v)374 aai_ab_account_hash (gconstpointer v)
375 {
376 if (v == NULL)
377 return 0;
378 else
379 /* Use the account unique id as hash value */
380 return AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v);
381 }
382
383 static gboolean
aai_ab_account_equal(gconstpointer v1,gconstpointer v2)384 aai_ab_account_equal (gconstpointer v1, gconstpointer v2)
385 {
386 if (v1 == NULL || v2 == NULL)
387 return v1 == v2;
388 else
389 {
390 /* Use the account unique id to check for equality */
391 uint32_t uid1 = AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v1);
392 uint32_t uid2 = AB_AccountSpec_GetUniqueId((const GNC_AB_ACCOUNT_SPEC *) v2);
393 return uid1 == uid2;
394 }
395 }
396 #endif
397
398 static void
insert_acc_into_revhash_cb(gpointer ab_acc,gpointer gnc_acc,gpointer revhash)399 insert_acc_into_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash)
400 {
401 g_return_if_fail(revhash && gnc_acc && ab_acc);
402 g_hash_table_insert((GHashTable *) revhash, gnc_acc, ab_acc);
403 }
404
405 static void
remove_acc_from_revhash_cb(gpointer ab_acc,gpointer gnc_acc,gpointer revhash)406 remove_acc_from_revhash_cb(gpointer ab_acc, gpointer gnc_acc, gpointer revhash)
407 {
408 g_return_if_fail(revhash && gnc_acc);
409 g_hash_table_remove((GHashTable *) revhash, gnc_acc);
410 }
411
412 void
aai_match_page_prepare(GtkAssistant * assistant,gpointer user_data)413 aai_match_page_prepare (GtkAssistant *assistant, gpointer user_data)
414 {
415 ABInitialInfo *info = user_data;
416 gint num = gtk_assistant_get_current_page (assistant);
417 GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
418
419 Account *root;
420 AccCbData data;
421
422 g_return_if_fail(info && info->api);
423
424 /* Do not run this twice */
425 if (!info->match_page_prepared)
426 {
427 #ifndef AQBANKING6
428 /* Load aqbanking accounts */
429 AB_Banking_OnlineInit(info->api);
430 #endif
431 /* Determine current mapping */
432 root = gnc_book_get_root_account(gnc_get_current_book());
433 #ifdef AQBANKING6
434 info->gnc_hash = g_hash_table_new(&aai_ab_account_hash, &aai_ab_account_equal);
435 #else
436 info->gnc_hash = g_hash_table_new(&g_direct_hash, &g_direct_equal);
437 #endif
438 data.api = info->api;
439 data.hash = info->gnc_hash;
440 gnc_account_foreach_descendant(root, (AccountCb) hash_from_kvp_acc_cb, &data);
441 /* Memorize initial matches in reverse hash table */
442 info->gnc_revhash = g_hash_table_new(NULL, NULL);
443 g_hash_table_foreach(data.hash, (GHFunc) insert_acc_into_revhash_cb, (gpointer) info->gnc_revhash);
444
445 info->match_page_prepared = TRUE;
446 }
447 /* Update the graphical representation */
448 update_account_list(info);
449
450 /* Enable the Assistant Buttons */
451 gtk_assistant_set_page_complete (assistant, page, TRUE);
452 }
453
454 void
aai_on_finish(GtkAssistant * assistant,gpointer user_data)455 aai_on_finish (GtkAssistant *assistant, gpointer user_data)
456 {
457 ABInitialInfo *info = user_data;
458
459 g_return_if_fail(info && info->gnc_hash && info->gnc_revhash);
460
461 /* Remove GnuCash accounts from reverse hash table which are still
462 * matched to an AqBanking account. For the remaining GnuCash accounts
463 * the KVPs must be cleared (i.e. deleted).
464 * Please note that the value (i.e. the GnuCash account) stored in info->gnc_hash
465 * is used as key for info->gnc_revhash */
466 g_hash_table_foreach(info->gnc_hash, (GHFunc) remove_acc_from_revhash_cb, info->gnc_revhash);
467 /* Commit the changes */
468 g_hash_table_foreach(info->gnc_revhash, (GHFunc) clear_kvp_acc_cb, NULL);
469 g_hash_table_foreach(info->gnc_hash, (GHFunc) save_kvp_acc_cb, NULL);
470
471 gtk_widget_destroy(info->window);
472 }
473
474 static gboolean
banking_has_accounts(AB_BANKING * banking)475 banking_has_accounts(AB_BANKING *banking)
476 {
477 GNC_AB_ACCOUNT_SPEC_LIST *accl = NULL;
478 gboolean result = FALSE;
479
480 g_return_val_if_fail(banking, FALSE);
481
482 #ifdef AQBANKING6
483 if (AB_Banking_GetAccountSpecList (banking, &accl) >= 0 &&
484 accl && AB_AccountSpec_List_GetCount (accl))
485 result = TRUE;
486 if (accl)
487 AB_AccountSpec_List_free (accl);
488 #else
489 AB_Banking_OnlineInit(banking);
490
491 accl = AB_Banking_GetAccounts(banking);
492 if (accl && (AB_Account_List2_GetSize(accl) > 0))
493 result = TRUE;
494
495 if (accl)
496 AB_Account_List2_free(accl);
497
498 AB_Banking_OnlineFini(banking);
499 #endif
500
501 return result;
502 }
503
504 static void
hash_from_kvp_acc_cb(Account * gnc_acc,gpointer user_data)505 hash_from_kvp_acc_cb(Account *gnc_acc, gpointer user_data)
506 {
507 AccCbData *data = user_data;
508 GNC_AB_ACCOUNT_SPEC *ab_acc;
509
510 ab_acc = gnc_ab_get_ab_account(data->api, gnc_acc);
511 if (ab_acc)
512 g_hash_table_insert(data->hash, ab_acc, gnc_acc);
513 }
514
515 static gchar *
ab_account_longname(const GNC_AB_ACCOUNT_SPEC * ab_acc)516 ab_account_longname(const GNC_AB_ACCOUNT_SPEC *ab_acc)
517 {
518 gchar *bankname = NULL;
519 gchar *result = NULL;
520 const char *ab_bankname, *bankcode, *subAccountId, *account_number;
521
522 g_return_val_if_fail(ab_acc, NULL);
523
524 #ifdef AQBANKING6
525 bankcode = AB_AccountSpec_GetBankCode(ab_acc);
526 subAccountId = AB_AccountSpec_GetSubAccountNumber(ab_acc);
527 account_number = AB_AccountSpec_GetAccountNumber (ab_acc);
528 #else
529 ab_bankname = AB_Account_GetBankName(ab_acc);
530 bankname = ab_bankname ? gnc_utf8_strip_invalid_strdup(ab_bankname) : NULL;
531 bankcode = AB_Account_GetBankCode(ab_acc);
532 subAccountId = AB_Account_GetSubAccountId(ab_acc);
533 account_number = AB_Account_GetAccountNumber (ab_acc);
534 #endif
535 /* Translators: Strings are 1. Bank code, 2. Bank name,
536 3. Account Number, 4. Subaccount ID */
537 result = g_strdup_printf(_("Bank code %s (%s), Account %s (%s)"),
538 bankcode,
539 bankname ? bankname : "",
540 account_number,
541 subAccountId ? subAccountId : "");
542 g_free(bankname);
543
544 return result;
545
546 }
547
548 static GNC_AB_ACCOUNT_SPEC *
update_account_list_acc_cb(GNC_AB_ACCOUNT_SPEC * ab_acc,gpointer user_data)549 update_account_list_acc_cb(GNC_AB_ACCOUNT_SPEC *ab_acc, gpointer user_data)
550 {
551 ABInitialInfo *info = user_data;
552 gchar *gnc_name, *ab_name;
553 Account *gnc_acc;
554 GtkTreeIter iter;
555
556 g_return_val_if_fail(ab_acc && info, NULL);
557
558 ab_name = ab_account_longname(ab_acc);
559
560 /* Get corresponding gnucash account */
561 gnc_acc = g_hash_table_lookup(info->gnc_hash, ab_acc);
562
563 /* Build the text for the gnucash account. */
564 if (gnc_acc)
565 gnc_name = gnc_account_get_full_name(gnc_acc);
566 else
567 gnc_name = g_strdup("");
568
569 /* Add item to the list store */
570 gtk_list_store_append(info->account_store, &iter);
571 gtk_list_store_set(info->account_store, &iter,
572 ACCOUNT_LIST_COL_AB_NAME, ab_name,
573 ACCOUNT_LIST_COL_AB_ACCT, ab_acc,
574 ACCOUNT_LIST_COL_GNC_NAME, gnc_name,
575 ACCOUNT_LIST_COL_CHECKED, FALSE,
576 -1);
577 g_free(gnc_name);
578 g_free(ab_name);
579
580 return NULL;
581 }
582
583 static void
update_account_list(ABInitialInfo * info)584 update_account_list(ABInitialInfo *info)
585 {
586 GNC_AB_ACCOUNT_SPEC_LIST *acclist = NULL;
587
588 g_return_if_fail(info && info->api && info->gnc_hash);
589
590 /* Detach model from view while updating */
591 g_object_ref(info->account_store);
592 gtk_tree_view_set_model(info->account_view, NULL);
593
594 /* Refill the list */
595 gtk_list_store_clear(info->account_store);
596 #ifdef AQBANKING6
597 if (AB_Banking_GetAccountSpecList(info->api, &acclist) >= 0 && acclist)
598 AB_AccountSpec_List_ForEach(acclist, update_account_list_acc_cb, info);
599 #else
600 acclist = AB_Banking_GetAccounts(info->api);
601 if (acclist)
602 AB_Account_List2_ForEach(acclist, update_account_list_acc_cb, info);
603 #endif
604 else
605 g_warning("update_account_list: Oops, account list from AB_Banking "
606 "is NULL");
607
608 /* Attach model to view again */
609 gtk_tree_view_set_model(info->account_view,
610 GTK_TREE_MODEL(info->account_store));
611
612 g_object_unref(info->account_store);
613 }
614
615 static gboolean
find_gnc_acc_cb(gpointer key,gpointer value,gpointer user_data)616 find_gnc_acc_cb(gpointer key, gpointer value, gpointer user_data)
617 {
618 RevLookupData *data = user_data;
619
620 g_return_val_if_fail(data, TRUE);
621
622 if (value == data->gnc_acc)
623 {
624 data->ab_acc = (GNC_AB_ACCOUNT_SPEC*) key;
625 return TRUE;
626 }
627 return FALSE;
628 }
629
630 static gboolean
clear_line_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)631 clear_line_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
632 gpointer user_data)
633 {
634 RevLookupData *data = user_data;
635 GtkListStore *store = GTK_LIST_STORE(model);
636 gpointer ab_acc;
637
638 g_return_val_if_fail(data && store, FALSE);
639
640 gtk_tree_model_get(model, iter, ACCOUNT_LIST_COL_AB_ACCT, &ab_acc, -1);
641
642 #ifdef AQBANKING6
643 if (aai_ab_account_equal(ab_acc, data->ab_acc))
644 #else
645 if (ab_acc == data->ab_acc)
646 #endif
647 {
648 gtk_list_store_set(store, iter, ACCOUNT_LIST_COL_GNC_NAME, "",
649 ACCOUNT_LIST_COL_CHECKED, TRUE, -1);
650 return TRUE;
651 }
652 return FALSE;
653 }
654
655 static void
account_list_clicked_cb(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * col,gpointer user_data)656 account_list_clicked_cb (GtkTreeView *view, GtkTreePath *path,
657 GtkTreeViewColumn *col, gpointer user_data)
658 {
659 ABInitialInfo *info = user_data;
660 GtkTreeModel *model;
661 GtkTreeIter iter;
662 GNC_AB_ACCOUNT_SPEC *ab_acc;
663 gchar *longname, *gnc_name;
664 Account *old_value, *gnc_acc;
665 const gchar *currency;
666 gnc_commodity *commodity = NULL;
667 gboolean ok_pressed;
668
669 g_return_if_fail(info);
670
671 PINFO("Row has been double-clicked.");
672
673 model = gtk_tree_view_get_model(view);
674
675 if (!gtk_tree_model_get_iter(model, &iter, path))
676 return; /* path describes a non-existing row - should not happen */
677
678 gtk_tree_model_get(model, &iter, ACCOUNT_LIST_COL_AB_ACCT, &ab_acc, -1);
679
680 if (ab_acc)
681 {
682 old_value = g_hash_table_lookup(info->gnc_hash, ab_acc);
683
684 longname = ab_account_longname(ab_acc);
685 #ifdef AQBANKING6
686 currency = AB_AccountSpec_GetCurrency(ab_acc);
687 #else
688 currency = AB_Account_GetCurrency(ab_acc);
689 #endif
690 if (currency && *currency)
691 {
692 commodity = gnc_commodity_table_lookup(
693 gnc_commodity_table_get_table(gnc_get_current_book()),
694 GNC_COMMODITY_NS_CURRENCY,
695 currency);
696 }
697
698 gnc_acc = gnc_import_select_account(info->window, NULL, TRUE,
699 longname, commodity, ACCT_TYPE_BANK,
700 old_value, &ok_pressed);
701 g_free(longname);
702
703 if (ok_pressed && old_value != gnc_acc)
704 {
705 if (gnc_acc)
706 {
707 RevLookupData data;
708
709 /* Lookup and clear other mappings to gnc_acc */
710 data.gnc_acc = gnc_acc;
711 data.ab_acc = NULL;
712 g_hash_table_find(info->gnc_hash, (GHRFunc) find_gnc_acc_cb,
713 &data);
714 if (data.ab_acc)
715 delete_account_match(info, &data);
716
717 /* Map ab_acc to gnc_acc */
718 g_hash_table_insert(info->gnc_hash, ab_acc, gnc_acc);
719 gnc_name = gnc_account_get_full_name(gnc_acc);
720 gtk_list_store_set(info->account_store, &iter,
721 ACCOUNT_LIST_COL_GNC_NAME, gnc_name,
722 ACCOUNT_LIST_COL_CHECKED, TRUE,
723 -1);
724 g_free(gnc_name);
725
726 }
727 else
728 {
729 g_hash_table_remove(info->gnc_hash, ab_acc);
730 gtk_list_store_set(info->account_store, &iter,
731 ACCOUNT_LIST_COL_GNC_NAME, "",
732 ACCOUNT_LIST_COL_CHECKED, TRUE,
733 -1);
734 }
735 }
736 }
737 }
738
739 static void
clear_kvp_acc_cb(gpointer gnc_acc,gpointer ab_acc,gpointer user_data)740 clear_kvp_acc_cb(gpointer gnc_acc, gpointer ab_acc, gpointer user_data)
741 {
742 g_return_if_fail(gnc_acc);
743 /* Delete "online-id" and complete "hbci..." KVPs for GnuCash account */
744 gnc_account_delete_map_entry((Account *) gnc_acc, "online_id", NULL, NULL, FALSE);
745 gnc_account_delete_map_entry((Account *) gnc_acc, "hbci", NULL, NULL, FALSE);
746 }
747
748 static void
save_kvp_acc_cb(gpointer key,gpointer value,gpointer user_data)749 save_kvp_acc_cb(gpointer key, gpointer value, gpointer user_data)
750 {
751 GNC_AB_ACCOUNT_SPEC *ab_acc = key;
752 Account *gnc_acc = value;
753 guint32 ab_account_uid;
754 const gchar *ab_accountid, *gnc_accountid;
755 const gchar *ab_bankcode, *gnc_bankcode;
756 #ifdef AQBANKING6
757 gchar *ab_online_id;
758 const gchar *gnc_online_id;
759 #endif
760
761 g_return_if_fail(ab_acc && gnc_acc);
762
763 #ifdef AQBANKING6
764 ab_account_uid = AB_AccountSpec_GetUniqueId(ab_acc);
765 #else
766 ab_account_uid = AB_Account_GetUniqueId(ab_acc);
767 #endif
768 if (gnc_ab_get_account_uid(gnc_acc) != ab_account_uid)
769 gnc_ab_set_account_uid(gnc_acc, ab_account_uid);
770
771 #ifdef AQBANKING6
772 ab_accountid = AB_AccountSpec_GetAccountNumber(ab_acc);
773 #else
774 ab_accountid = AB_Account_GetAccountNumber(ab_acc);
775 #endif
776 gnc_accountid = gnc_ab_get_account_accountid(gnc_acc);
777 if (ab_accountid
778 && (!gnc_accountid
779 || (strcmp(ab_accountid, gnc_accountid) != 0)))
780 gnc_ab_set_account_accountid(gnc_acc, ab_accountid);
781
782 #ifdef AQBANKING6
783 ab_bankcode = AB_AccountSpec_GetBankCode(ab_acc);
784 #else
785 ab_bankcode = AB_Account_GetBankCode(ab_acc);
786 #endif
787 gnc_bankcode = gnc_ab_get_account_bankcode(gnc_acc);
788 if (ab_bankcode
789 && (!gnc_bankcode
790 || (strcmp(gnc_bankcode, ab_bankcode) != 0)))
791 gnc_ab_set_account_bankcode(gnc_acc, ab_bankcode);
792
793 #ifdef AQBANKING6
794 ab_online_id = gnc_ab_create_online_id(ab_bankcode, ab_accountid);
795 gnc_online_id = gnc_import_get_acc_online_id(gnc_acc);
796 if (ab_online_id && (!gnc_online_id || (strcmp(ab_online_id, gnc_online_id) != 0)))
797 gnc_import_set_acc_online_id(gnc_acc, ab_online_id);
798 g_free(ab_online_id);
799 #endif
800 }
801
802 static void
aai_close_handler(gpointer user_data)803 aai_close_handler(gpointer user_data)
804 {
805 ABInitialInfo *info = user_data;
806
807 gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(info->window));
808 gtk_widget_destroy(info->window);
809 }
810
aai_on_prepare(GtkAssistant * assistant,GtkWidget * page,gpointer user_data)811 void aai_on_prepare (GtkAssistant *assistant, GtkWidget *page,
812 gpointer user_data)
813 {
814 switch (gtk_assistant_get_current_page(assistant))
815 {
816 case 1:
817 /* Current page is wizard button page */
818 aai_wizard_page_prepare (assistant , user_data );
819 break;
820 case 2:
821 /* Current page is match page */
822 aai_match_page_prepare (assistant , user_data );
823 break;
824 }
825 }
826
827 static ABInitialInfo *
gnc_ab_initial_assistant_new(void)828 gnc_ab_initial_assistant_new(void)
829 {
830 GtkBuilder *builder;
831 GtkTreeViewColumn *column;
832 GtkTreeSelection *selection;
833 gint component_id;
834
835 ABInitialInfo *info = g_new0(ABInitialInfo, 1);
836 builder = gtk_builder_new();
837 gnc_builder_add_from_file (builder, "assistant-ab-initial.glade", "aqbanking_init_assistant");
838
839 info->window = GTK_WIDGET(gtk_builder_get_object (builder, "aqbanking_init_assistant"));
840
841 info->api = gnc_AB_BANKING_new();
842 info->deferred_info = NULL;
843 info->gnc_hash = NULL;
844
845 info->match_page_prepared = FALSE;
846 info->account_view =
847 GTK_TREE_VIEW(gtk_builder_get_object (builder, "account_page_view"));
848
849 info->account_store = gtk_list_store_new(NUM_ACCOUNT_LIST_COLS,
850 G_TYPE_INT, G_TYPE_STRING,
851 G_TYPE_POINTER, G_TYPE_STRING,
852 G_TYPE_BOOLEAN);
853 gtk_tree_view_set_model(info->account_view,
854 GTK_TREE_MODEL(info->account_store));
855 g_object_unref(info->account_store);
856
857 column = gtk_tree_view_column_new_with_attributes(
858 _("Online Banking Account Name"), gtk_cell_renderer_text_new(),
859 "text", ACCOUNT_LIST_COL_AB_NAME, (gchar*) NULL);
860 gtk_tree_view_append_column(info->account_view, column);
861
862 column = gtk_tree_view_column_new_with_attributes(
863 _("GnuCash Account Name"), gtk_cell_renderer_text_new(),
864 "text", ACCOUNT_LIST_COL_GNC_NAME, (gchar*) NULL);
865 gtk_tree_view_column_set_expand(column, TRUE);
866 gtk_tree_view_append_column(info->account_view, column);
867
868 column = gtk_tree_view_column_new_with_attributes(
869 _("New?"), gtk_cell_renderer_toggle_new(),
870 "active", ACCOUNT_LIST_COL_CHECKED, (gchar*) NULL);
871 gtk_tree_view_append_column(info->account_view, column);
872
873 selection = gtk_tree_view_get_selection(info->account_view);
874 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
875
876 gnc_restore_window_size (GNC_PREFS_GROUP,
877 GTK_WINDOW(info->window), gnc_ui_get_main_window(NULL));
878
879 g_signal_connect(info->account_view, "row-activated",
880 G_CALLBACK(account_list_clicked_cb), info);
881
882 g_signal_connect (G_OBJECT(info->window), "destroy",
883 G_CALLBACK (aai_destroy_cb), info);
884
885 gtk_builder_connect_signals(builder, info);
886 g_object_unref(G_OBJECT(builder));
887
888 component_id = gnc_register_gui_component(ASSISTANT_AB_INITIAL_CM_CLASS,
889 NULL, aai_close_handler, info);
890
891 gnc_gui_component_set_session(component_id, gnc_get_current_session());
892 return info;
893 }
894
895 void
gnc_ab_initial_assistant(void)896 gnc_ab_initial_assistant(void)
897 {
898 if (!single_info)
899 single_info = gnc_ab_initial_assistant_new();
900 gtk_widget_show(single_info->window);
901 }
902
903