GnuCash  5.6-150-g038405b370+
gnc-ab-utils.c
1 /*
2  * gnc-ab-utils.c --
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 
30 #include <config.h>
31 
32 #include "gnc-ab-utils.h"
33 
34 #include <glib/gi18n.h>
35 #include <gwenhywfar/gwenhywfar.h>
36 #include <aqbanking/banking.h>
37 #include <aqbanking/types/balance.h>
38 #if (AQBANKING_VERSION_INT >= 60400)
39 #include <aqbanking/types/refaccount.h>
40 #include <gnc-aqbanking-templates.h>
41 #endif
42 #include "window-reconcile.h"
43 #include "Transaction.h"
44 #include "dialog-ab-trans.h"
45 #include "gnc-ab-kvp.h"
46 #include "gnc-glib-utils.h"
47 #include "gnc-gwen-gui.h"
48 #include "gnc-prefs.h"
49 #include "gnc-ui.h"
50 #include "import-account-matcher.h"
51 #include "import-main-matcher.h"
52 #include "qof.h"
53 #include "engine-helpers.h"
54 #include <aqbanking/gui/abgui.h>
55 
56 /* This static indicates the debugging module that this .o belongs to. */
57 G_GNUC_UNUSED static QofLogModule log_module = G_LOG_DOMAIN;
58 
59 /* Global variables for AB_BANKING caching. */
60 static AB_BANKING *gnc_AB_BANKING = NULL;
61 static gint gnc_AB_BANKING_refcount = 0;
62 
63 static gpointer join_ab_strings_cb (const gchar *str, gpointer user_data);
64 static Account *gnc_ab_accinfo_to_gnc_acc (GtkWidget *parent,
65  AB_IMEXPORTER_ACCOUNTINFO *account_info);
66 static Account *gnc_ab_txn_to_gnc_acc (GtkWidget *parent,
67  const AB_TRANSACTION *transaction);
68 static const AB_TRANSACTION *txn_transaction_cb (const AB_TRANSACTION *element,
69  gpointer user_data);
70 static AB_IMEXPORTER_ACCOUNTINFO *txn_accountinfo_cb (AB_IMEXPORTER_ACCOUNTINFO *element,
71  gpointer user_data);
72 static AB_IMEXPORTER_ACCOUNTINFO *bal_accountinfo_cb (AB_IMEXPORTER_ACCOUNTINFO *element,
73  gpointer user_data);
74 
76 {
77  guint awaiting;
78  gboolean txn_found;
79  Account *gnc_acc;
80  GNC_AB_ACCOUNT_SPEC *ab_acc;
81  gboolean execute_txns;
82  AB_BANKING *api;
83  GtkWidget *parent;
84  GNC_AB_JOB_LIST2 *job_list;
85  GNCImportMainMatcher *generic_importer;
86  GData *tmp_job_list;
87 };
88 
89 static inline gboolean is_leap_year (int year)
90 {
91  return (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0 ));
92 }
93 
94 static inline time64
95 gnc_gwen_date_to_time64 (const GNC_GWEN_DATE* date)
96 {
97  int day = GWEN_Date_GetDay (date);
98  int month = GWEN_Date_GetMonth (date);
99  int year = GWEN_Date_GetYear (date);
100  /* Some banks use nominal 30-day months and set the value date as
101  * the day after the posted date. In February this can appear to
102  * be an invalid date because February has fewer than 30 days. If
103  * that's the case then back up a day to get a real date for
104  * posting.
105  */
106  while (month == 2 && day <= 30 && day > (is_leap_year (year) ? 29 : 28))
107  --day;
108  return gnc_dmy2time64_neutral (day, month, year);
109 }
110 
111 void
113 {
114  gchar* gwen_logging = g_strdup (g_getenv ("GWEN_LOGLEVEL"));
115  gchar* aqb_logging = g_strdup (g_getenv ("AQBANKING_LOGLEVEL"));
116 
117  /* Initialize gwen library */
118  GWEN_Init();
119 
120  /* Initialize gwen logging */
121  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_VERBOSE_DEBUG))
122  {
123  if (!gwen_logging)
124  {
125  GWEN_Logger_SetLevel (NULL, GWEN_LoggerLevel_Info);
126  GWEN_Logger_SetLevel (GWEN_LOGDOMAIN, GWEN_LoggerLevel_Info);
127  }
128  if (!aqb_logging)
129  GWEN_Logger_SetLevel (AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Debug);
130  }
131  else
132  {
133  if (!gwen_logging)
134  {
135  GWEN_Logger_SetLevel (NULL, GWEN_LoggerLevel_Error);
136  GWEN_Logger_SetLevel (GWEN_LOGDOMAIN, GWEN_LoggerLevel_Error);
137  }
138  if (!aqb_logging)
139  GWEN_Logger_SetLevel (AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Warning);
140  }
141  g_free (gwen_logging);
142  g_free (aqb_logging);
144 }
145 
146 void
148 {
149  /* Shutdown the GWEN_GUIs */
151  GWEN_Logger_SetLevel (NULL, GWEN_LoggerLevel_Error);
152  GWEN_Logger_SetLevel (GWEN_LOGDOMAIN, GWEN_LoggerLevel_Warning);
153  GWEN_Logger_SetLevel (AQBANKING_LOGDOMAIN, GWEN_LoggerLevel_Warning);
154 
155  /* Finalize gwen library */
156  GWEN_Fini();
157 }
158 
159 static GWEN_GUI *gnc_gwengui_extended_by_ABBanking;
160 
161 AB_BANKING *
163 {
164  AB_BANKING *api;
165 
166  if (gnc_AB_BANKING)
167  {
168  /* API cached. */
169  api = gnc_AB_BANKING;
170 
171  /* Init the API again. */
172  if (gnc_AB_BANKING_refcount == 0)
173  g_return_val_if_fail (AB_Banking_Init (api) == 0, NULL);
174 
175  }
176  else
177  {
178  api = AB_Banking_new (PROJECT_NAME, NULL, 0);
179  g_return_val_if_fail (api, NULL);
180 
181  /* These two values must be set because newest bank regulation requires
182  the bank servers to require it. The string itself results from our
183  registration with the German bank association at
184  https://www.hbci-zka.de/register/prod_register.htm (where the
185  registration was requested and is managed by cstim). The function call was
186  introduced in aqbanking-5.99.25 and aqbanking-5.7.9. */
187  AB_Banking_RuntimeConfig_SetCharValue (api, "fintsRegistrationKey", "412748A1836CDD07181CE1910");
188  AB_Banking_RuntimeConfig_SetCharValue (api, "fintsApplicationVersionString", PROJECT_VERSION);
189 
190  /* Init the API */
191  g_return_val_if_fail (AB_Banking_Init (api) == 0, NULL);
192  gnc_gwengui_extended_by_ABBanking = GWEN_Gui_GetGui ();
193  AB_Gui_Extend (gnc_gwengui_extended_by_ABBanking, api);
194 
195  /* Cache it */
196  gnc_AB_BANKING = api;
197  gnc_AB_BANKING_refcount = 0;
198  }
199 
200  gnc_AB_BANKING_refcount++;
201 
202  return api;
203 }
204 
205 void
206 gnc_AB_BANKING_delete (AB_BANKING *api)
207 {
208  if (!api)
209  api = gnc_AB_BANKING;
210 
211  if (api)
212  {
213  if (api == gnc_AB_BANKING)
214  {
215  gnc_AB_BANKING = NULL;
216  gnc_AB_BANKING_fini (api);
217  }
218 
219  AB_Banking_free (api);
220  }
221 }
222 
223 
224 gint
225 gnc_AB_BANKING_fini (AB_BANKING *api)
226 {
227  if (api == gnc_AB_BANKING)
228  {
229  if (--gnc_AB_BANKING_refcount == 0)
230  {
231  if (gnc_gwengui_extended_by_ABBanking)
232  AB_Gui_Unextend (gnc_gwengui_extended_by_ABBanking);
233  gnc_gwengui_extended_by_ABBanking = NULL;
234  return AB_Banking_Fini (api);
235  }
236  }
237  else
238  {
239  if (gnc_gwengui_extended_by_ABBanking)
240  AB_Gui_Unextend (gnc_gwengui_extended_by_ABBanking);
241  gnc_gwengui_extended_by_ABBanking = NULL;
242  return AB_Banking_Fini (api);
243  }
244  return 0;
245 }
246 
247 GNC_AB_ACCOUNT_SPEC *
248 gnc_ab_get_ab_account (const AB_BANKING *api, Account *gnc_acc)
249 {
250  GNC_AB_ACCOUNT_SPEC *ab_account = NULL;
251  const gchar *bankcode = NULL;
252  const gchar *accountid = NULL;
253  guint32 account_uid = 0;
254 
255  bankcode = gnc_ab_get_account_bankcode (gnc_acc);
256  accountid = gnc_ab_get_account_accountid (gnc_acc);
257  account_uid = gnc_ab_get_account_uid (gnc_acc);
258 
259  if (account_uid > 0)
260  {
261  gint rv;
262 
263  rv = AB_Banking_GetAccountSpecByUniqueId (api, account_uid, &ab_account);
264 
265  if ( (rv<0 || !ab_account) && bankcode && *bankcode &&
266  accountid && *accountid)
267  {
268 /* Finding the account by code and number is suspended in AQBANKING 6 pending
269  * implementation of a replacement for AB_Banking_GetAccountByCodeAndNumber.
270  */
271  PINFO("gnc_ab_get_ab_account: No AB_ACCOUNT found for UID %d, "
272  "trying bank code\n", account_uid);
273  return NULL;
274  }
275  return ab_account;
276  }
277 
278  return NULL;
279 }
280 
281 gchar *
282 gnc_AB_VALUE_to_readable_string (const AB_VALUE *value)
283 {
284  if (value)
285  return g_strdup_printf ("%.2f %s",
286  AB_Value_GetValueAsDouble (value),
287  AB_Value_GetCurrency (value));
288  else
289  return g_strdup_printf ("%.2f", 0.0);
290 }
291 
292 
293 gchar*
294 gnc_ab_create_online_id (const gchar *bankcode, const gchar *accountnumber)
295 {
296  gchar *online_id;
297 
298  /* The accountnumber may have leading zeros, depending on where them
299  * accountnumber is came from, e.g. the accountnumber of accountinfo
300  * has no leading zeros while the (local)accountnumber of a transaction
301  * has leading zeros.
302  * So remove all leading '0', to get a consistent online_id.
303  */
304  while (accountnumber && *accountnumber == '0')
305  accountnumber++;
306 
307  online_id = g_strconcat (bankcode ? bankcode : "",
308  accountnumber ? accountnumber : "",
309  (gchar*)NULL);
310 
311  return online_id;
312 }
313 
318 static gpointer
319 join_ab_strings_cb (const gchar *str, gpointer user_data)
320 {
321  gchar **acc = user_data;
322  gchar *tmp;
323 
324  if (!str || !*str)
325  return NULL;
326 
327  tmp = g_utf8_normalize (str, -1, G_NORMALIZE_NFC);
328  g_strstrip (tmp);
330 
331  if (*acc)
332  {
333  if (!strstr (*acc, tmp))
334  {
335  gchar *join = g_strjoin (" ", *acc, tmp, (gchar*) NULL);
336  g_free (*acc);
337  *acc = join;
338  }
339  g_free (tmp);
340  }
341  else
342  {
343  *acc = tmp;
344  }
345  return NULL;
346 }
347 
348 gchar *
349 gnc_ab_get_remote_name (const AB_TRANSACTION *ab_trans)
350 {
351  const char* ab_remote_name;
352  gchar *gnc_other_name = NULL;
353 
354  g_return_val_if_fail (ab_trans, NULL);
355 
356  ab_remote_name = AB_Transaction_GetRemoteName (ab_trans);
357  if (ab_remote_name)
358  gnc_other_name = g_strdup(ab_remote_name);
359  if (!gnc_other_name || !*gnc_other_name)
360  {
361  g_free (gnc_other_name);
362  gnc_other_name = NULL;
363  }
364 
365  return gnc_other_name;
366 }
367 
368 gchar *
369 gnc_ab_get_purpose (const AB_TRANSACTION *ab_trans, gboolean is_ofx)
370 {
371  GWEN_STRINGLIST *ab_purpose;
372  const char *ab_transactionText = NULL;
373  gchar *gnc_description = NULL;
374 
375  g_return_val_if_fail (ab_trans, g_strdup (""));
376 
377  if (!is_ofx && gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING, GNC_PREF_USE_TRANSACTION_TXT))
378  {
379  /* According to AqBanking, some of the non-swift lines have a special
380  * meaning. Some banks place valuable text into the transaction text,
381  * hence we put this text in front of the purpose. */
382  ab_transactionText = AB_Transaction_GetTransactionText (ab_trans);
383  if (ab_transactionText && *ab_transactionText)
384  gnc_description = g_utf8_normalize (ab_transactionText, -1, G_NORMALIZE_NFC);
385  }
386 
387  ab_purpose = AB_Transaction_GetPurposeAsStringList (ab_trans);
388  if (ab_purpose)
389  GWEN_StringList_ForEach (ab_purpose, join_ab_strings_cb,
390  &gnc_description);
391 
392  GWEN_StringList_free (ab_purpose);
393 
394  return gnc_description;
395 }
396 
397 /* Ultimate Creditor and Ultimate Debtor are newish parameters added
398  * to SWIFT MT940 and CAMT.053 designating the originating
399  * payer or payee on the transaction. It's unlikely, but still
400  * possible, that a bank would use both this markup and the Non-swift
401  * TransactionText or RemoteName tags.
402  */
403 static gchar *
404 ab_ultimate_creditor_debtor_to_gnc (const AB_TRANSACTION *ab_trans,
405  gboolean is_ofx)
406 {
407  const gchar* ultimate;
408 
409  if (is_ofx)
410  return NULL;
411 
412  ultimate = AB_Transaction_GetUltimateCreditor (ab_trans);
413 
414  if (!ultimate || !*ultimate)
415  ultimate = AB_Transaction_GetUltimateDebtor (ab_trans);
416 
417  if (!ultimate || !*ultimate)
418  return NULL;
419 
420  return g_strdup (ultimate);
421 }
422 
423 gchar *
424 gnc_ab_description_to_gnc (const AB_TRANSACTION *ab_trans, gboolean is_ofx)
425 {
426  GList *acc = NULL;
427  gchar *retval;
428 
429  acc = g_list_prepend (acc, gnc_ab_get_remote_name (ab_trans));
430  acc = g_list_prepend (acc, gnc_ab_get_purpose (ab_trans, is_ofx));
431  acc = g_list_prepend (acc, ab_ultimate_creditor_debtor_to_gnc (ab_trans, is_ofx));
432  retval = gnc_g_list_stringjoin_nodups (acc, "; ");
433 
434  g_list_free_full (acc, g_free);
435  return retval ? retval : g_strdup (_("Unspecified"));
436 }
437 
438 gchar *
439 gnc_ab_memo_to_gnc (const AB_TRANSACTION *ab_trans)
440 {
441  const gchar *ab_remote_accountnumber =
442  AB_Transaction_GetRemoteAccountNumber (ab_trans);
443  const gchar *ab_remote_bankcode =
444  AB_Transaction_GetRemoteBankCode (ab_trans);
445 
446  gchar *ab_other_accountid;
447  gchar *ab_other_bankcode;
448 
449  gboolean have_accountid;
450  gboolean have_bankcode;
451 
452  gchar *retval;
453 
454  // For SEPA transactions, we need to ask for something different here
455  if (!ab_remote_accountnumber)
456  ab_remote_accountnumber = AB_Transaction_GetRemoteIban (ab_trans);
457  if (!ab_remote_bankcode)
458  ab_remote_bankcode = AB_Transaction_GetRemoteBic (ab_trans);
459 
460  ab_other_accountid = g_strdup (ab_remote_accountnumber ? ab_remote_accountnumber : "");
461  ab_other_bankcode = g_strdup (ab_remote_bankcode ? ab_remote_bankcode : "");
462 
463  /* Ensure string is in utf8 */
464  gnc_utf8_strip_invalid (ab_other_accountid);
465  gnc_utf8_strip_invalid (ab_other_bankcode);
466 
467  /* and -then- trim it */
468  g_strstrip (ab_other_accountid);
469  g_strstrip (ab_other_bankcode);
470 
471 
472  have_accountid = ab_other_accountid && *ab_other_accountid;
473  have_bankcode = ab_other_bankcode && *ab_other_bankcode;
474 
475  if ( have_accountid || have_bankcode )
476  {
477  retval = g_strdup_printf ("%s %s %s %s",
478  have_accountid ? _("Account") : "",
479  have_accountid ? ab_other_accountid : "",
480  have_bankcode ? _("Bank") : "",
481  have_bankcode ? ab_other_bankcode : ""
482  );
483  g_strstrip (retval);
484  }
485  else
486  {
487  retval = g_strdup ("");
488  }
489 
490  g_free (ab_other_accountid);
491  g_free (ab_other_bankcode);
492 
493  return retval;
494 }
495 
496 Transaction *
497 gnc_ab_trans_to_gnc (const AB_TRANSACTION *ab_trans, Account *gnc_acc)
498 {
499  QofBook *book;
500  Transaction *gnc_trans;
501  const gchar *fitid;
502  const GNC_GWEN_DATE *value_date, *post_date;
503  time64 post_time;
504  const char *custref;
505  gchar *description;
506  Split *split;
507  gchar *memo;
508 
509  g_return_val_if_fail (ab_trans && gnc_acc, NULL);
510 
511  /* Create new GnuCash transaction for the given AqBanking one */
512  book = gnc_account_get_book (gnc_acc);
513  gnc_trans = xaccMallocTransaction (book);
514  xaccTransBeginEdit (gnc_trans);
515 
516  /* Date / Time */
517  /* SWIFT import formats (in particular MT940) provide for two
518  * dates, the entry date and the value date (valuta is value in
519  * German). The value date is the effective date for financial
520  * calculation purposes and is mandatory, the entry date is the
521  * date that the financial institution posted the
522  * transaction. Unfortunately if the transaction field doesn't
523  * provide an entry date AQBanking substitutes the date from the
524  * last balance instead of using the value date or NULL, making
525  * the field unreliable.
526  */
527  value_date = AB_Transaction_GetValutaDate (ab_trans);
528  if (value_date)
529  post_time = gnc_gwen_date_to_time64 (value_date);
530  else if ((post_date = AB_Transaction_GetDate (ab_trans))) // always true
531  post_time = gnc_gwen_date_to_time64 (post_date);
532  else
533  {
534  g_warning ("transaction_cb: Import had no transaction date");
535  post_time = gnc_time (NULL);
536  }
537  xaccTransSetDatePostedSecsNormalized (gnc_trans, post_time);
538 
539  xaccTransSetDateEnteredSecs (gnc_trans, gnc_time (NULL));
540 
541  /* Currency. We take simply the default currency of the gnucash account */
542  xaccTransSetCurrency (gnc_trans, xaccAccountGetCommodity (gnc_acc));
543 
544  /* Trans-Num or Split-Action set with gnc_set_num_action below per book
545  * option */
546 
547  fitid = AB_Transaction_GetFiId (ab_trans);
548 
549  /* Description */
550  description = gnc_ab_description_to_gnc (ab_trans, (fitid && *fitid));
551  xaccTransSetDescription (gnc_trans, description);
552  g_free (description);
553 
554  /* Notes. */
555  /* xaccTransSetNotes(gnc_trans, g_notes); */
556  /* But Nobody ever uses the Notes field? */
557 
558  /* Add one split */
559  split = xaccMallocSplit (book);
560  xaccSplitSetParent (split, gnc_trans);
561  xaccSplitSetAccount (split, gnc_acc);
562 
563  /* Set the transaction number or split action field based on book option.
564  * We use the "customer reference", if there is one. */
565  custref = AB_Transaction_GetCustomerReference (ab_trans);
566  if (custref && *custref && g_ascii_strncasecmp (custref, "NONREF", 6) != 0)
567  gnc_set_num_action (gnc_trans, split, custref, NULL);
568 
569  /* Set OFX unique transaction ID */
570  if (fitid && *fitid)
571  xaccSplitSetOnlineID (split, fitid);
572 
573  /* FIXME: Extract function */
574  {
575  /* Amount into the split */
576  const AB_VALUE *ab_value = AB_Transaction_GetValue (ab_trans);
577  double d_value = ab_value ? AB_Value_GetValueAsDouble (ab_value) : 0.0;
578  AB_TRANSACTION_TYPE ab_type = AB_Transaction_GetType (ab_trans);
579  gnc_numeric gnc_amount;
580 
581  /*printf("Transaction with value %f has type %d\n", d_value, ab_type);*/
582  /* If the value is positive, but the transaction type says the
583  money is transferred away from our account (Transfer instead of
584  DebitNote), we switch the value to negative. */
585  if (d_value > 0.0 && ab_type == AB_Transaction_TypeTransfer)
586  d_value = -d_value;
587 
588  gnc_amount = double_to_gnc_numeric (
589  d_value,
590  xaccAccountGetCommoditySCU (gnc_acc),
592  if (!ab_value)
593  g_warning ("transaction_cb: Oops, value was NULL. Using 0");
594  xaccSplitSetBaseValue (split, gnc_amount, xaccAccountGetCommodity (gnc_acc));
595  }
596 
597  /* Memo in the Split. */
598  memo = gnc_ab_memo_to_gnc (ab_trans);
599  xaccSplitSetMemo (split, memo);
600  g_free (memo);
601 
602  return gnc_trans;
603 }
604 
613 static Account *
614 gnc_ab_accinfo_to_gnc_acc (GtkWidget *parent, AB_IMEXPORTER_ACCOUNTINFO *acc_info)
615 {
616  const gchar *bankcode, *accountnumber;
617  gchar *online_id;
618  Account *gnc_acc;
619 
620  g_return_val_if_fail (acc_info, NULL);
621 
622  bankcode = AB_ImExporterAccountInfo_GetBankCode (acc_info);
623  accountnumber = AB_ImExporterAccountInfo_GetAccountNumber (acc_info);
624  online_id = gnc_ab_create_online_id (bankcode, accountnumber);
625  gnc_acc = gnc_import_select_account (parent, online_id, 1,
626  AB_ImExporterAccountInfo_GetAccountName (acc_info),
627  NULL, ACCT_TYPE_NONE, NULL, NULL);
628  if (!gnc_acc)
629  {
630  g_warning ("gnc_ab_accinfo_to_gnc_acc: Could not determine source account"
631  " for online_id %s", online_id);
632  }
633  g_free (online_id);
634 
635  return gnc_acc;
636 }
637 
638 
647 static Account *
648 gnc_ab_txn_to_gnc_acc (GtkWidget *parent, const AB_TRANSACTION *transaction)
649 {
650  const gchar *bankcode, *accountnumber;
651  gchar *online_id;
652  Account *gnc_acc;
653 
654  g_return_val_if_fail(transaction, NULL);
655 
656  bankcode = AB_Transaction_GetLocalBankCode (transaction);
657  accountnumber = AB_Transaction_GetLocalAccountNumber (transaction);
658  if (!bankcode && !accountnumber)
659  {
660  return NULL;
661  }
662 
663  online_id = gnc_ab_create_online_id (bankcode, accountnumber);
664  gnc_acc = gnc_import_select_account (parent, online_id, 1,
665  AB_Transaction_GetLocalName (transaction),
666  NULL, ACCT_TYPE_NONE, NULL, NULL);
667  if (!gnc_acc)
668  {
669  g_warning ("gnc_ab_txn_to_gnc_acc: Could not determine source account"
670  " for online_id %s", online_id);
671  }
672  g_free (online_id);
673 
674  return gnc_acc;
675 }
676 
677 static const AB_TRANSACTION *
678 txn_transaction_cb (const AB_TRANSACTION *element, gpointer user_data)
679 {
680  GncABImExContextImport *data = user_data;
681  Transaction *gnc_trans;
682  GncABTransType trans_type;
683  Account* txnacc;
684 
685  g_return_val_if_fail (element && data, NULL);
686 
687  /* Create a GnuCash transaction from ab_trans */
688  txnacc = gnc_ab_txn_to_gnc_acc (GTK_WIDGET(data->parent), element);
689  gnc_trans = gnc_ab_trans_to_gnc (element, txnacc ? txnacc : data->gnc_acc);
690 
691  if (data->execute_txns && data->ab_acc)
692  {
693  AB_TRANSACTION *ab_trans = AB_Transaction_dup (element);
694  GNC_AB_JOB *job;
695 
696  /* NEW: The imported transaction has been imported into gnucash.
697  * Now also add it as a job to aqbanking */
698  AB_Transaction_SetLocalBankCode (
699  ab_trans, AB_AccountSpec_GetBankCode (data->ab_acc));
700  AB_Transaction_SetLocalAccountNumber (
701  ab_trans, AB_AccountSpec_GetAccountNumber (data->ab_acc));
702  AB_Transaction_SetLocalCountry (ab_trans, "DE");
703 
704 
705  switch (AB_Transaction_GetType (ab_trans))
706  {
707  case AB_Transaction_TypeDebitNote:
708  trans_type = SINGLE_DEBITNOTE;
709  break;
710  case AB_Transaction_TypeTransaction:
711  /* trans_type = SINGLE_INTERNAL_TRANSFER;
712  * break; */
713  case AB_Transaction_TypeTransfer:
714  default:
715  trans_type = SEPA_TRANSFER;
716  break;
717  } /* switch */
718 
719  job = gnc_ab_get_trans_job (data->ab_acc, ab_trans, trans_type);
720 
721  /* Check whether we really got a job */
722  if (!job || AB_AccountSpec_GetTransactionLimitsForCommand (data->ab_acc, AB_Transaction_GetCommand (job)) == NULL)
723  {
724  /* Oops, no job, probably not supported by bank */
725  if (gnc_verify_dialog (
726  GTK_WINDOW(data->parent), FALSE, "%s",
727  _("The backend found an error during the preparation "
728  "of the job. It is not possible to execute this job.\n"
729  "\n"
730  "Most probably the bank does not support your chosen "
731  "job or your Online Banking account does not have the permission "
732  "to execute this job. More error messages might be "
733  "visible on your console log.\n"
734  "\n"
735  "Do you want to enter the job again?")))
736  {
737  gnc_error_dialog (GTK_WINDOW(data->parent),
738  "Sorry, not implemented yet. Please check the console or trace file logs to see which job was rejected.");
739  }
740  }
741  else
742  {
743  gnc_gen_trans_list_add_trans_with_ref_id (data->generic_importer,
744  gnc_trans,
745  AB_Transaction_GetUniqueId (job));
746  /* AB_Job_List2_PushBack(data->job_list, job); -> delayed until trans is successfully imported */
747  g_datalist_set_data (&data->tmp_job_list, gnc_AB_JOB_to_readable_string (job), job);
748  }
749  AB_Transaction_free (ab_trans);
750  }
751  else
752  {
753  /* Instead of xaccTransCommitEdit(gnc_trans) */
754  gnc_gen_trans_list_add_trans (data->generic_importer, gnc_trans);
755  }
756 
757  return NULL;
758 }
759 
760 static void gnc_ab_trans_processed_cb (GNCImportTransInfo *trans_info,
761  gboolean imported,
762  gpointer user_data)
763 {
764  GncABImExContextImport *data = user_data;
765  gchar *jobname = gnc_AB_JOB_ID_to_string (gnc_import_TransInfo_get_ref_id (trans_info));
766  GNC_AB_JOB *job = g_datalist_get_data (&data->tmp_job_list, jobname);
767 
768  if (imported)
769  {
770  AB_Transaction_List2_PushBack (data->job_list, job);
771  }
772  else
773  {
774  AB_Transaction_free (job);
775  }
776 
777  g_datalist_remove_data (&data->tmp_job_list, jobname);
778 }
779 
780 gchar *
781 gnc_AB_JOB_to_readable_string (const GNC_AB_JOB *job)
782 {
783  if (job)
784  {
785  return gnc_AB_JOB_ID_to_string (AB_Transaction_GetUniqueId (job));
786  }
787  else
788  {
789  return gnc_AB_JOB_ID_to_string (0);
790  }
791 }
792 gchar *
793 gnc_AB_JOB_ID_to_string (gulong job_id)
794 {
795  return g_strdup_printf ("job_%lu", job_id);
796 }
797 
798 static const AB_TRANSACTION *
799 get_first_importable_transaction (AB_IMEXPORTER_ACCOUNTINFO *element,
800  gboolean import_noted_txns)
801 {
802  const AB_TRANSACTION *trans;
803 
804  trans = AB_ImExporterAccountInfo_GetFirstTransaction (
805  element, AB_Transaction_TypeStatement, 0);
806  if (trans)
807  return trans;
808 
809  if (!import_noted_txns)
810  return NULL;
811 
812  return AB_ImExporterAccountInfo_GetFirstTransaction (
813  element, AB_Transaction_TypeNotedStatement, 0);
814 }
815 
816 static void
817 import_account_transactions (AB_TRANSACTION_LIST *ab_trans_list,
818  GncABImExContextImport *data,
819  gboolean import_noted_txns)
820 {
821  AB_Transaction_List_ForEachByType (ab_trans_list, txn_transaction_cb, data,
822  AB_Transaction_TypeStatement, 0);
823 
824  if (import_noted_txns)
825  AB_Transaction_List_ForEachByType (ab_trans_list, txn_transaction_cb,
826  data,
827  AB_Transaction_TypeNotedStatement, 0);
828 }
829 
830 static AB_IMEXPORTER_ACCOUNTINFO *
831 txn_accountinfo_cb (AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data)
832 {
833  GncABImExContextImport *data = user_data;
834  Account *gnc_acc;
835  gboolean import_noted_txns;
836 
837  g_return_val_if_fail (element && data, NULL);
838 
839  if (data->awaiting & IGNORE_TRANSACTIONS)
840  /* Ignore them */
841  return NULL;
842 
843  import_noted_txns = gnc_prefs_get_bool (GNC_PREFS_GROUP_AQBANKING,
844  GNC_PREF_IMPORT_NOTED_TXNS);
845 
846  if (!get_first_importable_transaction (element, import_noted_txns))
847  /* No transaction found */
848  return NULL;
849  else
850  data->awaiting |= FOUND_TRANSACTIONS;
851 
852  if (!(data->awaiting & AWAIT_TRANSACTIONS))
853  {
854  if (gnc_verify_dialog (GTK_WINDOW(data->parent), TRUE, "%s",
855  _("The bank has sent transaction information "
856  "in its response."
857  "\n"
858  "Do you want to import it?")))
859  {
860  data->awaiting |= AWAIT_TRANSACTIONS;
861  }
862  else
863  {
864  data->awaiting |= IGNORE_TRANSACTIONS;
865  return NULL;
866  }
867  }
868 
869  /* Lookup the corresponding gnucash account */
870  gnc_acc = gnc_ab_accinfo_to_gnc_acc (GTK_WIDGET(data->parent), element);
871  if (!gnc_acc) return NULL;
872  data->gnc_acc = gnc_acc;
873 
874  if (data->execute_txns)
875  {
876  /* Retrieve the aqbanking account that belongs to this gnucash
877  * account */
878  data->ab_acc = gnc_ab_get_ab_account (data->api, gnc_acc);
879  if (!data->ab_acc)
880  {
881  gnc_error_dialog (GTK_WINDOW(data->parent), "%s",
882  _("No Online Banking account found for this "
883  "gnucash account. These transactions will "
884  "not be executed by Online Banking."));
885  }
886  }
887  else
888  {
889  data->ab_acc = NULL;
890  }
891 
892  if (!data->generic_importer)
893  {
894  data->generic_importer = gnc_gen_trans_list_new (data->parent, NULL,
895  TRUE, 14, TRUE);
896  if (data->execute_txns)
897  {
898  gnc_gen_trans_list_add_tp_cb (data->generic_importer,
899  gnc_ab_trans_processed_cb, data);
900  }
901  }
902 
903  /* Iterate through all transactions */
904  {
905  AB_TRANSACTION_LIST *ab_trans_list = AB_ImExporterAccountInfo_GetTransactionList (element);
906  if (ab_trans_list)
907  import_account_transactions (ab_trans_list, data, import_noted_txns);
908  }
909  return NULL;
910 }
911 
912 static AB_IMEXPORTER_ACCOUNTINFO *
913 bal_accountinfo_cb (AB_IMEXPORTER_ACCOUNTINFO *element, gpointer user_data)
914 {
915  GncABImExContextImport *data = user_data;
916  Account *gnc_acc;
917  const AB_BALANCE *booked_bal, *noted_bal;
918  const AB_VALUE *booked_val = NULL, *noted_val = NULL;
919  gdouble booked_value, noted_value;
920  gnc_numeric value;
921  time64 booked_tt = 0;
922  GtkWidget *dialog;
923  gboolean show_recn_window = FALSE;
924 
925  g_return_val_if_fail (element && data, NULL);
926 
927  if (data->awaiting & IGNORE_BALANCES)
928  /* Ignore them */
929  return NULL;
930 
931  if (!AB_ImExporterAccountInfo_GetFirstBalance (element))
932  /* No balance found */
933  return NULL;
934  else
935  data->awaiting |= FOUND_BALANCES;
936 
937  /* Lookup the most recent BALANCE available */
938  booked_bal = AB_Balance_List_GetLatestByType (AB_ImExporterAccountInfo_GetBalanceList (element),
939  AB_Balance_TypeBooked);
940 
941  if (!(data->awaiting & AWAIT_BALANCES))
942  {
943  GtkWindow *parent = data->generic_importer ?
944  GTK_WINDOW(gnc_gen_trans_list_widget (data->generic_importer)) :
945  GTK_WINDOW(data->parent);
946  const char* balance_msg =
947  _("The bank has sent balance information in its response.\n"
948  "Do you want to import it?");
949  /* Ignore zero balances if we don't await a balance */
950  if (!booked_bal || AB_Value_IsZero (AB_Balance_GetValue (booked_bal)))
951  return NULL;
952 
953  /* Ask the user whether to import unawaited non-zero balance */
954  if (gnc_verify_dialog (parent, TRUE, "%s", balance_msg))
955  {
956  data->awaiting |= AWAIT_BALANCES;
957  }
958  else
959  {
960  data->awaiting |= IGNORE_BALANCES;
961  return NULL;
962  }
963  }
964 
965  /* Lookup the corresponding gnucash account */
966  gnc_acc = gnc_ab_accinfo_to_gnc_acc (GTK_WIDGET(data->parent), element);
967  if (!gnc_acc) return NULL;
968  data->gnc_acc = gnc_acc;
969 
970  /* Lookup booked balance and time */
971  if (booked_bal)
972  {
973  const GWEN_DATE *ti = AB_Balance_GetDate (booked_bal);
974  if (ti)
975  {
976  booked_tt = gnc_gwen_date_to_time64 (ti);
977  }
978  else
979  {
980  /* No time found? Use today because the HBCI query asked for today's
981  * balance. */
982  booked_tt = gnc_time64_get_day_neutral (gnc_time (NULL));
983  }
984  booked_val = AB_Balance_GetValue (booked_bal);
985  if (booked_val)
986  {
987  booked_value = AB_Value_GetValueAsDouble (booked_val);
988  }
989  else
990  {
991  g_warning ("bal_accountinfo_cb: booked_val == NULL. Assuming 0");
992  booked_value = 0.0;
993  }
994  }
995  else
996  {
997  g_warning ("bal_accountinfo_cb: booked_bal == NULL. Assuming 0");
998  booked_tt = 0;
999  booked_value = 0.0;
1000  }
1001 
1002  /* Lookup noted balance */
1003  noted_bal = AB_Balance_List_GetLatestByType (AB_ImExporterAccountInfo_GetBalanceList (element),
1004  AB_Balance_TypeNoted);
1005  if (noted_bal)
1006  {
1007  noted_val = AB_Balance_GetValue (noted_bal);
1008  if (noted_val)
1009  noted_value = AB_Value_GetValueAsDouble (noted_val);
1010  else
1011  {
1012  g_warning ("bal_accountinfo_cb: noted_val == NULL. Assuming 0");
1013  noted_value = 0.0;
1014  }
1015  }
1016  else
1017  {
1018  g_warning ("bal_accountinfo_cb: noted_bal == NULL. Assuming 0");
1019  noted_value = 0.0;
1020  }
1021 
1022  value = double_to_gnc_numeric (booked_value,
1023  xaccAccountGetCommoditySCU (gnc_acc),
1025  if (noted_value == 0.0 && booked_value == 0.0)
1026  {
1027  dialog = gtk_message_dialog_new (
1028  GTK_WINDOW(data->parent),
1029  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1030  GTK_MESSAGE_INFO,
1031  GTK_BUTTONS_OK,
1032  "%s",
1033  /* Translators: Strings from this file are needed only in
1034  countries that have one of aqbanking's Online Banking
1035  techniques available. This is 'OFX DirectConnect'
1036  (U.S. and others), 'HBCI' (in Germany), or 'YellowNet'
1037  (Switzerland). If none of these techniques are available
1038  in your country, you may safely ignore strings from the
1039  import-export/hbci subdirectory. */
1040  _("The downloaded Online Banking Balance was zero.\n\n"
1041  "Either this is the correct balance, or your bank does not "
1042  "support Balance download in this Online Banking version. "
1043  "In the latter case you should choose a different "
1044  "Online Banking version number in the Online Banking "
1045  "(AqBanking or HBCI) Setup. After that, try again to "
1046  "download the Online Banking Balance."));
1047  gtk_dialog_run (GTK_DIALOG(dialog));
1048  gtk_widget_destroy (dialog);
1049 
1050  }
1051  else
1052  {
1053  gnc_numeric reconc_balance = xaccAccountGetReconciledBalance (gnc_acc);
1054 
1055  gchar *booked_str = gnc_AB_VALUE_to_readable_string (booked_val);
1056  gchar *message1 = g_strdup_printf (
1057  _("Result of Online Banking job:\n"
1058  "Account booked balance is %s"),
1059  booked_str);
1060  gchar *message2 =
1061  (noted_value == 0.0) ?
1062  g_strdup ("") :
1063  g_strdup_printf (_("For your information: This account also "
1064  "has a noted balance of %s\n"),
1065  gnc_AB_VALUE_to_readable_string (noted_val));
1066 
1067  if (gnc_numeric_equal (value, reconc_balance))
1068  {
1069  const gchar *message3 =
1070  _("The booked balance is identical to the current "
1071  "reconciled balance of the account.");
1072  dialog = gtk_message_dialog_new (
1073  GTK_WINDOW(data->parent),
1074  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1075  GTK_MESSAGE_INFO,
1076  GTK_BUTTONS_OK,
1077  "%s\n%s\n%s",
1078  message1, message2, message3);
1079  gtk_dialog_run (GTK_DIALOG(dialog));
1080  gtk_widget_destroy (GTK_WIDGET(dialog));
1081 
1082  }
1083  else
1084  {
1085  const char *message3 = _("Reconcile account now?");
1086 
1087  show_recn_window = gnc_verify_dialog (GTK_WINDOW(data->parent), TRUE, "%s\n%s\n%s",
1088  message1, message2, message3);
1089  }
1090  g_free (booked_str);
1091  g_free (message1);
1092  g_free (message2);
1093  }
1094 
1095  /* Show reconciliation window */
1096  if (show_recn_window)
1097  recnWindowWithBalance (GTK_WIDGET(data->parent), gnc_acc, value, booked_tt);
1098 
1099  return NULL;
1100 }
1101 
1102 GncABImExContextImport *
1103 gnc_ab_import_context (AB_IMEXPORTER_CONTEXT *context,
1104  guint awaiting, gboolean execute_txns,
1105  AB_BANKING *api, GtkWidget *parent)
1106 {
1107  GncABImExContextImport *data = g_new (GncABImExContextImport, 1);
1108  AB_IMEXPORTER_ACCOUNTINFO_LIST *ab_ail;
1109  g_return_val_if_fail (context, NULL);
1110  /* Do not await and ignore at the same time */
1111  g_return_val_if_fail (!(awaiting & AWAIT_BALANCES)
1112  || !(awaiting & IGNORE_BALANCES),
1113  NULL);
1114  g_return_val_if_fail (!(awaiting & AWAIT_TRANSACTIONS)
1115  || !(awaiting & IGNORE_TRANSACTIONS),
1116  NULL);
1117  /* execute_txns must be FALSE if txns are not awaited */
1118  g_return_val_if_fail (awaiting & AWAIT_TRANSACTIONS || !execute_txns, NULL);
1119  /* An api is needed for the jobs */
1120  g_return_val_if_fail (!execute_txns || api, NULL);
1121 
1122  data->awaiting = awaiting;
1123  data->txn_found = FALSE;
1124  data->execute_txns = execute_txns;
1125  data->api = api;
1126  data->parent = parent;
1127  data->job_list = AB_Transaction_List2_new ();
1128  data->tmp_job_list = NULL;
1129  data->generic_importer = NULL;
1130 
1131  g_datalist_init (&data->tmp_job_list);
1132 
1133  /* Import transactions */
1134  ab_ail = AB_ImExporterContext_GetAccountInfoList (context);
1135  if (ab_ail && AB_ImExporterAccountInfo_List_GetCount (ab_ail))
1136  {
1137  if (!(awaiting & IGNORE_TRANSACTIONS))
1138  AB_ImExporterAccountInfo_List_ForEach (ab_ail,
1139  txn_accountinfo_cb,
1140  data);
1141 
1142  /* populate and display the matching window */
1143  if (data->generic_importer)
1144  gnc_gen_trans_list_show_all (data->generic_importer);
1145 
1146  /* Check balances */
1147  if (!(awaiting & IGNORE_BALANCES))
1148  AB_ImExporterAccountInfo_List_ForEach (ab_ail,
1149  bal_accountinfo_cb,
1150  data);
1151  }
1152 
1153  /* Check bank-messages */
1154  {
1155  AB_MESSAGE * bankmsg = AB_ImExporterContext_GetFirstMessage (context);
1156  while (bankmsg)
1157  {
1158  const char* subject = AB_Message_GetSubject (bankmsg);
1159  const char* text = AB_Message_GetText (bankmsg);
1160  gnc_info_dialog (GTK_WINDOW(data->parent), "%s\n%s %s\n%s",
1161  _("The bank has sent a message in its response."),
1162  _("Subject:"),
1163  subject,
1164  text);
1165 
1166  bankmsg = AB_Message_List_Next (bankmsg);
1167  }
1168  }
1169 
1170  return data;
1171 }
1172 
1173 guint
1174 gnc_ab_ieci_get_found (GncABImExContextImport *ieci)
1175 {
1176  g_return_val_if_fail (ieci, 0);
1177 
1178  return ieci->awaiting;
1179 }
1180 
1181 GNC_AB_JOB_LIST2 *
1182 gnc_ab_ieci_get_job_list (GncABImExContextImport *ieci)
1183 {
1184  g_return_val_if_fail (ieci, NULL);
1185 
1186  return ieci->job_list;
1187 }
1188 
1189 gboolean
1190 gnc_ab_ieci_run_matcher (GncABImExContextImport *ieci)
1191 {
1192  g_return_val_if_fail (ieci, FALSE);
1193 
1194  return gnc_gen_trans_list_run (ieci->generic_importer);
1195 }
1196 
1197 GWEN_DB_NODE *
1199 {
1200  int rv;
1201  GWEN_DB_NODE *perm_certs = NULL;
1202  AB_BANKING *banking = gnc_AB_BANKING_new ();
1203 
1204  g_return_val_if_fail (banking, NULL);
1205  rv = AB_Banking_LoadSharedConfig (banking, "certs", &perm_certs);
1206  gnc_AB_BANKING_fini (banking);
1207  g_return_val_if_fail (rv >= 0, NULL);
1208  return perm_certs;
1209 }
1210 
1211 #if (AQBANKING_VERSION_INT >= 60400)
1212 GList*
1213 gnc_ab_trans_templ_list_new_from_ref_accounts (GNC_AB_ACCOUNT_SPEC *ab_acc)
1214 {
1215  GList *retval = NULL;
1216  AB_REFERENCE_ACCOUNT *ra;
1217  AB_REFERENCE_ACCOUNT_LIST *ral;
1218  GWEN_BUFFER *accNameForTemplate = GWEN_Buffer_new (0,120,0,0);
1219  gnc_numeric zero = gnc_numeric_zero ();
1220 
1221  /* get the target account list */
1222  ral = AB_AccountSpec_GetRefAccountList (ab_acc);
1223  ra = AB_ReferenceAccount_List_First (ral);
1224 
1225  /* fill the template list with the target accounts */
1226  while (ra)
1227  {
1228  GncABTransTempl *new_templ = gnc_ab_trans_templ_new ();
1229  const char *iban = AB_ReferenceAccount_GetIban (ra);
1230  const char *accName = AB_ReferenceAccount_GetAccountName (ra);
1231  GWEN_Buffer_Reset (accNameForTemplate);
1232  if (accName)
1233  {
1234  GWEN_Buffer_AppendString (accNameForTemplate, accName);
1235  GWEN_Buffer_AppendString (accNameForTemplate, ": ");
1236  }
1237  GWEN_Buffer_AppendString (accNameForTemplate, iban);
1238  gnc_ab_trans_templ_set_name (new_templ, GWEN_Buffer_GetStart (accNameForTemplate));
1239  gnc_ab_trans_templ_set_recp_name (new_templ, AB_ReferenceAccount_GetOwnerName (ra));
1240  gnc_ab_trans_templ_set_recp_account (new_templ, AB_ReferenceAccount_GetIban (ra));
1241  gnc_ab_trans_templ_set_recp_bankcode (new_templ, AB_ReferenceAccount_GetBic (ra));
1242  gnc_ab_trans_templ_set_amount (new_templ, zero);
1243  retval = g_list_prepend (retval, new_templ);
1244  ra = AB_ReferenceAccount_List_Next (ra);
1245  }
1246  retval = g_list_reverse (retval);
1247 
1248  GWEN_Buffer_free (accNameForTemplate);
1249 
1250  return retval;
1251 }
1252 #endif
1253 static int
1254 ab_node_pair_compare (AB_Node_Pair* left, AB_Node_Pair* right)
1255 {
1256  return left ? (right ? g_strcmp0 (left->name, right->name) : -1) :
1257  (right ? 1 : 0);
1258 }
1259 
1260 GList*
1261 gnc_ab_imexporter_list (AB_BANKING* api)
1262 {
1263  GList* desc_list = NULL;
1264  GWEN_PLUGIN_DESCRIPTION_LIST2 *il =
1265  AB_Banking_GetImExporterDescrs (api);
1266  GWEN_PLUGIN_DESCRIPTION_LIST2_ITERATOR *ilit;
1267  g_return_val_if_fail (il, NULL);
1268  ilit = GWEN_PluginDescription_List2_First(il);
1269 
1270  for (GWEN_PLUGIN_DESCRIPTION *pd =
1271  GWEN_PluginDescription_List2Iterator_Data(ilit);
1272  pd;
1273  pd = GWEN_PluginDescription_List2Iterator_Next(ilit))
1274  {
1275  AB_Node_Pair *node = NULL;
1276 
1277  node = g_slice_new (AB_Node_Pair);
1278  node->name = g_strdup(GWEN_PluginDescription_GetName(pd));
1279  node->descr = g_strdup(GWEN_PluginDescription_GetShortDescr(pd));
1280  desc_list = g_list_prepend (desc_list, node);
1281  }
1282  GWEN_PluginDescription_List2_free(il);
1283  return g_list_sort (desc_list, (GCompareFunc)ab_node_pair_compare);
1284 }
1285 
1286 GList*
1287 gnc_ab_imexporter_profile_list (AB_BANKING* api, const char* importer_name)
1288 {
1289  GList* prof_list = NULL;
1290  GWEN_DB_NODE* db = AB_Banking_GetImExporterProfiles(api, importer_name);
1291  g_return_val_if_fail (db, NULL);
1292 
1293  for (GWEN_DB_NODE *profile = GWEN_DB_GetFirstGroup(db); profile;
1294  profile = GWEN_DB_GetNextGroup(profile))
1295  {
1296  AB_Node_Pair *node = g_slice_new(AB_Node_Pair);
1297  if (!profile) continue;
1298  node->name = g_strdup(GWEN_DB_GetCharValue(profile, "name", 0, NULL));
1299  node->descr = g_strdup(GWEN_DB_GetCharValue(profile, "shortDescr", 0, NULL));
1300  prof_list = g_list_prepend (prof_list, node);
1301  }
1302  return g_list_sort (prof_list, (GCompareFunc)ab_node_pair_compare);
1303 }
Dialog for AqBanking transaction data.
guint32 gnc_ab_get_account_uid(const Account *a)
Return the unique id for the AB_BANKING account in the Account a.
Definition: gnc-ab-kvp.c:79
gchar * gnc_ab_create_online_id(const gchar *bankcode, const gchar *accountnumber)
Creates an online ID from bank code and account number.
Definition: gnc-ab-utils.c:294
gchar * gnc_ab_get_remote_name(const AB_TRANSACTION *ab_trans)
Retrieve the merged "remote name" fields from a transaction.
Definition: gnc-ab-utils.c:349
GList * gnc_ab_imexporter_list(AB_BANKING *api)
Retrieve the available AQBanking importers.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Depending on the base_currency, set either the value or the amount of this split or both: If the base...
Definition: Split.cpp:1355
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
time64 gnc_dmy2time64_neutral(gint day, gint month, gint year)
Converts a day, month, and year to a time64 representing 11:00:00 UTC 11:00:00 UTC falls on the same ...
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info)
Shows widgets.
void gnc_GWEN_Gui_shutdown(void)
Free all memory related to both the full-blown and minimalistic GUI objects.
Definition: gnc-gwen-gui.c:290
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2745
gchar * gnc_ab_get_purpose(const AB_TRANSACTION *ab_trans, gboolean is_ofx)
Retrieve the merged purpose fields from a transaction.
Definition: gnc-ab-utils.c:369
STRUCTS.
void gnc_ab_trans_templ_set_amount(GncABTransTempl *t, gnc_numeric amount)
Replace the amount stored in a template.
gchar * gnc_AB_VALUE_to_readable_string(const AB_VALUE *value)
Print the value of value with two decimal places and value&#39;s currency appended, or 0...
Definition: gnc-ab-utils.c:282
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gchar * gnc_AB_JOB_ID_to_string(gulong job_id)
Return the job_id as string.
Definition: gnc-ab-utils.c:793
Transaction matcher main window.
void gnc_utf8_strip_invalid_and_controls(gchar *str)
Strip any non-utf8 characters and any control characters (everything < 0x20, , , ...
gchar * gnc_ab_memo_to_gnc(const AB_TRANSACTION *ab_trans)
Create the appropriate memo field for a GnuCash Split by the information given in the AB_TRANSACTION ...
Definition: gnc-ab-utils.c:439
Account * gnc_import_select_account(GtkWidget *parent, const gchar *account_online_id_value, gboolean prompt_on_no_match, const gchar *account_human_description, const gnc_commodity *new_account_default_commodity, GNCAccountType new_account_default_type, Account *default_selection, gboolean *ok_pressed)
Must be called with a string containing a unique identifier for the account.
gboolean gnc_ab_ieci_run_matcher(GncABImExContextImport *ieci)
Run the generic transaction matcher dialog.
void gnc_gen_trans_list_add_tp_cb(GNCImportMainMatcher *info, GNCTransactionProcessedCB trans_processed_cb, gpointer user_data)
Add transaction processed callback to the transaction importer.
void gnc_ab_trans_templ_set_recp_account(GncABTransTempl *t, const gchar *recp_account)
Replace the Account Number of the recipient stored in a template.
Generic and very flexible account matcher/picker.
GNC_AB_JOB_LIST2 * gnc_ab_ieci_get_job_list(GncABImExContextImport *ieci)
Extract the job list from data.
void gnc_ab_trans_templ_set_name(GncABTransTempl *t, const gchar *name)
Set the name of a template.
guint32 gnc_import_TransInfo_get_ref_id(const GNCImportTransInfo *info)
Returns the reference id for this TransInfo.
const gchar * gnc_ab_get_account_bankcode(const Account *a)
Return the bankcode string in the Account a.
Definition: gnc-ab-kvp.c:59
void gnc_gen_trans_list_add_trans(GNCImportMainMatcher *gui, Transaction *trans)
Add a newly imported Transaction to the Transaction Importer.
GWEN_DB_NODE * gnc_ab_get_permanent_certs(void)
get the GWEN_DB_NODE from AqBanking configuration files
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
void gnc_ab_trans_templ_set_recp_name(GncABTransTempl *t, const gchar *recp_name)
Replace the Account Number of the recipient stored in a template.
void gnc_ab_trans_templ_set_recp_bankcode(GncABTransTempl *t, const gchar *recp_bankcode)
Replace the Bank Code of the recipient stored in a template.
GncABTransTempl * gnc_ab_trans_templ_new()
Create a template with unset contents.
gchar * gnc_ab_description_to_gnc(const AB_TRANSACTION *ab_trans, gboolean is_ofx)
Create the appropriate description field for a GnuCash Transaction by the information given in the AB...
Definition: gnc-ab-utils.c:424
GtkWidget * gnc_gen_trans_list_widget(GNCImportMainMatcher *info)
Returns the widget of this dialog.
Transaction * gnc_ab_trans_to_gnc(const AB_TRANSACTION *ab_trans, Account *gnc_acc)
Create an unbalanced and dirty GnuCash transaction with a split to gnc_acc from the information avail...
Definition: gnc-ab-utils.c:497
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
gchar * gnc_AB_JOB_to_readable_string(const GNC_AB_JOB *job)
Return the job as string.
Definition: gnc-ab-utils.c:781
GNCImportMainMatcher * gnc_gen_trans_list_new(GtkWidget *parent, const gchar *heading, bool all_from_same_account, gint match_date_hardlimit, bool show_all)
Create a new generic transaction dialog window and return it.
AB_BANKING * gnc_AB_BANKING_new(void)
If there is a cached AB_BANKING object, return it initialized.
Definition: gnc-ab-utils.c:162
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
gnc_numeric xaccAccountGetReconciledBalance(const Account *acc)
Get the current balance of the account, only including reconciled transactions.
Definition: Account.cpp:3481
GList * gnc_ab_imexporter_profile_list(AB_BANKING *api, const char *importer_name)
Retrieve the available format templates for an AQBanking importer.
gint gnc_AB_BANKING_fini(AB_BANKING *api)
Finish the AB_BANKING api.
Definition: gnc-ab-utils.c:225
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
GLib helper routines.
gchar * gnc_g_list_stringjoin_nodups(GList *list_of_strings, const gchar *sep)
Like stringjoin but ensures that the string to be added isn&#39;t already part of the return string...
Generic api to store and retrieve preferences.
QofBook reference.
Definition: qofbook-p.hpp:46
void gnc_gen_trans_list_add_trans_with_ref_id(GNCImportMainMatcher *gui, Transaction *trans, guint32 ref_id)
Add a newly imported Transaction to the Transaction Importer and provide an external reference id for...
GNC_AB_JOB * gnc_ab_get_trans_job(GNC_AB_ACCOUNT_SPEC *ab_acc, const AB_TRANSACTION *ab_trans, GncABTransType trans_type)
Return the AqBanking job associated with the transaction.
void gnc_AB_BANKING_delete(AB_BANKING *api)
Delete the AB_BANKING api.
Definition: gnc-ab-utils.c:206
void gnc_GWEN_Fini(void)
Finalize the gwenhywfar library.
Definition: gnc-ab-utils.c:147
GncABImExContextImport * gnc_ab_import_context(AB_IMEXPORTER_CONTEXT *context, guint awaiting, gboolean execute_txns, AB_BANKING *api, GtkWidget *parent)
Import balances and transactions found in a AB_IMEXPORTER_CONTEXT into GnuCash.
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3408
void xaccSplitSetOnlineID(Split *split, const char *id)
The online_id is the OFX/HBCI "FITID" recorded on a split when it is imported.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
bool gnc_gen_trans_list_run(GNCImportMainMatcher *info)
Run this dialog and return only after the user pressed Ok, Cancel, or closed the window.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
GNC_AB_ACCOUNT_SPEC * gnc_ab_get_ab_account(const AB_BANKING *api, Account *gnc_acc)
Get the corresponding AqBanking account to the GnuCash account gnc_acc.
Definition: gnc-ab-utils.c:248
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:262
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
const gchar * gnc_ab_get_account_accountid(const Account *a)
Return accountid string in the Account a.
Definition: gnc-ab-kvp.c:39
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
void gnc_GWEN_Init(void)
Initialize the gwenhywfar library by calling GWEN_Init() and setting up gwenhywfar logging...
Definition: gnc-ab-utils.c:112
AqBanking KVP handling.
GUI callbacks for AqBanking.
guint gnc_ab_ieci_get_found(GncABImExContextImport *ieci)
Extract awaiting from data.
Not a type.
Definition: Account.h:105
API for Transactions and Splits (journal entries)
void gnc_GWEN_Gui_log_init(void)
Hook our logging into the gwenhywfar logging framework by creating a minimalistic GWEN_GUI with only ...
Definition: gnc-gwen-gui.c:229
AqBanking utility functions.
time64 gnc_time64_get_day_neutral(time64 time_val)
The gnc_time64_get_day_neutral() routine will take the given time in seconds and adjust it to 10:59:0...
Definition: gnc-date.cpp:1377